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