summary refs log tree commit diff stats
path: root/scripts/tracetool/format/log_stap.py
blob: 710d62bffe422eac3de5c89a0c2e619fe2af017a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# -*- coding: utf-8 -*-

"""
Generate .stp file that printfs log messages (DTrace with SystemTAP only).
"""

__author__     = "Daniel P. Berrange <berrange@redhat.com>"
__copyright__  = "Copyright (C) 2014-2019, Red Hat, Inc."
__license__    = "GPL version 2 or (at your option) any later version"

__maintainer__ = "Daniel Berrange"
__email__      = "berrange@redhat.com"

import re

from tracetool import out
from tracetool.backend.dtrace import binary, probeprefix
from tracetool.backend.simple import is_string
from tracetool.format.stap import stap_escape

def global_var_name(name):
    return probeprefix().replace(".", "_") + "_" + name

STATE_SKIP = 0
STATE_LITERAL = 1
STATE_MACRO = 2

def c_macro_to_format(macro):
    if macro.startswith("PRI"):
        return macro[3]

    raise Exception("Unhandled macro '%s'" % macro)

def c_fmt_to_stap(fmt):
    state = 0
    bits = []
    literal = ""
    macro = ""
    escape = 0;
    for i in range(len(fmt)):
        if fmt[i] == '\\':
            if escape:
                escape = 0
            else:
                escape = 1
            if state != STATE_LITERAL:
                raise Exception("Unexpected escape outside string literal")
            literal = literal + fmt[i]
        elif fmt[i] == '"' and not escape:
            if state == STATE_LITERAL:
                state = STATE_SKIP
                bits.append(literal)
                literal = ""
            else:
                if state == STATE_MACRO:
                    bits.append(c_macro_to_format(macro))
                    macro = ""
                state = STATE_LITERAL
        elif fmt[i] == ' ' or fmt[i] == '\t':
            if state == STATE_MACRO:
                bits.append(c_macro_to_format(macro))
                macro = ""
                state = STATE_SKIP
            elif state == STATE_LITERAL:
                literal = literal + fmt[i]
        else:
            escape = 0
            if state == STATE_SKIP:
                state = STATE_MACRO

            if state == STATE_LITERAL:
                literal = literal + fmt[i]
            else:
                macro = macro + fmt[i]

    if state == STATE_MACRO:
        bits.append(c_macro_to_format(macro))
    elif state == STATE_LITERAL:
        bits.append(literal)

    # All variables in systemtap are 64-bit in size
    # The "%l" integer size qualifier is thus redundant
    # and "%ll" is not valid at all. Similarly the size_t
    # based "%z" size qualifier is not valid. We just
    # strip all size qualifiers for sanity.
    fmt = re.sub(r"%(\d*)(l+|z)(x|u|d)", r"%\1\3", "".join(bits))
    return fmt

def generate(events, backend, group):
    out('/* This file is autogenerated by tracetool, do not edit. */',
        '/* SPDX-License-Identifier: GPL-2.0-or-later */',
        '')

    for event_id, e in enumerate(events):
        if 'disable' in e.properties:
            continue

        out('probe %(probeprefix)s.log.%(name)s = %(probeprefix)s.%(name)s ?',
            '{',
            probeprefix=probeprefix(),
            name=e.name)

        # Get references to userspace strings
        for type_, name in e.args:
            name = stap_escape(name)
            if is_string(type_):
                out('    try {',
                    '        arg%(name)s_str = %(name)s ? ' +
                    'user_string_n(%(name)s, 512) : "<null>"',
                    '    } catch {}',
                    name=name)

        # Determine systemtap's view of variable names
        fields = ["pid()", "gettimeofday_ns()"]
        for type_, name in e.args:
            name = stap_escape(name)
            if is_string(type_):
                fields.append("arg" + name + "_str")
            else:
                fields.append(name)

        # Emit the entire record in a single SystemTap printf()
        arg_str = ', '.join(arg for arg in fields)
        fmt_str = "%d@%d " + e.name + " " + c_fmt_to_stap(e.fmt) + "\\n"
        out('    printf("%(fmt_str)s", %(arg_str)s)',
            fmt_str=fmt_str, arg_str=arg_str)

        out('}')

    out()