On Mon, Mar 26, 2012 at 07:37:50PM +0200, Lluís Vilanova wrote: An additional comment I forgot to add.
> Signed-off-by: Lluís Vilanova <vilan...@ac.upc.edu> > --- [snip] > + 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 ]) > + Nice to have addition (for debugging mainly): + def __repr__(self): + """String suitable to recreate this instance.""" + return '%s("%s")' % (self.__class__.__name__, 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. > + > + Parameters > + ---------- > + line : str > + Line describing the event. > + > + 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, line): > + m = self._CRE.match(line) > + assert m is not None > + groups = m.groupdict('') > + self.name = groups["name"] > + self.fmt = groups["fmt"] > + self.properties = groups["props"].split() > + self.args = Arguments(groups["args"]) > + > + unknown_props = set(self.properties) - self._VALID_PROPS > + if len(unknown_props) > 0: > + raise ValueError("Unknown properties: %s" % ", > ".join(unknown_props)) > + > + > +def _read_events(fobj): > + res = [] > + for line in fobj: > + if not line.strip(): > + continue > + if line.lstrip().startswith('#'): > + continue > + res.append(Event(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. > + """ > + mod_name = mod_name.replace("-", "_") > + 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, **options): > + """Generate the output for the given (format, backend) pair.""" > + # fix strange python error (UnboundLocalError tracetool) > + import tracetool > + > + if len(options) > 0: > + raise ValueError("unknown options: " + ", ".join(options)) > + > + 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 0000000..23cad9f > --- /dev/null > +++ b/scripts/tracetool/backend/__init__.py > @@ -0,0 +1,114 @@ > +#!/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 <vilan...@ac.upc.edu>" > +__copyright__ = "Copyright 2012, Lluís Vilanova <vilan...@ac.upc.edu>" > +__license__ = "GPL version 2 or (at your option) any later version" > + > +__maintainer__ = "Stefan Hajnoczi" > +__email__ = "stefa...@linux.vnet.ibm.com" > + > + > +import pkgutil > + > +import tracetool > + > + > +def get_list(only_public = False): > + """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)[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("-", "_") > + if name == "nop": > + return True > + 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) > + > + 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)) > + > + 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 0000000..5b37c00 > --- /dev/null > +++ b/scripts/tracetool/format/__init__.py > @@ -0,0 +1,91 @@ > +#!/usr/bin/env python > +# -*- coding: utf-8 -*- > + > +""" > +Format management. > + > + > +Creating new formats > +-------------------- > + > +A new format named 'foo-bar' corresponds to Python module > +'tracetool/frontend/foo_bar.py'. > + > +A frontend 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 <vilan...@ac.upc.edu>" > +__copyright__ = "Copyright 2012, Lluís Vilanova <vilan...@ac.upc.edu>" > +__license__ = "GPL version 2 or (at your option) any later version" > + > +__maintainer__ = "Stefan Hajnoczi" > +__email__ = "stefa...@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)[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 > + 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) > + > + 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) > + > + func = tracetool.try_import("tracetool.format." + name, > + "end", _empty)[1] > + func(events) > >