diff options
Diffstat (limited to 'scripts/tracetool/__init__.py')
-rw-r--r-- | scripts/tracetool/__init__.py | 202 |
1 files changed, 162 insertions, 40 deletions
diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 1d5238a..74062d2 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# SPDX-License-Identifier: GPL-2.0-or-later """ Machinery for generating tracing-related intermediate files. @@ -15,7 +15,6 @@ __email__ = "stefanha@redhat.com" import os import re import sys -import weakref from pathlib import PurePath import tracetool.backend @@ -31,6 +30,49 @@ def error(*lines): error_write(*lines) sys.exit(1) +FMT_TOKEN = re.compile(r'''(?: + " ( (?: [^"\\] | \\[\\"abfnrt] | # a string literal + \\x[0-9a-fA-F][0-9a-fA-F]) *? ) " + | ( PRI [duixX] (?:8|16|32|64|PTR|MAX) ) # a PRIxxx macro + | \s+ # spaces (ignored) + )''', re.X) + +PRI_SIZE_MAP = { + '8': 'hh', + '16': 'h', + '32': '', + '64': 'll', + 'PTR': 't', + 'MAX': 'j', +} + +def expand_format_string(c_fmt, prefix=""): + def pri_macro_to_fmt(pri_macro): + assert pri_macro.startswith("PRI") + fmt_type = pri_macro[3] # 'd', 'i', 'u', or 'x' + fmt_size = pri_macro[4:] # '8', '16', '32', '64', 'PTR', 'MAX' + + size = PRI_SIZE_MAP.get(fmt_size, None) + if size is None: + raise Exception(f"unknown macro {pri_macro}") + return size + fmt_type + + result = prefix + pos = 0 + while pos < len(c_fmt): + m = FMT_TOKEN.match(c_fmt, pos) + if not m: + print("No match at position", pos, ":", repr(c_fmt[pos:]), file=sys.stderr) + raise Exception("syntax error in trace file") + if m[1]: + substr = m[1] + elif m[2]: + substr = pri_macro_to_fmt(m[2]) + else: + substr = "" + result += substr + pos = m.end() + return result out_lineno = 1 out_filename = '<none>' @@ -90,6 +132,49 @@ ALLOWED_TYPES = [ "ptrdiff_t", ] +C_TYPE_KEYWORDS = {"char", "int", "void", "short", "long", "signed", "unsigned"} + +C_TO_RUST_TYPE_MAP = { + "int": "std::ffi::c_int", + "long": "std::ffi::c_long", + "long long": "std::ffi::c_longlong", + "short": "std::ffi::c_short", + "char": "std::ffi::c_char", + "bool": "bool", + "unsigned": "std::ffi::c_uint", + # multiple keywords, keep them sorted + "long unsigned": "std::ffi::c_long", + "long long unsigned": "std::ffi::c_ulonglong", + "short unsigned": "std::ffi::c_ushort", + "char unsigned": "u8", + "int8_t": "i8", + "uint8_t": "u8", + "int16_t": "i16", + "uint16_t": "u16", + "int32_t": "i32", + "uint32_t": "u32", + "int64_t": "i64", + "uint64_t": "u64", + "void": "()", + "size_t": "usize", + "ssize_t": "isize", + "uintptr_t": "usize", + "ptrdiff_t": "isize", +} + +# Rust requires manual casting of <32-bit types when passing them to +# variable-argument functions. +RUST_VARARGS_SMALL_TYPES = { + "std::ffi::c_short", + "std::ffi::c_ushort", + "std::ffi::c_char", + "i8", + "u8", + "i16", + "u16", + "bool", +} + def validate_type(name): bits = name.split(" ") for bit in bits: @@ -105,6 +190,38 @@ def validate_type(name): "other complex pointer types should be " "declared as 'void *'" % name) +def c_type_to_rust(name): + ptr = False + const = False + name = name.rstrip() + if name[-1] == '*': + name = name[:-1].rstrip() + ptr = True + if name[-1] == '*': + # pointers to pointers are the same as void* + name = "void" + + bits = name.split() + if "const" in bits: + const = True + bits.remove("const") + if bits[0] in C_TYPE_KEYWORDS: + if "signed" in bits: + bits.remove("signed") + if len(bits) > 1 and "int" in bits: + bits.remove("int") + bits.sort() + name = ' '.join(bits) + else: + if len(bits) > 1: + raise ValueError("Invalid type '%s'." % name) + name = bits[0] + + ty = C_TO_RUST_TYPE_MAP[name.strip()] + if ptr: + ty = f'*{"const" if const else "mut"} {ty}' + return ty + class Arguments: """Event arguments description.""" @@ -122,10 +239,6 @@ class Arguments: else: self._args.append(arg) - def copy(self): - """Create a new copy.""" - return Arguments(list(self._args)) - @staticmethod def build(arg_str): """Build and Arguments instance from an argument string. @@ -197,6 +310,43 @@ class Arguments: """List of argument names casted to their type.""" return ["(%s)%s" % (type_, name) for type_, name in self._args] + def rust_decl_extern(self): + """Return a Rust argument list for an extern "C" function""" + return ", ".join((f"_{name}: {c_type_to_rust(type_)}" + for type_, name in self._args)) + + def rust_decl(self): + """Return a Rust argument list for a tracepoint function""" + def decl_type(type_): + if type_ == "const char *": + return "&std::ffi::CStr" + return c_type_to_rust(type_) + + return ", ".join((f"_{name}: {decl_type(type_)}" + for type_, name in self._args)) + + def rust_call_extern(self): + """Return a Rust argument list for a call to an extern "C" function""" + def rust_cast(name, type_): + if type_ == "const char *": + return f"_{name}.as_ptr()" + return f"_{name}" + + return ", ".join((rust_cast(name, type_) for type_, name in self._args)) + + def rust_call_varargs(self): + """Return a Rust argument list for a call to a C varargs function""" + def rust_cast(name, type_): + if type_ == "const char *": + return f"_{name}.as_ptr()" + + type_ = c_type_to_rust(type_) + if type_ in RUST_VARARGS_SMALL_TYPES: + return f"_{name} as std::ffi::c_int" + return f"_{name} /* as {type_} */" + + return ", ".join((rust_cast(name, type_) for type_, name in self._args)) + class Event(object): """Event description. @@ -222,13 +372,12 @@ class Event(object): r"(?P<name>\w+)" r"\((?P<args>[^)]*)\)" r"\s*" - r"(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?" + r"(?P<fmt>\".+)?" r"\s*") _VALID_PROPS = set(["disable"]) - def __init__(self, name, props, fmt, args, lineno, filename, orig=None, - event_trans=None, event_exec=None): + def __init__(self, name, props, fmt, args, lineno, filename): """ Parameters ---------- @@ -236,20 +385,14 @@ class Event(object): Event name. props : list of str Property names. - fmt : str, list of str - Event printing format string(s). + fmt : str + Event printing format string. args : Arguments Event arguments. lineno : int The line number in the input file. filename : str The path to the input file. - orig : Event or None - Original Event before transformation/generation. - event_trans : Event or None - Generated translation-time event ("tcg" property). - event_exec : Event or None - Generated execution-time event ("tcg" property). """ self.name = name @@ -258,29 +401,16 @@ class Event(object): self.args = args self.lineno = int(lineno) self.filename = str(filename) - self.event_trans = event_trans - self.event_exec = event_exec if len(args) > 10: raise ValueError("Event '%s' has more than maximum permitted " "argument count" % name) - if orig is None: - self.original = weakref.ref(self) - else: - self.original = orig - unknown_props = set(self.properties) - self._VALID_PROPS if len(unknown_props) > 0: raise ValueError("Unknown properties: %s" % ", ".join(unknown_props)) - assert isinstance(self.fmt, str) or len(self.fmt) == 2 - def copy(self): - """Create a new copy.""" - return Event(self.name, list(self.properties), self.fmt, - self.args.copy(), self.lineno, self.filename, - self, self.event_trans, self.event_exec) @staticmethod def build(line_str, lineno, filename): @@ -302,8 +432,7 @@ class Event(object): name = groups["name"] props = groups["props"].split() fmt = groups["fmt"] - fmt_trans = groups["fmt_trans"] - if fmt.find("%m") != -1 or fmt_trans.find("%m") != -1: + if fmt.find("%m") != -1: raise ValueError("Event format '%m' is forbidden, pass the error " "as an explicit trace argument") if fmt.endswith(r'\n"'): @@ -312,29 +441,22 @@ class Event(object): if '\\n' in fmt: raise ValueError("Event format must not use new line character") - if len(fmt_trans) > 0: - fmt = [fmt_trans, fmt] args = Arguments.build(groups["args"]) return Event(name, props, fmt, args, lineno, posix_relpath(filename)) def __repr__(self): """Evaluable string representation for this object.""" - if isinstance(self.fmt, str): - fmt = self.fmt - else: - fmt = "%s, %s" % (self.fmt[0], self.fmt[1]) return "Event('%s %s(%s) %s')" % (" ".join(self.properties), self.name, self.args, - fmt) + self.fmt) # Star matching on PRI is dangerous as one might have multiple # arguments with that format, hence the non-greedy version of it. _FMT = re.compile(r"(%[\d\.]*\w+|%.*?PRI\S+)") def formats(self): """List conversion specifiers in the argument print format string.""" - assert not isinstance(self.fmt, list) return self._FMT.findall(self.fmt) QEMU_TRACE = "trace_%(name)s" |