aboutsummaryrefslogtreecommitdiffstats
path: root/scripts/tracetool
diff options
context:
space:
mode:
authorTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2023-10-10 11:40:56 +0000
committerTimos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>2023-10-10 11:40:56 +0000
commite02cda008591317b1625707ff8e115a4841aa889 (patch)
treeaee302e3cf8b59ec2d32ec481be3d1afddfc8968 /scripts/tracetool
parentcc668e6b7e0ffd8c9d130513d12053cf5eda1d3b (diff)
Introduce Virtio-loopback epsilon release:
Epsilon release introduces a new compatibility layer which make virtio-loopback design to work with QEMU and rust-vmm vhost-user backend without require any changes. Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com> Change-Id: I52e57563e08a7d0bdc002f8e928ee61ba0c53dd9
Diffstat (limited to 'scripts/tracetool')
-rw-r--r--scripts/tracetool/__init__.py513
-rw-r--r--scripts/tracetool/backend/__init__.py125
-rw-r--r--scripts/tracetool/backend/dtrace.py72
-rw-r--r--scripts/tracetool/backend/ftrace.py55
-rw-r--r--scripts/tracetool/backend/log.py64
-rw-r--r--scripts/tracetool/backend/simple.py111
-rw-r--r--scripts/tracetool/backend/syslog.py52
-rw-r--r--scripts/tracetool/backend/ust.py45
-rw-r--r--scripts/tracetool/format/__init__.py84
-rw-r--r--scripts/tracetool/format/c.py72
-rw-r--r--scripts/tracetool/format/d.py77
-rw-r--r--scripts/tracetool/format/h.py103
-rw-r--r--scripts/tracetool/format/log_stap.py129
-rw-r--r--scripts/tracetool/format/simpletrace_stap.py72
-rw-r--r--scripts/tracetool/format/stap.py60
-rw-r--r--scripts/tracetool/format/tcg_h.py83
-rw-r--r--scripts/tracetool/format/tcg_helper_c.py79
-rw-r--r--scripts/tracetool/format/tcg_helper_h.py48
-rw-r--r--scripts/tracetool/format/tcg_helper_wrapper_h.py70
-rw-r--r--scripts/tracetool/format/ust_events_c.py34
-rw-r--r--scripts/tracetool/format/ust_events_h.py105
-rw-r--r--scripts/tracetool/transform.py168
-rw-r--r--scripts/tracetool/vcpu.py69
23 files changed, 2290 insertions, 0 deletions
diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py
new file mode 100644
index 000000000..5bc94d95c
--- /dev/null
+++ b/scripts/tracetool/__init__.py
@@ -0,0 +1,513 @@
+# -*- coding: utf-8 -*-
+
+"""
+Machinery for generating tracing-related intermediate files.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+import re
+import sys
+import weakref
+
+import tracetool.format
+import tracetool.backend
+import tracetool.transform
+
+
+def error_write(*lines):
+ """Write a set of error lines."""
+ sys.stderr.writelines("\n".join(lines) + "\n")
+
+def error(*lines):
+ """Write a set of error lines and exit."""
+ error_write(*lines)
+ sys.exit(1)
+
+
+out_lineno = 1
+out_filename = '<none>'
+out_fobj = sys.stdout
+
+def out_open(filename):
+ global out_filename, out_fobj
+ out_filename = filename
+ out_fobj = open(filename, 'wt')
+
+def out(*lines, **kwargs):
+ """Write a set of output lines.
+
+ You can use kwargs as a shorthand for mapping variables when formatting all
+ the strings in lines.
+
+ The 'out_lineno' kwarg is automatically added to reflect the current output
+ file line number. The 'out_next_lineno' kwarg is also automatically added
+ with the next output line number. The 'out_filename' kwarg is automatically
+ added with the output filename.
+ """
+ global out_lineno
+ output = []
+ for l in lines:
+ kwargs['out_lineno'] = out_lineno
+ kwargs['out_next_lineno'] = out_lineno + 1
+ kwargs['out_filename'] = out_filename
+ output.append(l % kwargs)
+ out_lineno += 1
+
+ out_fobj.writelines("\n".join(output) + "\n")
+
+# We only want to allow standard C types or fixed sized
+# integer types. We don't want QEMU specific types
+# as we can't assume trace backends can resolve all the
+# typedefs
+ALLOWED_TYPES = [
+ "int",
+ "long",
+ "short",
+ "char",
+ "bool",
+ "unsigned",
+ "signed",
+ "int8_t",
+ "uint8_t",
+ "int16_t",
+ "uint16_t",
+ "int32_t",
+ "uint32_t",
+ "int64_t",
+ "uint64_t",
+ "void",
+ "size_t",
+ "ssize_t",
+ "uintptr_t",
+ "ptrdiff_t",
+ # Magic substitution is done by tracetool
+ "TCGv",
+]
+
+def validate_type(name):
+ bits = name.split(" ")
+ for bit in bits:
+ bit = re.sub("\*", "", bit)
+ if bit == "":
+ continue
+ if bit == "const":
+ continue
+ if bit not in ALLOWED_TYPES:
+ raise ValueError("Argument type '%s' is not allowed. "
+ "Only standard C types and fixed size integer "
+ "types should be used. struct, union, and "
+ "other complex pointer types should be "
+ "declared as 'void *'" % name)
+
+class Arguments:
+ """Event arguments description."""
+
+ def __init__(self, args):
+ """
+ Parameters
+ ----------
+ args :
+ List of (type, name) tuples or Arguments objects.
+ """
+ self._args = []
+ for arg in args:
+ if isinstance(arg, Arguments):
+ self._args.extend(arg._args)
+ 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.
+
+ Parameters
+ ----------
+ arg_str : str
+ String describing the event arguments.
+ """
+ res = []
+ for arg in arg_str.split(","):
+ arg = arg.strip()
+ if not arg:
+ raise ValueError("Empty argument (did you forget to use 'void'?)")
+ if arg == 'void':
+ continue
+
+ if '*' in arg:
+ arg_type, identifier = arg.rsplit('*', 1)
+ arg_type += '*'
+ identifier = identifier.strip()
+ else:
+ arg_type, identifier = arg.rsplit(None, 1)
+
+ validate_type(arg_type)
+ res.append((arg_type, identifier))
+ return Arguments(res)
+
+ def __getitem__(self, index):
+ if isinstance(index, slice):
+ return Arguments(self._args[index])
+ else:
+ return self._args[index]
+
+ def __iter__(self):
+ """Iterate over the (type, name) pairs."""
+ return iter(self._args)
+
+ def __len__(self):
+ """Number of arguments."""
+ return len(self._args)
+
+ def __str__(self):
+ """String suitable for declaring function arguments."""
+ if len(self._args) == 0:
+ return "void"
+ else:
+ return ", ".join([ " ".join([t, n]) for t,n in self._args ])
+
+ def __repr__(self):
+ """Evaluable string representation for this object."""
+ return "Arguments(\"%s\")" % str(self)
+
+ def names(self):
+ """List of argument names."""
+ return [ name for _, name in self._args ]
+
+ def types(self):
+ """List of argument types."""
+ return [ type_ for type_, _ in self._args ]
+
+ def casted(self):
+ """List of argument names casted to their type."""
+ return ["(%s)%s" % (type_, name) for type_, name in self._args]
+
+ def transform(self, *trans):
+ """Return a new Arguments instance with transformed types.
+
+ The types in the resulting Arguments instance are transformed according
+ to tracetool.transform.transform_type.
+ """
+ res = []
+ for type_, name in self._args:
+ res.append((tracetool.transform.transform_type(type_, *trans),
+ name))
+ return Arguments(res)
+
+
+class Event(object):
+ """Event description.
+
+ Attributes
+ ----------
+ name : str
+ The event name.
+ fmt : str
+ The event format string.
+ properties : set(str)
+ Properties of the event.
+ args : Arguments
+ The event arguments.
+ lineno : int
+ The line number in the input file.
+ filename : str
+ The path to the input file.
+
+ """
+
+ _CRE = re.compile("((?P<props>[\w\s]+)\s+)?"
+ "(?P<name>\w+)"
+ "\((?P<args>[^)]*)\)"
+ "\s*"
+ "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
+ "\s*")
+
+ _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"])
+
+ def __init__(self, name, props, fmt, args, lineno, filename, orig=None,
+ event_trans=None, event_exec=None):
+ """
+ Parameters
+ ----------
+ name : string
+ Event name.
+ props : list of str
+ Property names.
+ fmt : str, list of str
+ Event printing format string(s).
+ 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
+ self.properties = props
+ self.fmt = fmt
+ 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):
+ """Build an Event instance from a string.
+
+ Parameters
+ ----------
+ line_str : str
+ Line describing the event.
+ lineno : int
+ Line number in input file.
+ filename : str
+ Path to input file.
+ """
+ m = Event._CRE.match(line_str)
+ assert m is not None
+ groups = m.groupdict('')
+
+ 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:
+ raise ValueError("Event format '%m' is forbidden, pass the error "
+ "as an explicit trace argument")
+ if fmt.endswith(r'\n"'):
+ raise ValueError("Event format must not end with a newline "
+ "character")
+
+ if len(fmt_trans) > 0:
+ fmt = [fmt_trans, fmt]
+ args = Arguments.build(groups["args"])
+
+ if "tcg-trans" in props:
+ raise ValueError("Invalid property 'tcg-trans'")
+ if "tcg-exec" in props:
+ raise ValueError("Invalid property 'tcg-exec'")
+ if "tcg" not in props and not isinstance(fmt, str):
+ raise ValueError("Only events with 'tcg' property can have two format strings")
+ if "tcg" in props and isinstance(fmt, str):
+ raise ValueError("Events with 'tcg' property must have two format strings")
+
+ event = Event(name, props, fmt, args, lineno, filename)
+
+ # add implicit arguments when using the 'vcpu' property
+ import tracetool.vcpu
+ event = tracetool.vcpu.transform_event(event)
+
+ return event
+
+ 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)
+ # 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("(%[\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"
+ QEMU_TRACE_NOCHECK = "_nocheck__" + QEMU_TRACE
+ QEMU_TRACE_TCG = QEMU_TRACE + "_tcg"
+ QEMU_DSTATE = "_TRACE_%(NAME)s_DSTATE"
+ QEMU_BACKEND_DSTATE = "TRACE_%(NAME)s_BACKEND_DSTATE"
+ QEMU_EVENT = "_TRACE_%(NAME)s_EVENT"
+
+ def api(self, fmt=None):
+ if fmt is None:
+ fmt = Event.QEMU_TRACE
+ return fmt % {"name": self.name, "NAME": self.name.upper()}
+
+ def transform(self, *trans):
+ """Return a new Event with transformed Arguments."""
+ return Event(self.name,
+ list(self.properties),
+ self.fmt,
+ self.args.transform(*trans),
+ self.lineno,
+ self.filename,
+ self)
+
+
+def read_events(fobj, fname):
+ """Generate the output for the given (format, backends) pair.
+
+ Parameters
+ ----------
+ fobj : file
+ Event description file.
+ fname : str
+ Name of event file
+
+ Returns a list of Event objects
+ """
+
+ events = []
+ for lineno, line in enumerate(fobj, 1):
+ if line[-1] != '\n':
+ raise ValueError("%s does not end with a new line" % fname)
+ if not line.strip():
+ continue
+ if line.lstrip().startswith('#'):
+ continue
+
+ try:
+ event = Event.build(line, lineno, fname)
+ except ValueError as e:
+ arg0 = 'Error at %s:%d: %s' % (fname, lineno, e.args[0])
+ e.args = (arg0,) + e.args[1:]
+ raise
+
+ # transform TCG-enabled events
+ if "tcg" not in event.properties:
+ events.append(event)
+ else:
+ event_trans = event.copy()
+ event_trans.name += "_trans"
+ event_trans.properties += ["tcg-trans"]
+ event_trans.fmt = event.fmt[0]
+ # ignore TCG arguments
+ args_trans = []
+ for atrans, aorig in zip(
+ event_trans.transform(tracetool.transform.TCG_2_HOST).args,
+ event.args):
+ if atrans == aorig:
+ args_trans.append(atrans)
+ event_trans.args = Arguments(args_trans)
+
+ event_exec = event.copy()
+ event_exec.name += "_exec"
+ event_exec.properties += ["tcg-exec"]
+ event_exec.fmt = event.fmt[1]
+ event_exec.args = event_exec.args.transform(tracetool.transform.TCG_2_HOST)
+
+ new_event = [event_trans, event_exec]
+ event.event_trans, event.event_exec = new_event
+
+ events.extend(new_event)
+
+ return events
+
+
+class TracetoolError (Exception):
+ """Exception for calls to generate."""
+ pass
+
+
+def try_import(mod_name, attr_name=None, attr_default=None):
+ """Try to import a module and get an attribute from it.
+
+ Parameters
+ ----------
+ mod_name : str
+ Module name.
+ attr_name : str, optional
+ Name of an attribute in the module.
+ attr_default : optional
+ Default value if the attribute does not exist in the module.
+
+ Returns
+ -------
+ A pair indicating whether the module could be imported and the module or
+ object or attribute value.
+ """
+ try:
+ module = __import__(mod_name, globals(), locals(), ["__package__"])
+ if attr_name is None:
+ return True, module
+ return True, getattr(module, str(attr_name), attr_default)
+ except ImportError:
+ return False, None
+
+
+def generate(events, group, format, backends,
+ binary=None, probe_prefix=None):
+ """Generate the output for the given (format, backends) pair.
+
+ Parameters
+ ----------
+ events : list
+ list of Event objects to generate for
+ group: str
+ Name of the tracing group
+ format : str
+ Output format name.
+ backends : list
+ Output backend names.
+ binary : str or None
+ See tracetool.backend.dtrace.BINARY.
+ probe_prefix : str or None
+ See tracetool.backend.dtrace.PROBEPREFIX.
+ """
+ # fix strange python error (UnboundLocalError tracetool)
+ import tracetool
+
+ format = str(format)
+ if len(format) == 0:
+ raise TracetoolError("format not set")
+ if not tracetool.format.exists(format):
+ raise TracetoolError("unknown format: %s" % format)
+
+ if len(backends) == 0:
+ raise TracetoolError("no backends specified")
+ for backend in backends:
+ if not tracetool.backend.exists(backend):
+ raise TracetoolError("unknown backend: %s" % backend)
+ backend = tracetool.backend.Wrapper(backends, format)
+
+ import tracetool.backend.dtrace
+ tracetool.backend.dtrace.BINARY = binary
+ tracetool.backend.dtrace.PROBEPREFIX = probe_prefix
+
+ tracetool.format.generate(events, format, backend, group)
diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py
new file mode 100644
index 000000000..7bfcc86cc
--- /dev/null
+++ b/scripts/tracetool/backend/__init__.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+
+"""
+Backend management.
+
+
+Creating new backends
+---------------------
+
+A new backend named 'foo-bar' corresponds to Python module
+'tracetool/backend/foo_bar.py'.
+
+A backend module should provide a docstring, whose first non-empty line will be
+considered its short description.
+
+All backends must generate their contents through the 'tracetool.out' routine.
+
+
+Backend attributes
+------------------
+
+========= ====================================================================
+Attribute Description
+========= ====================================================================
+PUBLIC If exists and is set to 'True', the backend is considered "public".
+========= ====================================================================
+
+
+Backend functions
+-----------------
+
+All the following functions are optional, and no output will be generated if
+they do not exist.
+
+=============================== ==============================================
+Function Description
+=============================== ==============================================
+generate_<format>_begin(events) Generate backend- and format-specific file
+ header contents.
+generate_<format>_end(events) Generate backend- and format-specific file
+ footer contents.
+generate_<format>(event) Generate backend- and format-specific contents
+ for the given event.
+=============================== ==============================================
+
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+import os
+
+import tracetool
+
+
+def get_list(only_public = False):
+ """Get a list of (name, description) pairs."""
+ res = [("nop", "Tracing disabled.")]
+ modnames = []
+ for filename in os.listdir(tracetool.backend.__path__[0]):
+ if filename.endswith('.py') and filename != '__init__.py':
+ modnames.append(filename.rsplit('.', 1)[0])
+ for modname in sorted(modnames):
+ module = tracetool.try_import("tracetool.backend." + modname)
+
+ # just in case; should never fail unless non-module files are put there
+ if not module[0]:
+ continue
+ module = module[1]
+
+ public = getattr(module, "PUBLIC", False)
+ if only_public and not public:
+ continue
+
+ doc = module.__doc__
+ if doc is None:
+ doc = ""
+ doc = doc.strip().split("\n")[0]
+
+ name = modname.replace("_", "-")
+ res.append((name, doc))
+ return res
+
+
+def exists(name):
+ """Return whether the given backend exists."""
+ if len(name) == 0:
+ return False
+ if name == "nop":
+ return True
+ name = name.replace("-", "_")
+ return tracetool.try_import("tracetool.backend." + name)[1]
+
+
+class Wrapper:
+ def __init__(self, backends, format):
+ self._backends = [backend.replace("-", "_") for backend in backends]
+ self._format = format.replace("-", "_")
+ for backend in self._backends:
+ assert exists(backend)
+ assert tracetool.format.exists(self._format)
+
+ def _run_function(self, name, *args, **kwargs):
+ for backend in self._backends:
+ func = tracetool.try_import("tracetool.backend." + backend,
+ name % self._format, None)[1]
+ if func is not None:
+ func(*args, **kwargs)
+
+ def generate_begin(self, events, group):
+ self._run_function("generate_%s_begin", events, group)
+
+ def generate(self, event, group):
+ self._run_function("generate_%s", event, group)
+
+ def generate_backend_dstate(self, event, group):
+ self._run_function("generate_%s_backend_dstate", event, group)
+
+ def generate_end(self, events, group):
+ self._run_function("generate_%s_end", events, group)
diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py
new file mode 100644
index 000000000..e17edc9b9
--- /dev/null
+++ b/scripts/tracetool/backend/dtrace.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+
+"""
+DTrace/SystemTAP backend.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+PUBLIC = True
+
+
+PROBEPREFIX = None
+
+def probeprefix():
+ if PROBEPREFIX is None:
+ raise ValueError("you must set PROBEPREFIX")
+ return PROBEPREFIX
+
+
+BINARY = None
+
+def binary():
+ if BINARY is None:
+ raise ValueError("you must set BINARY")
+ return BINARY
+
+
+def generate_h_begin(events, group):
+ if group == "root":
+ header = "trace-dtrace-root.h"
+ else:
+ header = "trace-dtrace-%s.h" % group
+
+ # Workaround for ust backend, which also includes <sys/sdt.h> and may
+ # require SDT_USE_VARIADIC to be defined. If dtrace includes <sys/sdt.h>
+ # first without defining SDT_USE_VARIADIC then ust breaks because the
+ # STAP_PROBEV() macro is not defined.
+ out('#ifndef SDT_USE_VARIADIC')
+ out('#define SDT_USE_VARIADIC 1')
+ out('#endif')
+
+ out('#include "%s"' % header,
+ '')
+
+ out('#undef SDT_USE_VARIADIC')
+
+ # SystemTap defines <provider>_<name>_ENABLED() but other DTrace
+ # implementations might not.
+ for e in events:
+ out('#ifndef QEMU_%(uppername)s_ENABLED',
+ '#define QEMU_%(uppername)s_ENABLED() true',
+ '#endif',
+ uppername=e.name.upper())
+
+def generate_h(event, group):
+ out(' QEMU_%(uppername)s(%(argnames)s);',
+ uppername=event.name.upper(),
+ argnames=", ".join(event.args.names()))
+
+
+def generate_h_backend_dstate(event, group):
+ out(' QEMU_%(uppername)s_ENABLED() || \\',
+ uppername=event.name.upper())
diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py
new file mode 100644
index 000000000..5fa30ccc0
--- /dev/null
+++ b/scripts/tracetool/backend/ftrace.py
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+
+"""
+Ftrace built-in backend.
+"""
+
+__author__ = "Eiichi Tsukata <eiichi.tsukata.xh@hitachi.com>"
+__copyright__ = "Copyright (C) 2013 Hitachi, Ltd."
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+PUBLIC = True
+
+
+def generate_h_begin(events, group):
+ out('#include "trace/ftrace.h"',
+ '')
+
+
+def generate_h(event, group):
+ argnames = ", ".join(event.args.names())
+ if len(event.args) > 0:
+ argnames = ", " + argnames
+
+ out(' {',
+ ' char ftrace_buf[MAX_TRACE_STRLEN];',
+ ' int unused __attribute__ ((unused));',
+ ' int trlen;',
+ ' if (trace_event_get_state(%(event_id)s)) {',
+ '#line %(event_lineno)d "%(event_filename)s"',
+ ' trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN,',
+ ' "%(name)s " %(fmt)s "\\n" %(argnames)s);',
+ '#line %(out_next_lineno)d "%(out_filename)s"',
+ ' trlen = MIN(trlen, MAX_TRACE_STRLEN - 1);',
+ ' unused = write(trace_marker_fd, ftrace_buf, trlen);',
+ ' }',
+ ' }',
+ name=event.name,
+ args=event.args,
+ event_id="TRACE_" + event.name.upper(),
+ event_lineno=event.lineno,
+ event_filename=event.filename,
+ fmt=event.fmt.rstrip("\n"),
+ argnames=argnames)
+
+
+def generate_h_backend_dstate(event, group):
+ out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\',
+ event_id="TRACE_" + event.name.upper())
diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py
new file mode 100644
index 000000000..17ba1cd90
--- /dev/null
+++ b/scripts/tracetool/backend/log.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+
+"""
+Stderr built-in backend.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+PUBLIC = True
+
+
+def generate_h_begin(events, group):
+ out('#include "qemu/log-for-trace.h"',
+ '#include "qemu/error-report.h"',
+ '')
+
+
+def generate_h(event, group):
+ argnames = ", ".join(event.args.names())
+ if len(event.args) > 0:
+ argnames = ", " + argnames
+
+ if "vcpu" in event.properties:
+ # already checked on the generic format code
+ cond = "true"
+ else:
+ cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper())
+
+ out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {',
+ ' if (message_with_timestamp) {',
+ ' struct timeval _now;',
+ ' gettimeofday(&_now, NULL);',
+ '#line %(event_lineno)d "%(event_filename)s"',
+ ' qemu_log("%%d@%%zu.%%06zu:%(name)s " %(fmt)s "\\n",',
+ ' qemu_get_thread_id(),',
+ ' (size_t)_now.tv_sec, (size_t)_now.tv_usec',
+ ' %(argnames)s);',
+ '#line %(out_next_lineno)d "%(out_filename)s"',
+ ' } else {',
+ '#line %(event_lineno)d "%(event_filename)s"',
+ ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);',
+ '#line %(out_next_lineno)d "%(out_filename)s"',
+ ' }',
+ ' }',
+ cond=cond,
+ event_lineno=event.lineno,
+ event_filename=event.filename,
+ name=event.name,
+ fmt=event.fmt.rstrip("\n"),
+ argnames=argnames)
+
+
+def generate_h_backend_dstate(event, group):
+ out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\',
+ event_id="TRACE_" + event.name.upper())
diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py
new file mode 100644
index 000000000..a74d61fcd
--- /dev/null
+++ b/scripts/tracetool/backend/simple.py
@@ -0,0 +1,111 @@
+# -*- coding: utf-8 -*-
+
+"""
+Simple built-in backend.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+PUBLIC = True
+
+
+def is_string(arg):
+ strtype = ('const char*', 'char*', 'const char *', 'char *')
+ arg_strip = arg.lstrip()
+ if arg_strip.startswith(strtype) and arg_strip.count('*') == 1:
+ return True
+ else:
+ return False
+
+
+def generate_h_begin(events, group):
+ for event in events:
+ out('void _simple_%(api)s(%(args)s);',
+ api=event.api(),
+ args=event.args)
+ out('')
+
+
+def generate_h(event, group):
+ out(' _simple_%(api)s(%(args)s);',
+ api=event.api(),
+ args=", ".join(event.args.names()))
+
+
+def generate_h_backend_dstate(event, group):
+ out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\',
+ event_id="TRACE_" + event.name.upper())
+
+
+def generate_c_begin(events, group):
+ out('#include "qemu/osdep.h"',
+ '#include "trace/control.h"',
+ '#include "trace/simple.h"',
+ '')
+
+
+def generate_c(event, group):
+ out('void _simple_%(api)s(%(args)s)',
+ '{',
+ ' TraceBufferRecord rec;',
+ api=event.api(),
+ args=event.args)
+ sizes = []
+ for type_, name in event.args:
+ if is_string(type_):
+ out(' size_t arg%(name)s_len = %(name)s ? MIN(strlen(%(name)s), MAX_TRACE_STRLEN) : 0;',
+ name=name)
+ strsizeinfo = "4 + arg%s_len" % name
+ sizes.append(strsizeinfo)
+ else:
+ sizes.append("8")
+ sizestr = " + ".join(sizes)
+ if len(event.args) == 0:
+ sizestr = '0'
+
+ event_id = 'TRACE_' + event.name.upper()
+ if "vcpu" in event.properties:
+ # already checked on the generic format code
+ cond = "true"
+ else:
+ cond = "trace_event_get_state(%s)" % event_id
+
+ out('',
+ ' if (!%(cond)s) {',
+ ' return;',
+ ' }',
+ '',
+ ' if (trace_record_start(&rec, %(event_obj)s.id, %(size_str)s)) {',
+ ' return; /* Trace Buffer Full, Event Dropped ! */',
+ ' }',
+ cond=cond,
+ event_obj=event.api(event.QEMU_EVENT),
+ size_str=sizestr)
+
+ if len(event.args) > 0:
+ for type_, name in event.args:
+ # string
+ if is_string(type_):
+ out(' trace_record_write_str(&rec, %(name)s, arg%(name)s_len);',
+ name=name)
+ # pointer var (not string)
+ elif type_.endswith('*'):
+ out(' trace_record_write_u64(&rec, (uintptr_t)(uint64_t *)%(name)s);',
+ name=name)
+ # primitive data type
+ else:
+ out(' trace_record_write_u64(&rec, (uint64_t)%(name)s);',
+ name=name)
+
+ out(' trace_record_finish(&rec);',
+ '}',
+ '')
diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py
new file mode 100644
index 000000000..5a3a00fe3
--- /dev/null
+++ b/scripts/tracetool/backend/syslog.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+
+"""
+Syslog built-in backend.
+"""
+
+__author__ = "Paul Durrant <paul.durrant@citrix.com>"
+__copyright__ = "Copyright 2016, Citrix Systems Inc."
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+PUBLIC = True
+
+
+def generate_h_begin(events, group):
+ out('#include <syslog.h>',
+ '')
+
+
+def generate_h(event, group):
+ argnames = ", ".join(event.args.names())
+ if len(event.args) > 0:
+ argnames = ", " + argnames
+
+ if "vcpu" in event.properties:
+ # already checked on the generic format code
+ cond = "true"
+ else:
+ cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper())
+
+ out(' if (%(cond)s) {',
+ '#line %(event_lineno)d "%(event_filename)s"',
+ ' syslog(LOG_INFO, "%(name)s " %(fmt)s %(argnames)s);',
+ '#line %(out_next_lineno)d "%(out_filename)s"',
+ ' }',
+ cond=cond,
+ event_lineno=event.lineno,
+ event_filename=event.filename,
+ name=event.name,
+ fmt=event.fmt.rstrip("\n"),
+ argnames=argnames)
+
+
+def generate_h_backend_dstate(event, group):
+ out(' trace_event_get_state_dynamic_by_id(%(event_id)s) || \\',
+ event_id="TRACE_" + event.name.upper())
diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py
new file mode 100644
index 000000000..c857516f2
--- /dev/null
+++ b/scripts/tracetool/backend/ust.py
@@ -0,0 +1,45 @@
+# -*- coding: utf-8 -*-
+
+"""
+LTTng User Space Tracing backend.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+PUBLIC = True
+
+
+def generate_h_begin(events, group):
+ header = 'trace-ust-' + group + '.h'
+ out('#include <lttng/tracepoint.h>',
+ '#include "%s"' % header,
+ '',
+ '/* tracepoint_enabled() was introduced in LTTng UST 2.7 */',
+ '#ifndef tracepoint_enabled',
+ '#define tracepoint_enabled(a, b) true',
+ '#endif',
+ '')
+
+
+def generate_h(event, group):
+ argnames = ", ".join(event.args.names())
+ if len(event.args) > 0:
+ argnames = ", " + argnames
+
+ out(' tracepoint(qemu, %(name)s%(tp_args)s);',
+ name=event.name,
+ tp_args=argnames)
+
+
+def generate_h_backend_dstate(event, group):
+ out(' tracepoint_enabled(qemu, %(name)s) || \\',
+ name=event.name)
diff --git a/scripts/tracetool/format/__init__.py b/scripts/tracetool/format/__init__.py
new file mode 100644
index 000000000..2dc46f3dd
--- /dev/null
+++ b/scripts/tracetool/format/__init__.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+"""
+Format management.
+
+
+Creating new formats
+--------------------
+
+A new format named 'foo-bar' corresponds to Python module
+'tracetool/format/foo_bar.py'.
+
+A format module should provide a docstring, whose first non-empty line will be
+considered its short description.
+
+All formats must generate their contents through the 'tracetool.out' routine.
+
+
+Format functions
+----------------
+
+======== ==================================================================
+Function Description
+======== ==================================================================
+generate Called to generate a format-specific file.
+======== ==================================================================
+
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+import os
+
+import tracetool
+
+
+def get_list():
+ """Get a list of (name, description) pairs."""
+ res = []
+ modnames = []
+ for filename in os.listdir(tracetool.format.__path__[0]):
+ if filename.endswith('.py') and filename != '__init__.py':
+ modnames.append(filename.rsplit('.', 1)[0])
+ for modname in sorted(modnames):
+ module = tracetool.try_import("tracetool.format." + modname)
+
+ # just in case; should never fail unless non-module files are put there
+ if not module[0]:
+ continue
+ module = module[1]
+
+ doc = module.__doc__
+ if doc is None:
+ doc = ""
+ doc = doc.strip().split("\n")[0]
+
+ name = modname.replace("_", "-")
+ res.append((name, doc))
+ return res
+
+
+def exists(name):
+ """Return whether the given format exists."""
+ if len(name) == 0:
+ return False
+ name = name.replace("-", "_")
+ return tracetool.try_import("tracetool.format." + name)[1]
+
+
+def generate(events, format, backend, group):
+ if not exists(format):
+ raise ValueError("unknown format: %s" % format)
+ format = format.replace("-", "_")
+ func = tracetool.try_import("tracetool.format." + format,
+ "generate")[1]
+ if func is None:
+ raise AttributeError("format has no 'generate': %s" % format)
+ func(events, backend, group)
diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py
new file mode 100644
index 000000000..c390c1844
--- /dev/null
+++ b/scripts/tracetool/format/c.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+
+"""
+trace/generated-tracers.c
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+def generate(events, backend, group):
+ active_events = [e for e in events
+ if "disable" not in e.properties]
+
+ header = "trace-" + group + ".h"
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '',
+ '#include "qemu/osdep.h"',
+ '#include "qemu/module.h"',
+ '#include "%s"' % header,
+ '')
+
+ for e in events:
+ out('uint16_t %s;' % e.api(e.QEMU_DSTATE))
+
+ for e in events:
+ if "vcpu" in e.properties:
+ vcpu_id = 0
+ else:
+ vcpu_id = "TRACE_VCPU_EVENT_NONE"
+ out('TraceEvent %(event)s = {',
+ ' .id = 0,',
+ ' .vcpu_id = %(vcpu_id)s,',
+ ' .name = \"%(name)s\",',
+ ' .sstate = %(sstate)s,',
+ ' .dstate = &%(dstate)s ',
+ '};',
+ event = e.api(e.QEMU_EVENT),
+ vcpu_id = vcpu_id,
+ name = e.name,
+ sstate = "TRACE_%s_ENABLED" % e.name.upper(),
+ dstate = e.api(e.QEMU_DSTATE))
+
+ out('TraceEvent *%(group)s_trace_events[] = {',
+ group = group.lower())
+
+ for e in events:
+ out(' &%(event)s,', event = e.api(e.QEMU_EVENT))
+
+ out(' NULL,',
+ '};',
+ '')
+
+ out('static void trace_%(group)s_register_events(void)',
+ '{',
+ ' trace_event_register_group(%(group)s_trace_events);',
+ '}',
+ 'trace_init(trace_%(group)s_register_events)',
+ group = group.lower())
+
+ backend.generate_begin(active_events, group)
+ for event in active_events:
+ backend.generate(event, group)
+ backend.generate_end(active_events, group)
diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py
new file mode 100644
index 000000000..ebfb71420
--- /dev/null
+++ b/scripts/tracetool/format/d.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+
+"""
+trace/generated-tracers.dtrace (DTrace only).
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+from sys import platform
+
+
+# Reserved keywords from
+# https://wikis.oracle.com/display/DTrace/Types,+Operators+and+Expressions
+RESERVED_WORDS = (
+ 'auto', 'goto', 'sizeof', 'break', 'if', 'static', 'case', 'import',
+ 'string', 'char', 'inline', 'stringof', 'const', 'int', 'struct',
+ 'continue', 'long', 'switch', 'counter', 'offsetof', 'this',
+ 'default', 'probe', 'translator', 'do', 'provider', 'typedef',
+ 'double', 'register', 'union', 'else', 'restrict', 'unsigned',
+ 'enum', 'return', 'void', 'extern', 'self', 'volatile', 'float',
+ 'short', 'while', 'for', 'signed', 'xlate',
+)
+
+
+def generate(events, backend, group):
+ events = [e for e in events
+ if "disable" not in e.properties]
+
+ # SystemTap's dtrace(1) warns about empty "provider qemu {}" but is happy
+ # with an empty file. Avoid the warning.
+ # But dtrace on macOS can't deal with empty files.
+ if not events and platform != "darwin":
+ return
+
+ out('/* This file is autogenerated by tracetool, do not edit. */'
+ '',
+ 'provider qemu {')
+
+ for e in events:
+ args = []
+ for type_, name in e.args:
+ if platform == "darwin":
+ # macOS dtrace accepts only C99 _Bool
+ if type_ == 'bool':
+ type_ = '_Bool'
+ if type_ == 'bool *':
+ type_ = '_Bool *'
+ # It converts int8_t * in probe points to char * in header
+ # files and introduces [-Wpointer-sign] warning.
+ # Avoid it by changing probe type to signed char * beforehand.
+ if type_ == 'int8_t *':
+ type_ = 'signed char *'
+
+ # SystemTap dtrace(1) emits a warning when long long is used
+ type_ = type_.replace('unsigned long long', 'uint64_t')
+ type_ = type_.replace('signed long long', 'int64_t')
+ type_ = type_.replace('long long', 'int64_t')
+
+ if name in RESERVED_WORDS:
+ name += '_'
+ args.append(type_ + ' ' + name)
+
+ # Define prototype for probe arguments
+ out('',
+ 'probe %(name)s(%(args)s);',
+ name=e.name,
+ args=','.join(args))
+
+ out('',
+ '};')
diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py
new file mode 100644
index 000000000..e94f0be7d
--- /dev/null
+++ b/scripts/tracetool/format/h.py
@@ -0,0 +1,103 @@
+# -*- coding: utf-8 -*-
+
+"""
+trace/generated-tracers.h
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+def generate(events, backend, group):
+ if group == "root":
+ header = "trace/control-vcpu.h"
+ else:
+ header = "trace/control.h"
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '',
+ '#ifndef TRACE_%s_GENERATED_TRACERS_H' % group.upper(),
+ '#define TRACE_%s_GENERATED_TRACERS_H' % group.upper(),
+ '',
+ '#include "%s"' % header,
+ '')
+
+ for e in events:
+ out('extern TraceEvent %(event)s;',
+ event = e.api(e.QEMU_EVENT))
+
+ for e in events:
+ out('extern uint16_t %s;' % e.api(e.QEMU_DSTATE))
+
+ # static state
+ for e in events:
+ if 'disable' in e.properties:
+ enabled = 0
+ else:
+ enabled = 1
+ if "tcg-exec" in e.properties:
+ # a single define for the two "sub-events"
+ out('#define TRACE_%(name)s_ENABLED %(enabled)d',
+ name=e.original.name.upper(),
+ enabled=enabled)
+ out('#define TRACE_%s_ENABLED %d' % (e.name.upper(), enabled))
+
+ backend.generate_begin(events, group)
+
+ for e in events:
+ # tracer-specific dstate
+ out('',
+ '#define %(api)s() ( \\',
+ api=e.api(e.QEMU_BACKEND_DSTATE))
+
+ if "disable" not in e.properties:
+ backend.generate_backend_dstate(e, group)
+
+ out(' false)')
+
+ # tracer without checks
+ out('',
+ 'static inline void %(api)s(%(args)s)',
+ '{',
+ api=e.api(e.QEMU_TRACE_NOCHECK),
+ args=e.args)
+
+ if "disable" not in e.properties:
+ backend.generate(e, group)
+
+ out('}')
+
+ # tracer wrapper with checks (per-vCPU tracing)
+ if "vcpu" in e.properties:
+ trace_cpu = next(iter(e.args))[1]
+ cond = "trace_event_get_vcpu_state(%(cpu)s,"\
+ " TRACE_%(id)s)"\
+ % dict(
+ cpu=trace_cpu,
+ id=e.name.upper())
+ else:
+ cond = "true"
+
+ out('',
+ 'static inline void %(api)s(%(args)s)',
+ '{',
+ ' if (%(cond)s) {',
+ ' %(api_nocheck)s(%(names)s);',
+ ' }',
+ '}',
+ api=e.api(),
+ api_nocheck=e.api(e.QEMU_TRACE_NOCHECK),
+ args=e.args,
+ names=", ".join(e.args.names()),
+ cond=cond)
+
+ backend.generate_end(events, group)
+
+ out('#endif /* TRACE_%s_GENERATED_TRACERS_H */' % group.upper())
diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py
new file mode 100644
index 000000000..0b6549d53
--- /dev/null
+++ b/scripts/tracetool/format/log_stap.py
@@ -0,0 +1,129 @@
+# -*- 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("%(\d*)(l+|z)(x|u|d)", "%\\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()
diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py
new file mode 100644
index 000000000..4f4633b4e
--- /dev/null
+++ b/scripts/tracetool/format/simpletrace_stap.py
@@ -0,0 +1,72 @@
+# -*- coding: utf-8 -*-
+
+"""
+Generate .stp file that outputs simpletrace binary traces (DTrace with SystemTAP only).
+"""
+
+__author__ = "Stefan Hajnoczi <redhat.com>"
+__copyright__ = "Copyright (C) 2014, Red Hat, Inc."
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+from tracetool.backend.dtrace import probeprefix
+from tracetool.backend.simple import is_string
+from tracetool.format.stap import stap_escape
+
+def global_var_name(name):
+ return probeprefix().replace(".", "_") + "_" + name
+
+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.simpletrace.%(name)s = %(probeprefix)s.%(name)s ?',
+ '{',
+ probeprefix=probeprefix(),
+ name=e.name)
+
+ # Calculate record size
+ sizes = ['24'] # sizeof(TraceRecord)
+ 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 {}',
+ ' arg%(name)s_len = strlen(arg%(name)s_str)',
+ name=name)
+ sizes.append('4 + arg%s_len' % name)
+ else:
+ sizes.append('8')
+ sizestr = ' + '.join(sizes)
+
+ # Generate format string and value pairs for record header and arguments
+ fields = [('8b', str(event_id)),
+ ('8b', 'gettimeofday_ns()'),
+ ('4b', sizestr),
+ ('4b', 'pid()')]
+ for type_, name in e.args:
+ name = stap_escape(name)
+ if is_string(type_):
+ fields.extend([('4b', 'arg%s_len' % name),
+ ('.*s', 'arg%s_len, arg%s_str' % (name, name))])
+ else:
+ fields.append(('8b', name))
+
+ # Emit the entire record in a single SystemTap printf()
+ fmt_str = '%'.join(fmt for fmt, _ in fields)
+ arg_str = ', '.join(arg for _, arg in fields)
+ out(' printf("%%8b%%%(fmt_str)s", 1, %(arg_str)s)',
+ fmt_str=fmt_str, arg_str=arg_str)
+
+ out('}')
+
+ out()
diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py
new file mode 100644
index 000000000..a218b0445
--- /dev/null
+++ b/scripts/tracetool/format/stap.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+
+"""
+Generate .stp file (DTrace with SystemTAP only).
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+from tracetool.backend.dtrace import binary, probeprefix
+
+
+# Technically 'self' is not used by systemtap yet, but
+# they recommended we keep it in the reserved list anyway
+RESERVED_WORDS = (
+ 'break', 'catch', 'continue', 'delete', 'else', 'for',
+ 'foreach', 'function', 'global', 'if', 'in', 'limit',
+ 'long', 'next', 'probe', 'return', 'self', 'string',
+ 'try', 'while'
+ )
+
+
+def stap_escape(identifier):
+ # Append underscore to reserved keywords
+ if identifier in RESERVED_WORDS:
+ return identifier + '_'
+ return identifier
+
+
+def generate(events, backend, group):
+ events = [e for e in events
+ if "disable" not in e.properties]
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '')
+
+ for e in events:
+ # Define prototype for probe arguments
+ out('probe %(probeprefix)s.%(name)s = process("%(binary)s").mark("%(name)s")',
+ '{',
+ probeprefix=probeprefix(),
+ name=e.name,
+ binary=binary())
+
+ i = 1
+ if len(e.args) > 0:
+ for name in e.args.names():
+ name = stap_escape(name)
+ out(' %s = $arg%d;' % (name, i))
+ i += 1
+
+ out('}')
+
+ out()
diff --git a/scripts/tracetool/format/tcg_h.py b/scripts/tracetool/format/tcg_h.py
new file mode 100644
index 000000000..4d84440af
--- /dev/null
+++ b/scripts/tracetool/format/tcg_h.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+
+"""
+Generate .h file for TCG code generation.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out, Arguments
+import tracetool.vcpu
+
+
+def vcpu_transform_args(args):
+ assert len(args) == 1
+ return Arguments([
+ args,
+ # NOTE: this name must be kept in sync with the one in "tcg_h"
+ # NOTE: Current helper code uses TCGv_env (CPUArchState*)
+ ("TCGv_env", "__tcg_" + args.names()[0]),
+ ])
+
+
+def generate(events, backend, group):
+ if group == "root":
+ header = "trace/trace-root.h"
+ else:
+ header = "trace.h"
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '/* You must include this file after the inclusion of helper.h */',
+ '',
+ '#ifndef TRACE_%s_GENERATED_TCG_TRACERS_H' % group.upper(),
+ '#define TRACE_%s_GENERATED_TCG_TRACERS_H' % group.upper(),
+ '',
+ '#include "exec/helper-proto.h"',
+ '#include "%s"' % header,
+ '',
+ )
+
+ for e in events:
+ # just keep one of them
+ if "tcg-exec" not in e.properties:
+ continue
+
+ out('static inline void %(name_tcg)s(%(args)s)',
+ '{',
+ name_tcg=e.original.api(e.QEMU_TRACE_TCG),
+ args=tracetool.vcpu.transform_args("tcg_h", e.original))
+
+ if "disable" not in e.properties:
+ args_trans = e.original.event_trans.args
+ args_exec = tracetool.vcpu.transform_args(
+ "tcg_helper_c", e.original.event_exec, "wrapper")
+ if "vcpu" in e.properties:
+ trace_cpu = e.args.names()[0]
+ cond = "trace_event_get_vcpu_state(%(cpu)s,"\
+ " TRACE_%(id)s)"\
+ % dict(
+ cpu=trace_cpu,
+ id=e.original.event_exec.name.upper())
+ else:
+ cond = "true"
+
+ out(' %(name_trans)s(%(argnames_trans)s);',
+ ' if (%(cond)s) {',
+ ' gen_helper_%(name_exec)s(%(argnames_exec)s);',
+ ' }',
+ name_trans=e.original.event_trans.api(e.QEMU_TRACE),
+ name_exec=e.original.event_exec.api(e.QEMU_TRACE),
+ argnames_trans=", ".join(args_trans.names()),
+ argnames_exec=", ".join(args_exec.names()),
+ cond=cond)
+
+ out('}')
+
+ out('',
+ '#endif /* TRACE_%s_GENERATED_TCG_TRACERS_H */' % group.upper())
diff --git a/scripts/tracetool/format/tcg_helper_c.py b/scripts/tracetool/format/tcg_helper_c.py
new file mode 100644
index 000000000..72576e67d
--- /dev/null
+++ b/scripts/tracetool/format/tcg_helper_c.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+
+"""
+Generate trace/generated-helpers.c.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import Arguments, out
+from tracetool.transform import *
+import tracetool.vcpu
+
+
+def vcpu_transform_args(args, mode):
+ assert len(args) == 1
+ # NOTE: this name must be kept in sync with the one in "tcg_h"
+ args = Arguments([(args.types()[0], "__tcg_" + args.names()[0])])
+ if mode == "code":
+ return Arguments([
+ # Does cast from helper requirements to tracing types
+ ("CPUState *", "env_cpu(%s)" % args.names()[0]),
+ ])
+ else:
+ args = Arguments([
+ # NOTE: Current helper code uses TCGv_env (CPUArchState*)
+ ("CPUArchState *", args.names()[0]),
+ ])
+ if mode == "header":
+ return args
+ elif mode == "wrapper":
+ return args.transform(HOST_2_TCG)
+ else:
+ assert False
+
+
+def generate(events, backend, group):
+ if group == "root":
+ header = "trace/trace-root.h"
+ else:
+ header = "trace.h"
+
+ events = [e for e in events
+ if "disable" not in e.properties]
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '',
+ '#include "qemu/osdep.h"',
+ '#include "cpu.h"',
+ '#include "exec/helper-proto.h"',
+ '#include "%s"' % header,
+ '',
+ )
+
+ for e in events:
+ if "tcg-exec" not in e.properties:
+ continue
+
+ e_args_api = tracetool.vcpu.transform_args(
+ "tcg_helper_c", e.original, "header").transform(
+ HOST_2_TCG_COMPAT, TCG_2_TCG_HELPER_DEF)
+ e_args_call = tracetool.vcpu.transform_args(
+ "tcg_helper_c", e, "code")
+
+ out('void %(name_tcg)s(%(args_api)s)',
+ '{',
+ # NOTE: the check was already performed at TCG-generation time
+ ' %(name)s(%(args_call)s);',
+ '}',
+ name_tcg="helper_%s_proxy" % e.api(),
+ name=e.api(e.QEMU_TRACE_NOCHECK),
+ args_api=e_args_api,
+ args_call=", ".join(e_args_call.casted()),
+ )
diff --git a/scripts/tracetool/format/tcg_helper_h.py b/scripts/tracetool/format/tcg_helper_h.py
new file mode 100644
index 000000000..08554fbc8
--- /dev/null
+++ b/scripts/tracetool/format/tcg_helper_h.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+
+"""
+Generate trace/generated-helpers.h.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+from tracetool.transform import *
+import tracetool.vcpu
+
+
+def generate(events, backend, group):
+ events = [e for e in events
+ if "disable" not in e.properties]
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '',
+ )
+
+ for e in events:
+ if "tcg-exec" not in e.properties:
+ continue
+
+ # TCG helper proxy declaration
+ fmt = "DEF_HELPER_FLAGS_%(argc)d(%(name)s, %(flags)svoid%(types)s)"
+ e_args = tracetool.vcpu.transform_args("tcg_helper_c", e.original, "header")
+ args = e_args.transform(HOST_2_TCG_COMPAT, HOST_2_TCG,
+ TCG_2_TCG_HELPER_DECL)
+ types = ", ".join(args.types())
+ if types != "":
+ types = ", " + types
+
+ flags = "TCG_CALL_NO_RWG, "
+
+ out(fmt,
+ flags=flags,
+ argc=len(args),
+ name=e.api() + "_proxy",
+ types=types,
+ )
diff --git a/scripts/tracetool/format/tcg_helper_wrapper_h.py b/scripts/tracetool/format/tcg_helper_wrapper_h.py
new file mode 100644
index 000000000..0c5a9797d
--- /dev/null
+++ b/scripts/tracetool/format/tcg_helper_wrapper_h.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+
+"""
+Generate trace/generated-helpers-wrappers.h.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+from tracetool.transform import *
+import tracetool.vcpu
+
+
+def generate(events, backend, group):
+ events = [e for e in events
+ if "disable" not in e.properties]
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '',
+ '#define tcg_temp_new_nop(v) (v)',
+ '#define tcg_temp_free_nop(v)',
+ '',
+ )
+
+ for e in events:
+ if "tcg-exec" not in e.properties:
+ continue
+
+ # tracetool.generate always transforms types to host
+ e_args = tracetool.vcpu.transform_args("tcg_helper_c", e.original, "wrapper")
+
+ # mixed-type to TCG helper bridge
+ args_tcg_compat = e_args.transform(HOST_2_TCG_COMPAT)
+
+ code_new = [
+ "%(tcg_type)s __%(name)s = %(tcg_func)s(%(name)s);" %
+ {"tcg_type": transform_type(type_, HOST_2_TCG),
+ "tcg_func": transform_type(type_, HOST_2_TCG_TMP_NEW),
+ "name": name}
+ for (type_, name) in args_tcg_compat
+ ]
+
+ code_free = [
+ "%(tcg_func)s(__%(name)s);" %
+ {"tcg_func": transform_type(type_, HOST_2_TCG_TMP_FREE),
+ "name": name}
+ for (type_, name) in args_tcg_compat
+ ]
+
+ gen_name = "gen_helper_" + e.api()
+
+ out('static inline void %(name)s(%(args)s)',
+ '{',
+ ' %(code_new)s',
+ ' %(proxy_name)s(%(tmp_names)s);',
+ ' %(code_free)s',
+ '}',
+ name=gen_name,
+ args=e_args,
+ proxy_name=gen_name + "_proxy",
+ code_new="\n ".join(code_new),
+ code_free="\n ".join(code_free),
+ tmp_names=", ".join(["__%s" % name for _, name in e_args]),
+ )
diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py
new file mode 100644
index 000000000..deced9533
--- /dev/null
+++ b/scripts/tracetool/format/ust_events_c.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+"""
+trace/generated-ust.c
+"""
+
+__author__ = "Mohamad Gebai <mohamad.gebai@polymtl.ca>"
+__copyright__ = "Copyright 2012, Mohamad Gebai <mohamad.gebai@polymtl.ca>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+def generate(events, backend, group):
+ events = [e for e in events
+ if "disabled" not in e.properties]
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '',
+ '#include "qemu/osdep.h"',
+ '',
+ '#define TRACEPOINT_DEFINE',
+ '#define TRACEPOINT_CREATE_PROBES',
+ '',
+ '/* If gcc version 4.7 or older is used, LTTng ust gives a warning when compiling with',
+ ' -Wredundant-decls.',
+ ' */',
+ '#pragma GCC diagnostic ignored "-Wredundant-decls"',
+ '',
+ '#include "trace-ust-all.h"')
diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py
new file mode 100644
index 000000000..6ce559f6c
--- /dev/null
+++ b/scripts/tracetool/format/ust_events_h.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+
+"""
+trace/generated-ust-provider.h
+"""
+
+__author__ = "Mohamad Gebai <mohamad.gebai@polymtl.ca>"
+__copyright__ = "Copyright 2012, Mohamad Gebai <mohamad.gebai@polymtl.ca>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import out
+
+
+def generate(events, backend, group):
+ events = [e for e in events
+ if "disabled" not in e.properties]
+
+ if group == "all":
+ include = "trace-ust-all.h"
+ else:
+ include = "trace-ust.h"
+
+ out('/* This file is autogenerated by tracetool, do not edit. */',
+ '',
+ '#undef TRACEPOINT_PROVIDER',
+ '#define TRACEPOINT_PROVIDER qemu',
+ '',
+ '#undef TRACEPOINT_INCLUDE_FILE',
+ '#define TRACEPOINT_INCLUDE_FILE ./%s' % include,
+ '',
+ '#if !defined (TRACE_%s_GENERATED_UST_H) || \\' % group.upper(),
+ ' defined(TRACEPOINT_HEADER_MULTI_READ)',
+ '#define TRACE_%s_GENERATED_UST_H' % group.upper(),
+ '',
+ '#include <lttng/tracepoint.h>',
+ '',
+ '/*',
+ ' * LTTng ust 2.0 does not allow you to use TP_ARGS(void) for tracepoints',
+ ' * requiring no arguments. We define these macros introduced in more recent'
+ ' * versions of LTTng ust as a workaround',
+ ' */',
+ '#ifndef _TP_EXPROTO1',
+ '#define _TP_EXPROTO1(a) void',
+ '#endif',
+ '#ifndef _TP_EXDATA_PROTO1',
+ '#define _TP_EXDATA_PROTO1(a) void *__tp_data',
+ '#endif',
+ '#ifndef _TP_EXDATA_VAR1',
+ '#define _TP_EXDATA_VAR1(a) __tp_data',
+ '#endif',
+ '#ifndef _TP_EXVAR1',
+ '#define _TP_EXVAR1(a)',
+ '#endif',
+ '')
+
+ for e in events:
+ if len(e.args) > 0:
+ out('TRACEPOINT_EVENT(',
+ ' qemu,',
+ ' %(name)s,',
+ ' TP_ARGS(%(args)s),',
+ ' TP_FIELDS(',
+ name=e.name,
+ args=", ".join(", ".join(i) for i in e.args))
+
+ types = e.args.types()
+ names = e.args.names()
+ fmts = e.formats()
+ for t,n,f in zip(types, names, fmts):
+ if ('char *' in t) or ('char*' in t):
+ out(' ctf_string(' + n + ', ' + n + ')')
+ elif ("%p" in f) or ("x" in f) or ("PRIx" in f):
+ out(' ctf_integer_hex('+ t + ', ' + n + ', ' + n + ')')
+ elif ("ptr" in t) or ("*" in t):
+ out(' ctf_integer_hex('+ t + ', ' + n + ', ' + n + ')')
+ elif ('int' in t) or ('long' in t) or ('unsigned' in t) \
+ or ('size_t' in t) or ('bool' in t):
+ out(' ctf_integer(' + t + ', ' + n + ', ' + n + ')')
+ elif ('double' in t) or ('float' in t):
+ out(' ctf_float(' + t + ', ' + n + ', ' + n + ')')
+ elif ('void *' in t) or ('void*' in t):
+ out(' ctf_integer_hex(unsigned long, ' + n + ', ' + n + ')')
+
+ out(' )',
+ ')',
+ '')
+
+ else:
+ out('TRACEPOINT_EVENT(',
+ ' qemu,',
+ ' %(name)s,',
+ ' TP_ARGS(void),',
+ ' TP_FIELDS()',
+ ')',
+ '',
+ name=e.name)
+
+ out('#endif /* TRACE_%s_GENERATED_UST_H */' % group.upper(),
+ '',
+ '/* This part must be outside ifdef protection */',
+ '#include <lttng/tracepoint-event.h>')
diff --git a/scripts/tracetool/transform.py b/scripts/tracetool/transform.py
new file mode 100644
index 000000000..ea8b27799
--- /dev/null
+++ b/scripts/tracetool/transform.py
@@ -0,0 +1,168 @@
+# -*- coding: utf-8 -*-
+
+"""
+Type-transformation rules.
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2012-2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+def _transform_type(type_, trans):
+ if isinstance(trans, str):
+ return trans
+ elif isinstance(trans, dict):
+ if type_ in trans:
+ return _transform_type(type_, trans[type_])
+ elif None in trans:
+ return _transform_type(type_, trans[None])
+ else:
+ return type_
+ elif callable(trans):
+ return trans(type_)
+ else:
+ raise ValueError("Invalid type transformation rule: %s" % trans)
+
+
+def transform_type(type_, *trans):
+ """Return a new type transformed according to the given rules.
+
+ Applies each of the transformation rules in trans in order.
+
+ If an element of trans is a string, return it.
+
+ If an element of trans is a function, call it with type_ as its only
+ argument.
+
+ If an element of trans is a dict, search type_ in its keys. If type_ is
+ a key, use the value as a transformation rule for type_. Otherwise, if
+ None is a key use the value as a transformation rule for type_.
+
+ Otherwise, return type_.
+
+ Parameters
+ ----------
+ type_ : str
+ Type to transform.
+ trans : list of function or dict
+ Type transformation rules.
+ """
+ if len(trans) == 0:
+ raise ValueError
+ res = type_
+ for t in trans:
+ res = _transform_type(res, t)
+ return res
+
+
+##################################################
+# tcg -> host
+
+def _tcg_2_host(type_):
+ if type_ == "TCGv":
+ # force a fixed-size type (target-independent)
+ return "uint64_t"
+ else:
+ return type_
+
+TCG_2_HOST = {
+ "TCGv_i32": "uint32_t",
+ "TCGv_i64": "uint64_t",
+ "TCGv_ptr": "void *",
+ None: _tcg_2_host,
+ }
+
+
+##################################################
+# host -> host compatible with tcg sizes
+
+HOST_2_TCG_COMPAT = {
+ "uint8_t": "uint32_t",
+ "uint16_t": "uint32_t",
+ }
+
+
+##################################################
+# host/tcg -> tcg
+
+def _host_2_tcg(type_):
+ if type_.startswith("TCGv"):
+ return type_
+ raise ValueError("Don't know how to translate '%s' into a TCG type\n" % type_)
+
+HOST_2_TCG = {
+ "uint32_t": "TCGv_i32",
+ "uint64_t": "TCGv_i64",
+ "void *" : "TCGv_ptr",
+ "CPUArchState *": "TCGv_env",
+ None: _host_2_tcg,
+ }
+
+
+##################################################
+# tcg -> tcg helper definition
+
+def _tcg_2_helper_def(type_):
+ if type_ == "TCGv":
+ return "target_ulong"
+ else:
+ return type_
+
+TCG_2_TCG_HELPER_DEF = {
+ "TCGv_i32": "uint32_t",
+ "TCGv_i64": "uint64_t",
+ "TCGv_ptr": "void *",
+ None: _tcg_2_helper_def,
+ }
+
+
+##################################################
+# tcg -> tcg helper declaration
+
+def _tcg_2_tcg_helper_decl_error(type_):
+ raise ValueError("Don't know how to translate type '%s' into a TCG helper declaration type\n" % type_)
+
+TCG_2_TCG_HELPER_DECL = {
+ "TCGv" : "tl",
+ "TCGv_ptr": "ptr",
+ "TCGv_i32": "i32",
+ "TCGv_i64": "i64",
+ "TCGv_env": "env",
+ None: _tcg_2_tcg_helper_decl_error,
+ }
+
+
+##################################################
+# host/tcg -> tcg temporal constant allocation
+
+def _host_2_tcg_tmp_new(type_):
+ if type_.startswith("TCGv"):
+ return "tcg_temp_new_nop"
+ raise ValueError("Don't know how to translate type '%s' into a TCG temporal allocation" % type_)
+
+HOST_2_TCG_TMP_NEW = {
+ "uint32_t": "tcg_const_i32",
+ "uint64_t": "tcg_const_i64",
+ "void *" : "tcg_const_ptr",
+ None: _host_2_tcg_tmp_new,
+ }
+
+
+##################################################
+# host/tcg -> tcg temporal constant deallocation
+
+def _host_2_tcg_tmp_free(type_):
+ if type_.startswith("TCGv"):
+ return "tcg_temp_free_nop"
+ raise ValueError("Don't know how to translate type '%s' into a TCG temporal deallocation" % type_)
+
+HOST_2_TCG_TMP_FREE = {
+ "uint32_t": "tcg_temp_free_i32",
+ "uint64_t": "tcg_temp_free_i64",
+ "void *" : "tcg_temp_free_ptr",
+ None: _host_2_tcg_tmp_free,
+ }
diff --git a/scripts/tracetool/vcpu.py b/scripts/tracetool/vcpu.py
new file mode 100644
index 000000000..868b4cb04
--- /dev/null
+++ b/scripts/tracetool/vcpu.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+
+"""
+Generic management for the 'vcpu' property.
+
+"""
+
+__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
+__copyright__ = "Copyright 2016, Lluís Vilanova <vilanova@ac.upc.edu>"
+__license__ = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__ = "stefanha@redhat.com"
+
+
+from tracetool import Arguments, try_import
+
+
+def transform_event(event):
+ """Transform event to comply with the 'vcpu' property (if present)."""
+ if "vcpu" in event.properties:
+ # events with 'tcg-trans' and 'tcg-exec' are auto-generated from
+ # already-patched events
+ assert "tcg-trans" not in event.properties
+ assert "tcg-exec" not in event.properties
+
+ event.args = Arguments([("void *", "__cpu"), event.args])
+ if "tcg" in event.properties:
+ fmt = "\"cpu=%p \""
+ event.fmt = [fmt + event.fmt[0],
+ fmt + event.fmt[1]]
+ else:
+ fmt = "\"cpu=%p \""
+ event.fmt = fmt + event.fmt
+ return event
+
+
+def transform_args(format, event, *args, **kwargs):
+ """Transforms the arguments to suit the specified format.
+
+ The format module must implement function 'vcpu_args', which receives the
+ implicit arguments added by the 'vcpu' property, and must return suitable
+ arguments for the given format.
+
+ The function is only called for events with the 'vcpu' property.
+
+ Parameters
+ ==========
+ format : str
+ Format module name.
+ event : Event
+ args, kwargs
+ Passed to 'vcpu_transform_args'.
+
+ Returns
+ =======
+ Arguments
+ The transformed arguments, including the non-implicit ones.
+
+ """
+ if "vcpu" in event.properties:
+ ok, func = try_import("tracetool.format." + format,
+ "vcpu_transform_args")
+ assert ok
+ assert func
+ return Arguments([func(event.args[:1], *args, **kwargs),
+ event.args[1:]])
+ else:
+ return event.args