qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [RFC PATCH v2 1/8] tracetool: Rewrite infrastructure as


From: Alon Levy
Subject: Re: [Qemu-devel] [RFC PATCH v2 1/8] tracetool: Rewrite infrastructure as python modules
Date: Tue, 27 Mar 2012 17:21:53 +0200
User-agent: Mutt/1.5.21 (2010-09-15)

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 <address@hidden>
> ---
[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 <address@hidden>"
> +__copyright__  = "Copyright 2012, Lluís Vilanova <address@hidden>"
> +__license__    = "GPL version 2 or (at your option) any later version"
> +
> +__maintainer__ = "Stefan Hajnoczi"
> +__email__      = "address@hidden"
> +
> +
> +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 <address@hidden>"
> +__copyright__  = "Copyright 2012, Lluís Vilanova <address@hidden>"
> +__license__    = "GPL version 2 or (at your option) any later version"
> +
> +__maintainer__ = "Stefan Hajnoczi"
> +__email__      = "address@hidden"
> +
> +
> +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)
> 
> 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]