2019-01-23 15:00:16 +03:00
|
|
|
# -*- 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))
|
tracetool: fix "PRI" macro decoding
macro is not reset after use, so the format decoded is always the
one of the first "PRI" in the format string.
For instance:
vhost_vdpa_set_config(void *dev, uint32_t offset, uint32_t size, \
uint32_t flags) "dev: %p offset: %"PRIu32" \
size: %"PRIu32" flags: 0x%"PRIx32
generates:
printf("%d@%d vhost_vdpa_set_config dev: %p offset: %u size: %u \
flags: 0x%u\n", pid(), gettimeofday_ns(), dev, offset, \
size, flags)
for the "flags" parameter, we can see a "0x%u" rather than a "0x%x"
because the first macro was "PRIu32" (for offset).
In the loop, macro becomes "PRIu32PRIu32PRIx32", and c_macro_to_format()
returns always macro[3] ('u' in this case). This patch resets macro after
the format has been decoded.
Signed-off-by: Laurent Vivier <lvivier@redhat.com>
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Message-id: 20210105191721.120463-3-lvivier@redhat.com
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
2021-01-05 22:17:21 +03:00
|
|
|
macro = ""
|
2019-01-23 15:00:16 +03:00
|
|
|
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)
|
|
|
|
|
|
|
|
fmt = re.sub("%(\d*)z(x|u|d)", "%\\1\\2", "".join(bits))
|
|
|
|
return fmt
|
|
|
|
|
|
|
|
def generate(events, backend, group):
|
|
|
|
out('/* This file is autogenerated by tracetool, do not edit. */',
|
|
|
|
'')
|
|
|
|
|
|
|
|
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()
|