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                state = STATE_LITERAL
58        elif fmt[i] == ' ' or fmt[i] == '\t':
59            if state == STATE_MACRO:
60                bits.append(c_macro_to_format(macro))
61                macro = ""
62                state = STATE_SKIP
63            elif state == STATE_LITERAL:
64                literal = literal + fmt[i]
65        else:
66            escape = 0
67            if state == STATE_SKIP:
68                state = STATE_MACRO
69
70            if state == STATE_LITERAL:
71                literal = literal + fmt[i]
72            else:
73                macro = macro + fmt[i]
74
75    if state == STATE_MACRO:
76        bits.append(c_macro_to_format(macro))
77    elif state == STATE_LITERAL:
78        bits.append(literal)
79
80    fmt = re.sub("%(\d*)z(x|u|d)", "%\\1\\2", "".join(bits))
81    return fmt
82
83def generate(events, backend, group):
84    out('/* This file is autogenerated by tracetool, do not edit. */',
85        '')
86
87    for event_id, e in enumerate(events):
88        if 'disable' in e.properties:
89            continue
90
91        out('probe %(probeprefix)s.log.%(name)s = %(probeprefix)s.%(name)s ?',
92            '{',
93            probeprefix=probeprefix(),
94            name=e.name)
95
96        # Get references to userspace strings
97        for type_, name in e.args:
98            name = stap_escape(name)
99            if is_string(type_):
100                out('    try {',
101                    '        arg%(name)s_str = %(name)s ? ' +
102                    'user_string_n(%(name)s, 512) : "<null>"',
103                    '    } catch {}',
104                    name=name)
105
106        # Determine systemtap's view of variable names
107        fields = ["pid()", "gettimeofday_ns()"]
108        for type_, name in e.args:
109            name = stap_escape(name)
110            if is_string(type_):
111                fields.append("arg" + name + "_str")
112            else:
113                fields.append(name)
114
115        # Emit the entire record in a single SystemTap printf()
116        arg_str = ', '.join(arg for arg in fields)
117        fmt_str = "%d@%d " + e.name + " " + c_fmt_to_stap(e.fmt) + "\\n"
118        out('    printf("%(fmt_str)s", %(arg_str)s)',
119            fmt_str=fmt_str, arg_str=arg_str)
120
121        out('}')
122
123    out()
124