qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

__init__.py (14172B)


      1 # -*- coding: utf-8 -*-
      2 
      3 """
      4 Machinery for generating tracing-related intermediate files.
      5 """
      6 
      7 __author__     = "Lluís Vilanova <vilanova@ac.upc.edu>"
      8 __copyright__  = "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
      9 __license__    = "GPL version 2 or (at your option) any later version"
     10 
     11 __maintainer__ = "Stefan Hajnoczi"
     12 __email__      = "stefanha@redhat.com"
     13 
     14 
     15 import re
     16 import sys
     17 import weakref
     18 
     19 import tracetool.format
     20 import tracetool.backend
     21 import tracetool.transform
     22 
     23 
     24 def error_write(*lines):
     25     """Write a set of error lines."""
     26     sys.stderr.writelines("\n".join(lines) + "\n")
     27 
     28 def error(*lines):
     29     """Write a set of error lines and exit."""
     30     error_write(*lines)
     31     sys.exit(1)
     32 
     33 
     34 out_lineno = 1
     35 out_filename = '<none>'
     36 out_fobj = sys.stdout
     37 
     38 def out_open(filename):
     39     global out_filename, out_fobj
     40     out_filename = filename
     41     out_fobj = open(filename, 'wt')
     42 
     43 def out(*lines, **kwargs):
     44     """Write a set of output lines.
     45 
     46     You can use kwargs as a shorthand for mapping variables when formatting all
     47     the strings in lines.
     48 
     49     The 'out_lineno' kwarg is automatically added to reflect the current output
     50     file line number. The 'out_next_lineno' kwarg is also automatically added
     51     with the next output line number. The 'out_filename' kwarg is automatically
     52     added with the output filename.
     53     """
     54     global out_lineno
     55     output = []
     56     for l in lines:
     57         kwargs['out_lineno'] = out_lineno
     58         kwargs['out_next_lineno'] = out_lineno + 1
     59         kwargs['out_filename'] = out_filename
     60         output.append(l % kwargs)
     61         out_lineno += 1
     62 
     63     out_fobj.writelines("\n".join(output) + "\n")
     64 
     65 # We only want to allow standard C types or fixed sized
     66 # integer types. We don't want QEMU specific types
     67 # as we can't assume trace backends can resolve all the
     68 # typedefs
     69 ALLOWED_TYPES = [
     70     "int",
     71     "long",
     72     "short",
     73     "char",
     74     "bool",
     75     "unsigned",
     76     "signed",
     77     "int8_t",
     78     "uint8_t",
     79     "int16_t",
     80     "uint16_t",
     81     "int32_t",
     82     "uint32_t",
     83     "int64_t",
     84     "uint64_t",
     85     "void",
     86     "size_t",
     87     "ssize_t",
     88     "uintptr_t",
     89     "ptrdiff_t",
     90 ]
     91 
     92 def validate_type(name):
     93     bits = name.split(" ")
     94     for bit in bits:
     95         bit = re.sub("\*", "", bit)
     96         if bit == "":
     97             continue
     98         if bit == "const":
     99             continue
    100         if bit not in ALLOWED_TYPES:
    101             raise ValueError("Argument type '%s' is not allowed. "
    102                              "Only standard C types and fixed size integer "
    103                              "types should be used. struct, union, and "
    104                              "other complex pointer types should be "
    105                              "declared as 'void *'" % name)
    106 
    107 class Arguments:
    108     """Event arguments description."""
    109 
    110     def __init__(self, args):
    111         """
    112         Parameters
    113         ----------
    114         args :
    115             List of (type, name) tuples or Arguments objects.
    116         """
    117         self._args = []
    118         for arg in args:
    119             if isinstance(arg, Arguments):
    120                 self._args.extend(arg._args)
    121             else:
    122                 self._args.append(arg)
    123 
    124     def copy(self):
    125         """Create a new copy."""
    126         return Arguments(list(self._args))
    127 
    128     @staticmethod
    129     def build(arg_str):
    130         """Build and Arguments instance from an argument string.
    131 
    132         Parameters
    133         ----------
    134         arg_str : str
    135             String describing the event arguments.
    136         """
    137         res = []
    138         for arg in arg_str.split(","):
    139             arg = arg.strip()
    140             if not arg:
    141                 raise ValueError("Empty argument (did you forget to use 'void'?)")
    142             if arg == 'void':
    143                 continue
    144 
    145             if '*' in arg:
    146                 arg_type, identifier = arg.rsplit('*', 1)
    147                 arg_type += '*'
    148                 identifier = identifier.strip()
    149             else:
    150                 arg_type, identifier = arg.rsplit(None, 1)
    151 
    152             validate_type(arg_type)
    153             res.append((arg_type, identifier))
    154         return Arguments(res)
    155 
    156     def __getitem__(self, index):
    157         if isinstance(index, slice):
    158             return Arguments(self._args[index])
    159         else:
    160             return self._args[index]
    161 
    162     def __iter__(self):
    163         """Iterate over the (type, name) pairs."""
    164         return iter(self._args)
    165 
    166     def __len__(self):
    167         """Number of arguments."""
    168         return len(self._args)
    169 
    170     def __str__(self):
    171         """String suitable for declaring function arguments."""
    172         if len(self._args) == 0:
    173             return "void"
    174         else:
    175             return ", ".join([ " ".join([t, n]) for t,n in self._args ])
    176 
    177     def __repr__(self):
    178         """Evaluable string representation for this object."""
    179         return "Arguments(\"%s\")" % str(self)
    180 
    181     def names(self):
    182         """List of argument names."""
    183         return [ name for _, name in self._args ]
    184 
    185     def types(self):
    186         """List of argument types."""
    187         return [ type_ for type_, _ in self._args ]
    188 
    189     def casted(self):
    190         """List of argument names casted to their type."""
    191         return ["(%s)%s" % (type_, name) for type_, name in self._args]
    192 
    193     def transform(self, *trans):
    194         """Return a new Arguments instance with transformed types.
    195 
    196         The types in the resulting Arguments instance are transformed according
    197         to tracetool.transform.transform_type.
    198         """
    199         res = []
    200         for type_, name in self._args:
    201             res.append((tracetool.transform.transform_type(type_, *trans),
    202                         name))
    203         return Arguments(res)
    204 
    205 
    206 class Event(object):
    207     """Event description.
    208 
    209     Attributes
    210     ----------
    211     name : str
    212         The event name.
    213     fmt : str
    214         The event format string.
    215     properties : set(str)
    216         Properties of the event.
    217     args : Arguments
    218         The event arguments.
    219     lineno : int
    220         The line number in the input file.
    221     filename : str
    222         The path to the input file.
    223 
    224     """
    225 
    226     _CRE = re.compile("((?P<props>[\w\s]+)\s+)?"
    227                       "(?P<name>\w+)"
    228                       "\((?P<args>[^)]*)\)"
    229                       "\s*"
    230                       "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
    231                       "\s*")
    232 
    233     _VALID_PROPS = set(["disable", "vcpu"])
    234 
    235     def __init__(self, name, props, fmt, args, lineno, filename, orig=None,
    236                  event_trans=None, event_exec=None):
    237         """
    238         Parameters
    239         ----------
    240         name : string
    241             Event name.
    242         props : list of str
    243             Property names.
    244         fmt : str, list of str
    245             Event printing format string(s).
    246         args : Arguments
    247             Event arguments.
    248         lineno : int
    249             The line number in the input file.
    250         filename : str
    251             The path to the input file.
    252         orig : Event or None
    253             Original Event before transformation/generation.
    254         event_trans : Event or None
    255             Generated translation-time event ("tcg" property).
    256         event_exec : Event or None
    257             Generated execution-time event ("tcg" property).
    258 
    259         """
    260         self.name = name
    261         self.properties = props
    262         self.fmt = fmt
    263         self.args = args
    264         self.lineno = int(lineno)
    265         self.filename = str(filename)
    266         self.event_trans = event_trans
    267         self.event_exec = event_exec
    268 
    269         if len(args) > 10:
    270             raise ValueError("Event '%s' has more than maximum permitted "
    271                              "argument count" % name)
    272 
    273         if orig is None:
    274             self.original = weakref.ref(self)
    275         else:
    276             self.original = orig
    277 
    278         unknown_props = set(self.properties) - self._VALID_PROPS
    279         if len(unknown_props) > 0:
    280             raise ValueError("Unknown properties: %s"
    281                              % ", ".join(unknown_props))
    282         assert isinstance(self.fmt, str) or len(self.fmt) == 2
    283 
    284     def copy(self):
    285         """Create a new copy."""
    286         return Event(self.name, list(self.properties), self.fmt,
    287                      self.args.copy(), self.lineno, self.filename,
    288                      self, self.event_trans, self.event_exec)
    289 
    290     @staticmethod
    291     def build(line_str, lineno, filename):
    292         """Build an Event instance from a string.
    293 
    294         Parameters
    295         ----------
    296         line_str : str
    297             Line describing the event.
    298         lineno : int
    299             Line number in input file.
    300         filename : str
    301             Path to input file.
    302         """
    303         m = Event._CRE.match(line_str)
    304         assert m is not None
    305         groups = m.groupdict('')
    306 
    307         name = groups["name"]
    308         props = groups["props"].split()
    309         fmt = groups["fmt"]
    310         fmt_trans = groups["fmt_trans"]
    311         if fmt.find("%m") != -1 or fmt_trans.find("%m") != -1:
    312             raise ValueError("Event format '%m' is forbidden, pass the error "
    313                              "as an explicit trace argument")
    314         if fmt.endswith(r'\n"'):
    315             raise ValueError("Event format must not end with a newline "
    316                              "character")
    317 
    318         if len(fmt_trans) > 0:
    319             fmt = [fmt_trans, fmt]
    320         args = Arguments.build(groups["args"])
    321 
    322         event = Event(name, props, fmt, args, lineno, filename)
    323 
    324         # add implicit arguments when using the 'vcpu' property
    325         import tracetool.vcpu
    326         event = tracetool.vcpu.transform_event(event)
    327 
    328         return event
    329 
    330     def __repr__(self):
    331         """Evaluable string representation for this object."""
    332         if isinstance(self.fmt, str):
    333             fmt = self.fmt
    334         else:
    335             fmt = "%s, %s" % (self.fmt[0], self.fmt[1])
    336         return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
    337                                           self.name,
    338                                           self.args,
    339                                           fmt)
    340     # Star matching on PRI is dangerous as one might have multiple
    341     # arguments with that format, hence the non-greedy version of it.
    342     _FMT = re.compile("(%[\d\.]*\w+|%.*?PRI\S+)")
    343 
    344     def formats(self):
    345         """List conversion specifiers in the argument print format string."""
    346         assert not isinstance(self.fmt, list)
    347         return self._FMT.findall(self.fmt)
    348 
    349     QEMU_TRACE               = "trace_%(name)s"
    350     QEMU_TRACE_NOCHECK       = "_nocheck__" + QEMU_TRACE
    351     QEMU_TRACE_TCG           = QEMU_TRACE + "_tcg"
    352     QEMU_DSTATE              = "_TRACE_%(NAME)s_DSTATE"
    353     QEMU_BACKEND_DSTATE      = "TRACE_%(NAME)s_BACKEND_DSTATE"
    354     QEMU_EVENT               = "_TRACE_%(NAME)s_EVENT"
    355 
    356     def api(self, fmt=None):
    357         if fmt is None:
    358             fmt = Event.QEMU_TRACE
    359         return fmt % {"name": self.name, "NAME": self.name.upper()}
    360 
    361     def transform(self, *trans):
    362         """Return a new Event with transformed Arguments."""
    363         return Event(self.name,
    364                      list(self.properties),
    365                      self.fmt,
    366                      self.args.transform(*trans),
    367                      self.lineno,
    368                      self.filename,
    369                      self)
    370 
    371 
    372 def read_events(fobj, fname):
    373     """Generate the output for the given (format, backends) pair.
    374 
    375     Parameters
    376     ----------
    377     fobj : file
    378         Event description file.
    379     fname : str
    380         Name of event file
    381 
    382     Returns a list of Event objects
    383     """
    384 
    385     events = []
    386     for lineno, line in enumerate(fobj, 1):
    387         if line[-1] != '\n':
    388             raise ValueError("%s does not end with a new line" % fname)
    389         if not line.strip():
    390             continue
    391         if line.lstrip().startswith('#'):
    392             continue
    393 
    394         try:
    395             event = Event.build(line, lineno, fname)
    396         except ValueError as e:
    397             arg0 = 'Error at %s:%d: %s' % (fname, lineno, e.args[0])
    398             e.args = (arg0,) + e.args[1:]
    399             raise
    400 
    401         events.append(event)
    402 
    403     return events
    404 
    405 
    406 class TracetoolError (Exception):
    407     """Exception for calls to generate."""
    408     pass
    409 
    410 
    411 def try_import(mod_name, attr_name=None, attr_default=None):
    412     """Try to import a module and get an attribute from it.
    413 
    414     Parameters
    415     ----------
    416     mod_name : str
    417         Module name.
    418     attr_name : str, optional
    419         Name of an attribute in the module.
    420     attr_default : optional
    421         Default value if the attribute does not exist in the module.
    422 
    423     Returns
    424     -------
    425     A pair indicating whether the module could be imported and the module or
    426     object or attribute value.
    427     """
    428     try:
    429         module = __import__(mod_name, globals(), locals(), ["__package__"])
    430         if attr_name is None:
    431             return True, module
    432         return True, getattr(module, str(attr_name), attr_default)
    433     except ImportError:
    434         return False, None
    435 
    436 
    437 def generate(events, group, format, backends,
    438              binary=None, probe_prefix=None):
    439     """Generate the output for the given (format, backends) pair.
    440 
    441     Parameters
    442     ----------
    443     events : list
    444         list of Event objects to generate for
    445     group: str
    446         Name of the tracing group
    447     format : str
    448         Output format name.
    449     backends : list
    450         Output backend names.
    451     binary : str or None
    452         See tracetool.backend.dtrace.BINARY.
    453     probe_prefix : str or None
    454         See tracetool.backend.dtrace.PROBEPREFIX.
    455     """
    456     # fix strange python error (UnboundLocalError tracetool)
    457     import tracetool
    458 
    459     format = str(format)
    460     if len(format) == 0:
    461         raise TracetoolError("format not set")
    462     if not tracetool.format.exists(format):
    463         raise TracetoolError("unknown format: %s" % format)
    464 
    465     if len(backends) == 0:
    466         raise TracetoolError("no backends specified")
    467     for backend in backends:
    468         if not tracetool.backend.exists(backend):
    469             raise TracetoolError("unknown backend: %s" % backend)
    470     backend = tracetool.backend.Wrapper(backends, format)
    471 
    472     import tracetool.backend.dtrace
    473     tracetool.backend.dtrace.BINARY = binary
    474     tracetool.backend.dtrace.PROBEPREFIX = probe_prefix
    475 
    476     tracetool.format.generate(events, format, backend, group)