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