[libvirt] [PATCH 04/13] src: Add scripts 'tracetool' to supporting the translation from tracepoint files to multiple trace backend supporting files

yangzy.fnst at cn.fujitsu.com yangzy.fnst at cn.fujitsu.com
Mon Mar 10 08:17:21 UTC 2014


From: Xinghai Yu <yuxinghai at cn.fujitsu.com>

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 at cn.fujitsu.com>
Signed-off-by: Xinghai Yu <yuxinghai at cn.fujitsu.com>
Cc: Stefan Hajnoczi <stefanha at redhat.com>
---
 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 at ac.upc.edu>"
+__copyright__  = "Copyright 2012, Lluís Vilanova <vilanova at ac.upc.edu>"
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha at linux.vnet.ibm.com"
+
+
+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 at ac.upc.edu>"
+__copyright__  = "Copyright 2012, Lluís Vilanova <vilanova at ac.upc.edu>"
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha at 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()
+            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 at ac.upc.edu>"
+__copyright__  = "Copyright 2012, Lluís Vilanova <vilanova at ac.upc.edu>"
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha at linux.vnet.ibm.com"
+
+
+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 at hitachi.com>"
+__copyright__  = "Copyright (C) 2013 Hitachi, Ltd."
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha at redhat.com"
+
+
+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 at ac.upc.edu>"
+__copyright__  = "Copyright 2012, Lluís Vilanova <vilanova at ac.upc.edu>"
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha at linux.vnet.ibm.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 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 at ac.upc.edu>"
+__copyright__  = "Copyright 2012, Lluís Vilanova <vilanova at ac.upc.edu>"
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha at linux.vnet.ibm.com"
+
+
+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 at ac.upc.edu>"
+__copyright__  = "Copyright 2012, Lluís Vilanova <vilanova at ac.upc.edu>"
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha at linux.vnet.ibm.com"
+
+
+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 at ac.upc.edu>"
+__copyright__  = "Copyright 2012, Lluís Vilanova <vilanova at ac.upc.edu>"
+__license__    = "GPL version 2 or (at your option) any later version"
+
+__maintainer__ = "Stefan Hajnoczi"
+__email__      = "stefanha at linux.vnet.ibm.com"
+
+
+from tracetool import out
+
+
+def begin(events):
+    pass
+def end(events):
+    pass
-- 
1.8.3.1




More information about the libvir-list mailing list