From 650ab98d1d9551f0ca2180c0d88427acfcb081cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Vilanova?= Date: Tue, 3 Apr 2012 20:47:39 +0200 Subject: [PATCH 1/9] tracetool: Rewrite infrastructure as python modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tracetool script is written in shell and has hit several portability problems due to shell quirks or external tools across host platforms. Additionally the amount of string processing and lack of real data structures makes it tough to implement code generator backends for tracers that are more complex. This patch replaces the shell version of tracetool with a Python version. The new tracetool design is: scripts/tracetool.py - top-level script scripts/tracetool/backend/ - tracer backends live here (simple, ust) scripts/tracetool/format/ - output formats live here (.c, .h) There is common code for trace-events definition parsing so that backends can focus on generating code rather than parsing input. Support for all existing backends (nop, stderr, simple, ust, and dtrace) is added back in follow-up patches. [Commit description written by Stefan Hajnoczi] Signed-off-by: Lluís Vilanova Signed-off-by: Stefan Hajnoczi --- Makefile.objs | 6 +- Makefile.target | 13 +- configure | 4 +- scripts/tracetool | 666 -------------------------- scripts/tracetool.py | 108 +++++ scripts/tracetool/__init__.py | 262 ++++++++++ scripts/tracetool/backend/__init__.py | 111 +++++ scripts/tracetool/format/__init__.py | 99 ++++ 8 files changed, 592 insertions(+), 677 deletions(-) delete mode 100755 scripts/tracetool create mode 100755 scripts/tracetool.py create mode 100644 scripts/tracetool/__init__.py create mode 100644 scripts/tracetool/backend/__init__.py create mode 100644 scripts/tracetool/format/__init__.py diff --git a/Makefile.objs b/Makefile.objs index 5c3bcdaa39..6d6f24d9d3 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -378,12 +378,12 @@ else trace.h: trace.h-timestamp endif trace.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -h < $< > $@," GEN trace.h") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=h --backend=$(TRACE_BACKEND) < $< > $@," GEN trace.h") @cmp -s $@ trace.h || cp $@ trace.h trace.c: trace.c-timestamp trace.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -c < $< > $@," GEN trace.c") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=c --backend=$(TRACE_BACKEND) < $< > $@," GEN trace.c") @cmp -s $@ trace.c || cp $@ trace.c trace.o: trace.c $(GENERATED_HEADERS) @@ -396,7 +396,7 @@ trace-dtrace.h: trace-dtrace.dtrace # rule file. So we use '.dtrace' instead trace-dtrace.dtrace: trace-dtrace.dtrace-timestamp trace-dtrace.dtrace-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/config-host.mak - $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool --$(TRACE_BACKEND) -d < $< > $@," GEN trace-dtrace.dtrace") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py --format=d --backend=$(TRACE_BACKEND) < $< > $@," GEN trace-dtrace.dtrace") @cmp -s $@ trace-dtrace.dtrace || cp $@ trace-dtrace.dtrace trace-dtrace.o: trace-dtrace.dtrace $(GENERATED_HEADERS) diff --git a/Makefile.target b/Makefile.target index 84951a09ec..a0540cd663 100644 --- a/Makefile.target +++ b/Makefile.target @@ -59,12 +59,13 @@ TARGET_TYPE=system endif $(QEMU_PROG).stp: $(SRC_PATH)/trace-events - $(call quiet-command,sh $(SRC_PATH)/scripts/tracetool \ - --$(TRACE_BACKEND) \ - --binary $(bindir)/$(QEMU_PROG) \ - --target-arch $(TARGET_ARCH) \ - --target-type $(TARGET_TYPE) \ - --stap < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp") + $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/tracetool.py \ + --format=stap \ + --backend=$(TRACE_BACKEND) \ + --binary=$(bindir)/$(QEMU_PROG) \ + --target-arch=$(TARGET_ARCH) \ + --target-type=$(TARGET_TYPE) \ + < $(SRC_PATH)/trace-events > $(QEMU_PROG).stp," GEN $(QEMU_PROG).stp") else stap: endif diff --git a/configure b/configure index 2d62d12796..03b49f6b70 100755 --- a/configure +++ b/configure @@ -1097,7 +1097,7 @@ echo " --disable-docs disable documentation build" echo " --disable-vhost-net disable vhost-net acceleration support" echo " --enable-vhost-net enable vhost-net acceleration support" echo " --enable-trace-backend=B Set trace backend" -echo " Available backends:" $("$source_path"/scripts/tracetool --list-backends) +echo " Available backends:" $($python "$source_path"/scripts/tracetool.py --list-backends) echo " --with-trace-file=NAME Full PATH,NAME of file to store traces" echo " Default:trace-" echo " --disable-spice disable spice" @@ -2670,7 +2670,7 @@ fi ########################################## # check if trace backend exists -sh "$source_path/scripts/tracetool" "--$trace_backend" --check-backend > /dev/null 2> /dev/null +$python "$source_path/scripts/tracetool.py" "--backend=$trace_backend" --check-backend > /dev/null 2> /dev/null if test "$?" -ne 0 ; then echo echo "Error: invalid trace backend" diff --git a/scripts/tracetool b/scripts/tracetool deleted file mode 100755 index 7b1c142b67..0000000000 --- a/scripts/tracetool +++ /dev/null @@ -1,666 +0,0 @@ -#!/bin/sh -# -# Code generator for trace events -# -# Copyright IBM, Corp. 2010 -# -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. - -# Disable pathname expansion, makes processing text with '*' characters simpler -set -f - -usage() -{ - cat >&2 < return 0 if property is present, or 1 otherwise -has_property() -{ - local props prop - props=${1%%\(*} - props=${props% *} - for prop in $props; do - if [ "$prop" = "$2" ]; then - return 0 - fi - done - return 1 -} - -# Get the argument list of a trace event, including types and names -get_args() -{ - local args - args=${1#*\(} - args=${args%%\)*} - echo "$args" - - if (echo "$args" | grep "[ *]next\($\|[, ]\)" > /dev/null 2>&1); then - echo -e "\n#error 'next' is a bad argument name (clash with systemtap keyword)\n " - fi -} - -# Get the argument name list of a trace event -get_argnames() -{ - local nfields field name sep - nfields=0 - sep="$2" - for field in $(get_args "$1"); do - nfields=$((nfields + 1)) - - # Drop pointer star - field=${field#\*} - - # Only argument names have commas at the end - name=${field%,} - test "$field" = "$name" && continue - - printf "%s%s " $name $sep - done - - # Last argument name - if [ "$nfields" -gt 1 ] - then - printf "%s" "$name" - fi -} - -# Get the number of arguments to a trace event -get_argc() -{ - local name argc - argc=0 - for name in $(get_argnames "$1", ","); do - argc=$((argc + 1)) - done - echo $argc -} - -# Get the format string including double quotes for a trace event -get_fmt() -{ - puts "${1#*)}" -} - -linetoh_begin_nop() -{ - return -} - -linetoh_nop() -{ - local name args - name=$(get_name "$1") - args=$(get_args "$1") - - # Define an empty function for the trace event - cat < -#include "trace/stderr.h" - -extern TraceEvent trace_list[]; -EOF - - stderr_event_num=0 -} - -linetoh_stderr() -{ - local name args argnames argc fmt - name=$(get_name "$1") - args=$(get_args "$1") - argnames=$(get_argnames "$1" ",") - argc=$(get_argc "$1") - fmt=$(get_fmt "$1") - - if [ "$argc" -gt 0 ]; then - argnames=", $argnames" - fi - - cat <" - ust_clean_namespace -} - -linetoh_ust() -{ - local name args argnames - name=$(get_name "$1") - args=$(get_args "$1") - argnames=$(get_argnames "$1", ",") - - cat < -$(ust_clean_namespace) -#include "trace.h" -EOF -} - -linetoc_ust() -{ - local name args argnames fmt - name=$(get_name "$1") - args=$(get_args "$1") - argnames=$(get_argnames "$1", ",") - [ -z "$argnames" ] || argnames=", $argnames" - fmt=$(get_fmt "$1") - - cat < --backend= [] + +Backends: +%(backends)s + +Formats: +%(formats)s + +Options: + --help This help message. + --list-backends Print list of available backends. + --check-backend Check if the given backend is valid. +""" % { + "script" : _SCRIPT, + "backends" : backend_descr, + "formats" : format_descr, + }) + + if msg is None: + sys.exit(0) + else: + sys.exit(1) + + +def main(args): + global _SCRIPT + _SCRIPT = args[0] + + long_opts = [ "backend=", "format=", "help", "list-backends", "check-backend" ] + long_opts += [ "binary=", "target-type=", "target-arch=", "probe-prefix=" ] + + try: + opts, args = getopt.getopt(args[1:], "", long_opts) + except getopt.GetoptError as err: + error_opt(str(err)) + + check_backend = False + arg_backend = "" + arg_format = "" + for opt, arg in opts: + if opt == "--help": + error_opt() + + elif opt == "--backend": + arg_backend = arg + elif opt == "--format": + arg_format = arg + + elif opt == "--list-backends": + backends = tracetool.backend.get_list() + out(", ".join([ b for b,_ in backends ])) + sys.exit(0) + elif opt == "--check-backend": + check_backend = True + + else: + error_opt("unhandled option: %s" % opt) + + if arg_backend is None: + error_opt("backend not set") + + if check_backend: + if tracetool.backend.exists(arg_backend): + sys.exit(0) + else: + sys.exit(1) + + try: + tracetool.generate(sys.stdin, arg_format, arg_backend) + except tracetool.TracetoolError as e: + error_opt(str(e)) + +if __name__ == "__main__": + main(sys.argv) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py new file mode 100644 index 0000000000..1719bb4f92 --- /dev/null +++ b/scripts/tracetool/__init__.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Machinery for generating tracing-related intermediate files. +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +import re +import sys + +import tracetool.format +import tracetool.backend + + +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) + + +def out(*lines, **kwargs): + """Write a set of output lines. + + You can use kwargs as a shorthand for mapping variables when formating all + the strings in lines. + """ + lines = [ l % kwargs for l in lines ] + sys.stdout.writelines("\n".join(lines) + "\n") + + +class Arguments: + """Event arguments description.""" + + def __init__(self, args): + """ + Parameters + ---------- + args : + List of (type, name) tuples. + """ + self._args = 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() + parts = arg.split() + head, sep, tail = parts[-1].rpartition("*") + parts = parts[:-1] + if tail == "void": + assert len(parts) == 0 and sep == "" + continue + arg_type = " ".join(parts + [ " ".join([head, sep]).strip() ]).strip() + res.append((arg_type, tail)) + return Arguments(res) + + 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 ] + + +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. + """ + + _CRE = re.compile("((?P.*)\s+)?(?P[^(\s]+)\((?P[^)]*)\)\s*(?P\".*)?") + + _VALID_PROPS = set(["disable"]) + + def __init__(self, name, props, fmt, args): + """ + Parameters + ---------- + name : string + Event name. + props : list of str + Property names. + fmt : str + Event printing format. + args : Arguments + Event arguments. + """ + self.name = name + self.properties = props + self.fmt = fmt + self.args = args + + unknown_props = set(self.properties) - self._VALID_PROPS + if len(unknown_props) > 0: + raise ValueError("Unknown properties: %s" % ", ".join(unknown_props)) + + @staticmethod + def build(line_str): + """Build an Event instance from a string. + + Parameters + ---------- + line_str : str + Line describing the event. + """ + m = Event._CRE.match(line_str) + assert m is not None + groups = m.groupdict('') + + name = groups["name"] + props = groups["props"].split() + fmt = groups["fmt"] + args = Arguments.build(groups["args"]) + + return Event(name, props, fmt, args) + + def __repr__(self): + """Evaluable string representation for this object.""" + return "Event('%s %s(%s) %s')" % (" ".join(self.properties), + self.name, + self.args, + self.fmt) + +def _read_events(fobj): + res = [] + for line in fobj: + if not line.strip(): + continue + if line.lstrip().startswith('#'): + continue + res.append(Event.build(line)) + return res + + +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, fromlist=["__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(fevents, format, backend): + """Generate the output for the given (format, backend) pair. + + Parameters + ---------- + fevents : file + Event description file. + format : str + Output format name. + backend : str + Output backend name. + """ + # fix strange python error (UnboundLocalError tracetool) + import tracetool + + format = str(format) + if len(format) is 0: + raise TracetoolError("format not set") + mformat = format.replace("-", "_") + if not tracetool.format.exists(mformat): + raise TracetoolError("unknown format: %s" % format) + + backend = str(backend) + if len(backend) is 0: + raise TracetoolError("backend not set") + mbackend = backend.replace("-", "_") + if not tracetool.backend.exists(mbackend): + raise TracetoolError("unknown backend: %s" % backend) + + if not tracetool.backend.compatible(mbackend, mformat): + raise TracetoolError("backend '%s' not compatible with format '%s'" % + (backend, format)) + + events = _read_events(fevents) + + if backend == "nop": + ( e.properies.add("disable") for e in events ) + + tracetool.format.generate_begin(mformat, events) + tracetool.backend.generate("nop", format, + [ e + for e in events + if "disable" in e.properties ]) + tracetool.backend.generate(backend, format, + [ e + for e in events + if "disable" not in e.properties ]) + tracetool.format.generate_end(mformat, events) diff --git a/scripts/tracetool/backend/__init__.py b/scripts/tracetool/backend/__init__.py new file mode 100644 index 0000000000..34b7ed8081 --- /dev/null +++ b/scripts/tracetool/backend/__init__.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# -*- 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 functions +----------------- + +======== ======================================================================= +Function Description +======== ======================================================================= + Called to generate the format- and backend-specific code for each of + the specified events. If the function does not exist, the backend is + considered not compatible with the given format. +======== ======================================================================= +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +import pkgutil + +import tracetool + + +def get_list(): + """Get a list of (name, description) pairs.""" + res = [("nop", "Tracing disabled.")] + for _, modname, _ in pkgutil.iter_modules(tracetool.backend.__path__): + 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] + + 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] + + +def compatible(backend, format): + """Whether a backend is compatible with the given format.""" + if not exists(backend): + raise ValueError("unknown backend: %s" % backend) + + backend = backend.replace("-", "_") + format = format.replace("-", "_") + + if backend == "nop": + return True + else: + func = tracetool.try_import("tracetool.backend." + backend, + format, None)[1] + return func is not None + + +def _empty(events): + pass + +def generate(backend, format, events): + """Generate the per-event output for the given (backend, format) pair.""" + if not compatible(backend, format): + raise ValueError("backend '%s' not compatible with format '%s'" % + (backend, format)) + + backend = backend.replace("-", "_") + format = format.replace("-", "_") + + if backend == "nop": + func = tracetool.try_import("tracetool.format." + format, + "nop", _empty)[1] + else: + func = tracetool.try_import("tracetool.backend." + backend, + format, None)[1] + + func(events) diff --git a/scripts/tracetool/format/__init__.py b/scripts/tracetool/format/__init__.py new file mode 100644 index 0000000000..0e4baf0e56 --- /dev/null +++ b/scripts/tracetool/format/__init__.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# -*- 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 +---------------- + +All the following functions are optional, and no output will be generated if +they do not exist. + +======== ======================================================================= +Function Description +======== ======================================================================= +begin Called to generate the format-specific file header. +end Called to generate the format-specific file footer. +nop Called to generate the per-event contents when the event is disabled or + the selected backend is 'nop'. +======== ======================================================================= +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +import pkgutil + +import tracetool + + +def get_list(): + """Get a list of (name, description) pairs.""" + res = [] + for _, modname, _ in pkgutil.iter_modules(tracetool.format.__path__): + 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 _empty(events): + pass + +def generate_begin(name, events): + """Generate the header of the format-specific file.""" + if not exists(name): + raise ValueError("unknown format: %s" % name) + + name = name.replace("-", "_") + func = tracetool.try_import("tracetool.format." + name, + "begin", _empty)[1] + func(events) + +def generate_end(name, events): + """Generate the footer of the format-specific file.""" + if not exists(name): + raise ValueError("unknown format: %s" % name) + + name = name.replace("-", "_") + func = tracetool.try_import("tracetool.format." + name, + "end", _empty)[1] + func(events) From 5de7f9c8eefcb42e5657817574b6f8b3b3720af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Vilanova?= Date: Tue, 3 Apr 2012 20:47:44 +0200 Subject: [PATCH 2/9] tracetool: Add module for the 'c' format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lluís Vilanova Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/format/c.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 scripts/tracetool/format/c.py diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py new file mode 100644 index 0000000000..35555aee1f --- /dev/null +++ b/scripts/tracetool/format/c.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .c file. +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */') From c419e62a037d44520b3d3a3849b919f8cdaf2249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Vilanova?= Date: Tue, 3 Apr 2012 20:47:50 +0200 Subject: [PATCH 3/9] tracetool: Add module for the 'h' format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lluís Vilanova Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/format/h.py | 45 +++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 scripts/tracetool/format/h.py diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py new file mode 100644 index 0000000000..6ffb3c2f4e --- /dev/null +++ b/scripts/tracetool/format/h.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .h file. +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */', + '', + '#ifndef TRACE_H', + '#define TRACE_H', + '', + '#include "qemu-common.h"') + +def end(events): + for e in events: + if "disable" in e.properties: + enabled = 0 + else: + enabled = 1 + out('#define TRACE_%s_ENABLED %d' % (e.name.upper(), enabled)) + out('', + '#endif /* TRACE_H */') + +def nop(events): + for e in events: + out('', + 'static inline void trace_%(name)s(%(args)s)', + '{', + '}', + name = e.name, + args = e.args, + ) From 9008d85a96bb42ed40da6b4d70a0f14b9353f442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Vilanova?= Date: Tue, 3 Apr 2012 20:47:55 +0200 Subject: [PATCH 4/9] tracetool: Add support for the 'stderr' backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lluís Vilanova Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/stderr.py | 56 +++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 scripts/tracetool/backend/stderr.py diff --git a/scripts/tracetool/backend/stderr.py b/scripts/tracetool/backend/stderr.py new file mode 100644 index 0000000000..917fde7c15 --- /dev/null +++ b/scripts/tracetool/backend/stderr.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Stderr built-in backend. +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def c(events): + out('#include "trace.h"', + '', + 'TraceEvent trace_list[] = {') + + for e in events: + out('{.tp_name = "%(name)s", .state=0},', + name = e.name, + ) + + out('};') + +def h(events): + out('#include ', + '#include "trace/stderr.h"', + '', + 'extern TraceEvent trace_list[];') + + for num, e in enumerate(events): + argnames = ", ".join(e.args.names()) + if len(e.args) > 0: + argnames = ", " + argnames + + out('static inline void trace_%(name)s(%(args)s)', + '{', + ' if (trace_list[%(event_num)s].state != 0) {', + ' fprintf(stderr, "%(name)s " %(fmt)s "\\n" %(argnames)s);', + ' }', + '}', + name = e.name, + args = e.args, + event_num = num, + fmt = e.fmt, + argnames = argnames, + ) + + out('', + '#define NR_TRACE_EVENTS %d' % len(events)) From dd03a39e8a5952fbb0d7a3960d45c33b726019a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Vilanova?= Date: Tue, 3 Apr 2012 20:48:01 +0200 Subject: [PATCH 5/9] tracetool: Add support for the 'simple' backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lluís Vilanova Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/simple.py | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 scripts/tracetool/backend/simple.py diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py new file mode 100644 index 0000000000..fbb5717c66 --- /dev/null +++ b/scripts/tracetool/backend/simple.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Simple built-in backend. +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def c(events): + out('#include "trace.h"', + '', + 'TraceEvent trace_list[] = {') + + for e in events: + out('{.tp_name = "%(name)s", .state=0},', + name = e.name, + ) + + out('};') + +def h(events): + out('#include "trace/simple.h"', + '') + + for num, e in enumerate(events): + if len(e.args): + argstr = e.args.names() + arg_prefix = ', (uint64_t)(uintptr_t)' + cast_args = arg_prefix + arg_prefix.join(argstr) + simple_args = (str(num) + cast_args) + else: + simple_args = str(num) + + out('static inline void trace_%(name)s(%(args)s)', + '{', + ' trace%(argc)d(%(trace_args)s);', + '}', + name = e.name, + args = e.args, + argc = len(e.args), + trace_args = simple_args, + ) + + out('#define NR_TRACE_EVENTS %d' % len(events)) + out('extern TraceEvent trace_list[NR_TRACE_EVENTS];') From fbc54b9412a905c460d0f5e9e0508d64f9e9759b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Vilanova?= Date: Tue, 3 Apr 2012 20:48:06 +0200 Subject: [PATCH 6/9] tracetool: Add support for the 'ust' backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lluís Vilanova Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/ust.py | 90 ++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 scripts/tracetool/backend/ust.py diff --git a/scripts/tracetool/backend/ust.py b/scripts/tracetool/backend/ust.py new file mode 100644 index 0000000000..31a2ff0404 --- /dev/null +++ b/scripts/tracetool/backend/ust.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +LTTng User Space Tracing backend. +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def c(events): + out('#include ', + '#undef mutex_lock', + '#undef mutex_unlock', + '#undef inline', + '#undef wmb', + '#include "trace.h"') + + for e in events: + argnames = ", ".join(e.args.names()) + if len(e.args) > 0: + argnames = ', ' + argnames + + out('DEFINE_TRACE(ust_%(name)s);', + '', + 'static void ust_%(name)s_probe(%(args)s)', + '{', + ' trace_mark(ust, %(name)s, %(fmt)s%(argnames)s);', + '}', + name = e.name, + args = e.args, + fmt = e.fmt, + argnames = argnames, + ) + + else: + out('DEFINE_TRACE(ust_%(name)s);', + '', + 'static void ust_%(name)s_probe(%(args)s)', + '{', + ' trace_mark(ust, %(name)s, UST_MARKER_NOARGS);', + '}', + name = e.name, + args = e.args, + ) + + # register probes + out('', + 'static void __attribute__((constructor)) trace_init(void)', + '{') + + for e in events: + out(' register_trace_ust_%(name)s(ust_%(name)s_probe);', + name = e.name, + ) + + out('}') + + +def h(events): + out('#include ', + '#undef mutex_lock', + '#undef mutex_unlock', + '#undef inline', + '#undef wmb') + + for e in events: + if len(e.args) > 0: + out('DECLARE_TRACE(ust_%(name)s, TP_PROTO(%(args)s), TP_ARGS(%(argnames)s));', + '#define trace_%(name)s trace_ust_%(name)s', + name = e.name, + args = e.args, + argnames = ", ".join(e.args.names()), + ) + + else: + out('_DECLARE_TRACEPOINT_NOARGS(ust_%(name)s);', + '#define trace_%(name)s trace_ust_%(name)s', + name = e.name, + ) + + out() From 52ef093aceddbe43dcc2cb4190e2178036dac60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Vilanova?= Date: Tue, 3 Apr 2012 20:48:12 +0200 Subject: [PATCH 7/9] tracetool: Add support for the 'dtrace' backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lluís Vilanova Signed-off-by: Stefan Hajnoczi --- scripts/tracetool.py | 32 +++++++++- scripts/tracetool/__init__.py | 11 +++- scripts/tracetool/backend/dtrace.py | 97 +++++++++++++++++++++++++++++ scripts/tracetool/format/d.py | 20 ++++++ scripts/tracetool/format/stap.py | 20 ++++++ 5 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 scripts/tracetool/backend/dtrace.py create mode 100644 scripts/tracetool/format/d.py create mode 100644 scripts/tracetool/format/stap.py diff --git a/scripts/tracetool.py b/scripts/tracetool.py index fe2ea344de..cacfd99b62 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -44,6 +44,11 @@ Options: --help This help message. --list-backends Print list of available backends. --check-backend Check if the given backend is valid. + --binary Full path to QEMU binary. + --target-type QEMU emulator target type ('system' or 'user'). + --target-arch QEMU emulator target arch. + --probe-prefix Prefix for dtrace probe names + (default: qemu--).\ """ % { "script" : _SCRIPT, "backends" : backend_descr, @@ -71,6 +76,10 @@ def main(args): check_backend = False arg_backend = "" arg_format = "" + binary = None + target_type = None + target_arch = None + probe_prefix = None for opt, arg in opts: if opt == "--help": error_opt() @@ -87,6 +96,15 @@ def main(args): elif opt == "--check-backend": check_backend = True + elif opt == "--binary": + binary = arg + elif opt == '--target-type': + target_type = arg + elif opt == '--target-arch': + target_arch = arg + elif opt == '--probe-prefix': + probe_prefix = arg + else: error_opt("unhandled option: %s" % opt) @@ -99,8 +117,20 @@ def main(args): else: sys.exit(1) + if arg_format == "stap": + if binary is None: + error_opt("--binary is required for SystemTAP tapset generator") + if probe_prefix is None and target_type is None: + error_opt("--target-type is required for SystemTAP tapset generator") + if probe_prefix is None and target_arch is None: + error_opt("--target-arch is required for SystemTAP tapset generator") + + if probe_prefix is None: + probe_prefix = ".".join([ "qemu", target_type, target_arch ]) + try: - tracetool.generate(sys.stdin, arg_format, arg_backend) + tracetool.generate(sys.stdin, arg_format, arg_backend, + binary = binary, probe_prefix = probe_prefix) except tracetool.TracetoolError as e: error_opt(str(e)) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 1719bb4f92..74fe21b226 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -212,7 +212,8 @@ def try_import(mod_name, attr_name = None, attr_default = None): return False, None -def generate(fevents, format, backend): +def generate(fevents, format, backend, + binary = None, probe_prefix = None): """Generate the output for the given (format, backend) pair. Parameters @@ -223,6 +224,10 @@ def generate(fevents, format, backend): Output format name. backend : str Output backend name. + 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 @@ -245,6 +250,10 @@ def generate(fevents, format, backend): raise TracetoolError("backend '%s' not compatible with format '%s'" % (backend, format)) + import tracetool.backend.dtrace + tracetool.backend.dtrace.BINARY = binary + tracetool.backend.dtrace.PROBEPREFIX = probe_prefix + events = _read_events(fevents) if backend == "nop": diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py new file mode 100644 index 0000000000..cebbd57dd8 --- /dev/null +++ b/scripts/tracetool/backend/dtrace.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +DTrace/SystemTAP backend. +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +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 c(events): + pass + + +def h(events): + out('#include "trace-dtrace.h"', + '') + + for e in events: + out('static inline void trace_%(name)s(%(args)s) {', + ' QEMU_%(uppername)s(%(argnames)s);', + '}', + name = e.name, + args = e.args, + uppername = e.name.upper(), + argnames = ", ".join(e.args.names()), + ) + + +def d(events): + out('provider qemu {') + + for e in events: + args = str(e.args) + + # DTrace provider syntax expects foo() for empty + # params, not foo(void) + if args == 'void': + args = '' + + # Define prototype for probe arguments + out('', + 'probe %(name)s(%(args)s);', + name = e.name, + args = args, + ) + + out('', + '};') + + +def stap(events): + 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(): + # 'limit' is a reserved keyword + if name == 'limit': + name = '_limit' + out(' %s = $arg%d;' % (name.lstrip(), i)) + i += 1 + + out('}') + + out() diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py new file mode 100644 index 0000000000..a2d594773c --- /dev/null +++ b/scripts/tracetool/format/d.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .d file (DTrace only). +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */') diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py new file mode 100644 index 0000000000..50a4c69954 --- /dev/null +++ b/scripts/tracetool/format/stap.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .stp file (DTrace with SystemTAP only). +""" + +__author__ = "Lluís Vilanova " +__copyright__ = "Copyright 2012, Lluís Vilanova " +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@linux.vnet.ibm.com" + + +from tracetool import out + + +def begin(events): + out('/* This file is autogenerated by tracetool, do not edit. */') From f70fd8fdf646714c3f75d3b27a33f42caf856fc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Llu=C3=ADs=20Vilanova?= Date: Tue, 3 Apr 2012 20:48:17 +0200 Subject: [PATCH 8/9] tracetool: Add MAINTAINERS info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the MAINTAINERS file to reflect the new Python tracetool code. [Commit description written by Stefan Hajnoczi] Signed-off-by: Lluís Vilanova Signed-off-by: Stefan Hajnoczi --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 922945c920..cce37e797f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -553,6 +553,8 @@ Tracing M: Stefan Hajnoczi S: Maintained F: trace/ +F: scripts/tracetool.py +F: scripts/tracetool/ F: docs/tracing.txt T: git://github.com/stefanha/qemu.git tracing From 256a721d46a112d8807a488ec0176985c09bbbf1 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 16 Apr 2012 12:47:58 +0100 Subject: [PATCH 9/9] tracetool: handle DTrace keywords 'in', 'next', 'self' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Language keywords cannot be used as argument names. The DTrace backend appends an underscore to the argument name in order to make the argument name legal. This patch adds 'in', 'next', and 'self' keywords to dtrace.py. Also drop the unnecessary argument name lstrip() call. The Arguments.build() method already ensures there is no space around argument names. Furthermore it is misleading to do the lstrip() *after* checking against keywords because the keyword check would not match if spaces were in the name. Signed-off-by: Stefan Hajnoczi Reviewed-by: Alon Levy Reviewed-by: Lluís Vilanova --- scripts/tracetool/backend/dtrace.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/tracetool/backend/dtrace.py b/scripts/tracetool/backend/dtrace.py index cebbd57dd8..9cab75cde8 100644 --- a/scripts/tracetool/backend/dtrace.py +++ b/scripts/tracetool/backend/dtrace.py @@ -86,10 +86,10 @@ def stap(events): i = 1 if len(e.args) > 0: for name in e.args.names(): - # 'limit' is a reserved keyword - if name == 'limit': - name = '_limit' - out(' %s = $arg%d;' % (name.lstrip(), i)) + # Append underscore to reserved keywords + if name in ('limit', 'in', 'next', 'self'): + name += '_' + out(' %s = $arg%d;' % (name, i)) i += 1 out('}')