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