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