| # -*- 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. */', |
| '') |
| |
| 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() |