1# -*- coding: utf-8 -*-
2
3"""
4Generate .stp file that printfs log messages (DTrace with SystemTAP only).
5"""
6
7__author__     = "Daniel P. Berrange <berrange@redhat.com>"
8__copyright__  = "Copyright (C) 2014-2019, Red Hat, Inc."
9__license__    = "GPL version 2 or (at your option) any later version"
10
11__maintainer__ = "Daniel Berrange"
12__email__      = "berrange@redhat.com"
13
14import re
15
16from tracetool import out
17from tracetool.backend.dtrace import binary, probeprefix
18from tracetool.backend.simple import is_string
19from tracetool.format.stap import stap_escape
20
21def global_var_name(name):
22    return probeprefix().replace(".", "_") + "_" + name
23
24STATE_SKIP = 0
25STATE_LITERAL = 1
26STATE_MACRO = 2
27
28def c_macro_to_format(macro):
29    if macro.startswith("PRI"):
30        return macro[3]
31
32    raise Exception("Unhandled macro '%s'" % macro)
33
34def c_fmt_to_stap(fmt):
35    state = 0
36    bits = []
37    literal = ""
38    macro = ""
39    escape = 0;
40    for i in range(len(fmt)):
41        if fmt[i] == '\\':
42            if escape:
43                escape = 0
44            else:
45                escape = 1
46            if state != STATE_LITERAL:
47                raise Exception("Unexpected escape outside string literal")
48            literal = literal + fmt[i]
49        elif fmt[i] == '"' and not escape:
50            if state == STATE_LITERAL:
51                state = STATE_SKIP
52                bits.append(literal)
53                literal = ""
54            else:
55                if state == STATE_MACRO:
56                    bits.append(c_macro_to_format(macro))
57                    macro = ""
58                state = STATE_LITERAL
59        elif fmt[i] == ' ' or fmt[i] == '\t':
60            if state == STATE_MACRO:
61                bits.append(c_macro_to_format(macro))
62                macro = ""
63                state = STATE_SKIP
64            elif state == STATE_LITERAL:
65                literal = literal + fmt[i]
66        else:
67            escape = 0
68            if state == STATE_SKIP:
69                state = STATE_MACRO
70
71            if state == STATE_LITERAL:
72                literal = literal + fmt[i]
73            else:
74                macro = macro + fmt[i]
75
76    if state == STATE_MACRO:
77        bits.append(c_macro_to_format(macro))
78    elif state == STATE_LITERAL:
79        bits.append(literal)
80
81    # All variables in systemtap are 64-bit in size
82    # The "%l" integer size qualifier is thus redundant
83    # and "%ll" is not valid at all. Similarly the size_t
84    # based "%z" size qualifier is not valid. We just
85    # strip all size qualifiers for sanity.
86    fmt = re.sub(r"%(\d*)(l+|z)(x|u|d)", r"%\1\3", "".join(bits))
87    return fmt
88
89def generate(events, backend, group):
90    out('/* This file is autogenerated by tracetool, do not edit. */',
91        '')
92
93    for event_id, e in enumerate(events):
94        if 'disable' in e.properties:
95            continue
96
97        out('probe %(probeprefix)s.log.%(name)s = %(probeprefix)s.%(name)s ?',
98            '{',
99            probeprefix=probeprefix(),
100            name=e.name)
101
102        # Get references to userspace strings
103        for type_, name in e.args:
104            name = stap_escape(name)
105            if is_string(type_):
106                out('    try {',
107                    '        arg%(name)s_str = %(name)s ? ' +
108                    'user_string_n(%(name)s, 512) : "<null>"',
109                    '    } catch {}',
110                    name=name)
111
112        # Determine systemtap's view of variable names
113        fields = ["pid()", "gettimeofday_ns()"]
114        for type_, name in e.args:
115            name = stap_escape(name)
116            if is_string(type_):
117                fields.append("arg" + name + "_str")
118            else:
119                fields.append(name)
120
121        # Emit the entire record in a single SystemTap printf()
122        arg_str = ', '.join(arg for arg in fields)
123        fmt_str = "%d@%d " + e.name + " " + c_fmt_to_stap(e.fmt) + "\\n"
124        out('    printf("%(fmt_str)s", %(arg_str)s)',
125            fmt_str=fmt_str, arg_str=arg_str)
126
127        out('}')
128
129    out()
130