From: Xinghai Yu <yuxinghai@xxxxxxxxxxxxxx> This 'tracetool' is used for converting the tracepoint format file to all other files we need to supporting dtrace, ftrace or them together. Signed-off-by: Yang Zhiyong <yangzy.fnst@xxxxxxxxxxxxxx> Signed-off-by: Xinghai Yu <yuxinghai@xxxxxxxxxxxxxx> Cc: Stefan Hajnoczi <stefanha@xxxxxxxxxx> --- src/tracetool.py | 103 +++++++++++++++ src/tracetool/__init__.py | 268 ++++++++++++++++++++++++++++++++++++++ src/tracetool/backend/__init__.py | 120 +++++++++++++++++ src/tracetool/backend/trace.py | 112 ++++++++++++++++ src/tracetool/format/__init__.py | 103 +++++++++++++++ src/tracetool/format/c.py | 22 ++++ src/tracetool/format/d.py | 20 +++ src/tracetool/format/h.py | 22 ++++ 8 files changed, 770 insertions(+) create mode 100644 src/tracetool.py create mode 100644 src/tracetool/__init__.py create mode 100644 src/tracetool/backend/__init__.py create mode 100644 src/tracetool/backend/trace.py create mode 100644 src/tracetool/format/__init__.py create mode 100644 src/tracetool/format/c.py create mode 100644 src/tracetool/format/d.py create mode 100644 src/tracetool/format/h.py diff --git a/src/tracetool.py b/src/tracetool.py new file mode 100644 index 0000000..b8cdede --- /dev/null +++ b/src/tracetool.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Command-line wrapper for the tracetool machinery. +""" + +__author__ = "LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@xxxxxxxxxxxxxxxxxx" + + +import sys +import getopt + +from tracetool import error_write, out +import tracetool.backend +import tracetool.format + + +_SCRIPT = "" + +def error_opt(msg = None): + if msg is not None: + error_write("Error: " + msg + "\n") + + backend_descr = "\n".join([ " %-15s %s" % (n, d) + for n,d in tracetool.backend.get_list() ]) + format_descr = "\n".join([ " %-15s %s" % (n, d) + for n,d in tracetool.format.get_list() ]) + error_write("""\ +Usage: %(script)s --format=<format> --backend=<backend> + +Backends: +%(backends)s + +Formats: +%(formats)s + +Options: + --help This help message. + --format Available values are ftrace,dtrace or dtrace_ftrace. + --backend Available values are c,h or d. \ +""" % { + "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" ] + + try: + opts, args = getopt.getopt(args[1:], "", long_opts) + except getopt.GetoptError, 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 == "--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, e: + error_opt(str(e)) + +if __name__ == "__main__": + main(sys.argv) diff --git a/src/tracetool/__init__.py b/src/tracetool/__init__.py new file mode 100644 index 0000000..5e9a951 --- /dev/null +++ b/src/tracetool/__init__.py @@ -0,0 +1,268 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Machinery for generating tracing-related intermediate files. +""" + +__author__ = "LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@xxxxxxxxxxxxxxxxxx" + + +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() + 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) + + res.append((arg_type, identifier)) + 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<props>.*)\s+)?(?P<name>[^(\s]+)\((?P<args>[^)]*)\)\s*(?P<fmt>\".*)?") + + _VALID_PROPS = set(["disable"]) + + def __init__(self, name="", props="", fmt="", args="", isTitle = False): + """ + 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 + self.isTitle = isTitle + + 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) + @staticmethod + def buildTitle(line_str): + return Event(name = line_str.rstrip("\n"), isTitle = True) + + 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('#'): + res.append(Event.buildTitle(line)) + 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, 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(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. + 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) 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) + + tracetool.format.generate_begin(mformat, events) + 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/src/tracetool/backend/__init__.py b/src/tracetool/backend/__init__.py new file mode 100644 index 0000000..e4f5247 --- /dev/null +++ b/src/tracetool/backend/__init__.py @@ -0,0 +1,120 @@ +#!/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 attributes +------------------ + +========= ==================================================================== +Attribute Description +========= ==================================================================== +PUBLIC If exists and is set to 'True', the backend is considered "public". +========= ==================================================================== + + +Backend functions +----------------- + +======== ======================================================================= +Function Description +======== ======================================================================= +<format> 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 <vilanova@xxxxxxxxxx>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@xxxxxxxxxxxxxxxxxx" + + +import os + +import tracetool + + +def get_list(only_public = False): + """Get a list of (name, description) pairs.""" + res = [] + 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 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 + 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("-", "_") + + 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("-", "_") + + func = tracetool.try_import("tracetool.backend." + backend, + format, None)[1] + + func(events) diff --git a/src/tracetool/backend/trace.py b/src/tracetool/backend/trace.py new file mode 100644 index 0000000..d6b4322 --- /dev/null +++ b/src/tracetool/backend/trace.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +ALL_trace built-in backend. +""" + +__author__ = "Eiichi Tsukata <eiichi.tsukata.xh@xxxxxxxxxxx>" +__copyright__ = "Copyright (C) 2013 Hitachi, Ltd." +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@xxxxxxxxxx" + + +from tracetool import out + + +PUBLIC = True + + +def c(events): + out('#include <config.h>', + '#include <stdio.h>', + '', + '#ifdef WITH_QEMU', + '#include "libvirt_probes.h"', + '#include "libvirt_qemu_probes.h"', + '#else', + '#include "libvirt_probes.h"', + '#endif', + '', + '#ifdef WITH_FTRACE_PROBES', + '#include <sys/param.h>', + '#include "ftrace.h"', + '#endif', + '', + '#ifdef WITH_DTRACE_PROBES', + '#include "libvirt_probes_dtrace.h"', + '#include "libvirt_qemu_probes_dtrace.h"', + '#endif', + '') + + for e in events: + if e.isTitle: + continue + argnames_f = ", ".join(e.args.names()) + if len(e.args) > 0: + argnames_f= ", " + argnames_f + + out('void trace_%(name)s(%(args)s) {', + '#ifdef WITH_FTRACE_PROBES', + ' char ftrace_buf[MAX_TRACE_STRLEN];', + ' int unused __attribute__ ((unused));', + ' int trlen;', + ' trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN,', + ' "%(name)s " %(fmt)s "\\n" %(argnames_f)s);', + ' trlen = MIN(trlen, MAX_TRACE_STRLEN - 1);', + ' unused = write(trace_marker_fd, ftrace_buf, trlen);', + '#endif', + '', + '#ifdef WITH_DTRACE_PROBES', + ' LIBVIRT_%(uppername)s(%(argnames_d)s);', + '#endif', + '}', + '', + name = e.name, + args = e.args, + event_id = "TRACE_" + e.name.upper(), + fmt = e.fmt.rstrip("\n"), + argnames_f = argnames_f, + argnames_d = ", ".join(e.args.names()), + uppername = e.name.upper(), + ) +def h(events): + for e in events: + if e.isTitle: + continue + argnames_f = ", ".join(e.args.names()) + if len(e.args) > 0: + argnames_f= ", " + argnames_f + + out('extern void trace_%(name)s(%(args)s);', + '', + name = e.name, + args = e.args, + ) +def d(events): + out('provider libvirt {') + + for e in events: + if e.isTitle: + out(' %(title)s', + title = e.name + ) + continue + 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('', + '};') + diff --git a/src/tracetool/format/__init__.py b/src/tracetool/format/__init__.py new file mode 100644 index 0000000..3c2a0d8 --- /dev/null +++ b/src/tracetool/format/__init__.py @@ -0,0 +1,103 @@ +#!/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 <vilanova@xxxxxxxxxx>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@xxxxxxxxxxxxxxxxxx" + + +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 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 _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) diff --git a/src/tracetool/format/c.py b/src/tracetool/format/c.py new file mode 100644 index 0000000..57032db --- /dev/null +++ b/src/tracetool/format/c.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .c file. +""" + +__author__ = "LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@xxxxxxxxxxxxxxxxxx" + + +from tracetool import out + + +def begin(events): + pass +def end(events): + pass diff --git a/src/tracetool/format/d.py b/src/tracetool/format/d.py new file mode 100644 index 0000000..c762961 --- /dev/null +++ b/src/tracetool/format/d.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .d file (DTrace only). +""" + +__author__ = "LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@xxxxxxxxxxxxxxxxxx" + + +from tracetool import out + + +def begin(events): + pass diff --git a/src/tracetool/format/h.py b/src/tracetool/format/h.py new file mode 100644 index 0000000..8603a33 --- /dev/null +++ b/src/tracetool/format/h.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .h file. +""" + +__author__ = "LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__copyright__ = "Copyright 2012, LluÃs Vilanova <vilanova@xxxxxxxxxx>" +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Stefan Hajnoczi" +__email__ = "stefanha@xxxxxxxxxxxxxxxxxx" + + +from tracetool import out + + +def begin(events): + pass +def end(events): + pass -- 1.8.3.1 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list