Add qemu 2.4.0
[kvmfornfv.git] / qemu / scripts / tracetool / __init__.py
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
3
4 """
5 Machinery for generating tracing-related intermediate files.
6 """
7
8 __author__     = "Lluís Vilanova <vilanova@ac.upc.edu>"
9 __copyright__  = "Copyright 2012-2014, Lluís Vilanova <vilanova@ac.upc.edu>"
10 __license__    = "GPL version 2 or (at your option) any later version"
11
12 __maintainer__ = "Stefan Hajnoczi"
13 __email__      = "stefanha@linux.vnet.ibm.com"
14
15
16 import re
17 import sys
18 import weakref
19
20 import tracetool.format
21 import tracetool.backend
22 import tracetool.transform
23
24
25 def error_write(*lines):
26     """Write a set of error lines."""
27     sys.stderr.writelines("\n".join(lines) + "\n")
28
29 def error(*lines):
30     """Write a set of error lines and exit."""
31     error_write(*lines)
32     sys.exit(1)
33
34
35 def out(*lines, **kwargs):
36     """Write a set of output lines.
37
38     You can use kwargs as a shorthand for mapping variables when formating all
39     the strings in lines.
40     """
41     lines = [ l % kwargs for l in lines ]
42     sys.stdout.writelines("\n".join(lines) + "\n")
43
44
45 class Arguments:
46     """Event arguments description."""
47
48     def __init__(self, args):
49         """
50         Parameters
51         ----------
52         args :
53             List of (type, name) tuples.
54         """
55         self._args = args
56
57     def copy(self):
58         """Create a new copy."""
59         return Arguments(list(self._args))
60
61     @staticmethod
62     def build(arg_str):
63         """Build and Arguments instance from an argument string.
64
65         Parameters
66         ----------
67         arg_str : str
68             String describing the event arguments.
69         """
70         res = []
71         for arg in arg_str.split(","):
72             arg = arg.strip()
73             if arg == 'void':
74                 continue
75
76             if '*' in arg:
77                 arg_type, identifier = arg.rsplit('*', 1)
78                 arg_type += '*'
79                 identifier = identifier.strip()
80             else:
81                 arg_type, identifier = arg.rsplit(None, 1)
82
83             res.append((arg_type, identifier))
84         return Arguments(res)
85
86     def __iter__(self):
87         """Iterate over the (type, name) pairs."""
88         return iter(self._args)
89
90     def __len__(self):
91         """Number of arguments."""
92         return len(self._args)
93
94     def __str__(self):
95         """String suitable for declaring function arguments."""
96         if len(self._args) == 0:
97             return "void"
98         else:
99             return ", ".join([ " ".join([t, n]) for t,n in self._args ])
100
101     def __repr__(self):
102         """Evaluable string representation for this object."""
103         return "Arguments(\"%s\")" % str(self)
104
105     def names(self):
106         """List of argument names."""
107         return [ name for _, name in self._args ]
108
109     def types(self):
110         """List of argument types."""
111         return [ type_ for type_, _ in self._args ]
112
113     def transform(self, *trans):
114         """Return a new Arguments instance with transformed types.
115
116         The types in the resulting Arguments instance are transformed according
117         to tracetool.transform.transform_type.
118         """
119         res = []
120         for type_, name in self._args:
121             res.append((tracetool.transform.transform_type(type_, *trans),
122                         name))
123         return Arguments(res)
124
125
126 class Event(object):
127     """Event description.
128
129     Attributes
130     ----------
131     name : str
132         The event name.
133     fmt : str
134         The event format string.
135     properties : set(str)
136         Properties of the event.
137     args : Arguments
138         The event arguments.
139
140     """
141
142     _CRE = re.compile("((?P<props>[\w\s]+)\s+)?"
143                       "(?P<name>\w+)"
144                       "\((?P<args>[^)]*)\)"
145                       "\s*"
146                       "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
147                       "\s*")
148
149     _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec"])
150
151     def __init__(self, name, props, fmt, args, orig=None):
152         """
153         Parameters
154         ----------
155         name : string
156             Event name.
157         props : list of str
158             Property names.
159         fmt : str, list of str
160             Event printing format (or formats).
161         args : Arguments
162             Event arguments.
163         orig : Event or None
164             Original Event before transformation.
165
166         """
167         self.name = name
168         self.properties = props
169         self.fmt = fmt
170         self.args = args
171
172         if orig is None:
173             self.original = weakref.ref(self)
174         else:
175             self.original = orig
176
177         unknown_props = set(self.properties) - self._VALID_PROPS
178         if len(unknown_props) > 0:
179             raise ValueError("Unknown properties: %s"
180                              % ", ".join(unknown_props))
181         assert isinstance(self.fmt, str) or len(self.fmt) == 2
182
183     def copy(self):
184         """Create a new copy."""
185         return Event(self.name, list(self.properties), self.fmt,
186                      self.args.copy(), self)
187
188     @staticmethod
189     def build(line_str):
190         """Build an Event instance from a string.
191
192         Parameters
193         ----------
194         line_str : str
195             Line describing the event.
196         """
197         m = Event._CRE.match(line_str)
198         assert m is not None
199         groups = m.groupdict('')
200
201         name = groups["name"]
202         props = groups["props"].split()
203         fmt = groups["fmt"]
204         fmt_trans = groups["fmt_trans"]
205         if len(fmt_trans) > 0:
206             fmt = [fmt_trans, fmt]
207         args = Arguments.build(groups["args"])
208
209         if "tcg-trans" in props:
210             raise ValueError("Invalid property 'tcg-trans'")
211         if "tcg-exec" in props:
212             raise ValueError("Invalid property 'tcg-exec'")
213         if "tcg" not in props and not isinstance(fmt, str):
214             raise ValueError("Only events with 'tcg' property can have two formats")
215         if "tcg" in props and isinstance(fmt, str):
216             raise ValueError("Events with 'tcg' property must have two formats")
217
218         return Event(name, props, fmt, args)
219
220     def __repr__(self):
221         """Evaluable string representation for this object."""
222         if isinstance(self.fmt, str):
223             fmt = self.fmt
224         else:
225             fmt = "%s, %s" % (self.fmt[0], self.fmt[1])
226         return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
227                                           self.name,
228                                           self.args,
229                                           fmt)
230
231     _FMT = re.compile("(%[\d\.]*\w+|%.*PRI\S+)")
232
233     def formats(self):
234         """List of argument print formats."""
235         assert not isinstance(self.fmt, list)
236         return self._FMT.findall(self.fmt)
237
238     QEMU_TRACE               = "trace_%(name)s"
239     QEMU_TRACE_TCG           = QEMU_TRACE + "_tcg"
240
241     def api(self, fmt=None):
242         if fmt is None:
243             fmt = Event.QEMU_TRACE
244         return fmt % {"name": self.name}
245
246     def transform(self, *trans):
247         """Return a new Event with transformed Arguments."""
248         return Event(self.name,
249                      list(self.properties),
250                      self.fmt,
251                      self.args.transform(*trans),
252                      self)
253
254
255 def _read_events(fobj):
256     events = []
257     for line in fobj:
258         if not line.strip():
259             continue
260         if line.lstrip().startswith('#'):
261             continue
262
263         event = Event.build(line)
264
265         # transform TCG-enabled events
266         if "tcg" not in event.properties:
267             events.append(event)
268         else:
269             event_trans = event.copy()
270             event_trans.name += "_trans"
271             event_trans.properties += ["tcg-trans"]
272             event_trans.fmt = event.fmt[0]
273             args_trans = []
274             for atrans, aorig in zip(
275                     event_trans.transform(tracetool.transform.TCG_2_HOST).args,
276                     event.args):
277                 if atrans == aorig:
278                     args_trans.append(atrans)
279             event_trans.args = Arguments(args_trans)
280             event_trans = event_trans.copy()
281
282             event_exec = event.copy()
283             event_exec.name += "_exec"
284             event_exec.properties += ["tcg-exec"]
285             event_exec.fmt = event.fmt[1]
286             event_exec = event_exec.transform(tracetool.transform.TCG_2_HOST)
287
288             new_event = [event_trans, event_exec]
289             event.event_trans, event.event_exec = new_event
290
291             events.extend(new_event)
292
293     return events
294
295
296 class TracetoolError (Exception):
297     """Exception for calls to generate."""
298     pass
299
300
301 def try_import(mod_name, attr_name=None, attr_default=None):
302     """Try to import a module and get an attribute from it.
303
304     Parameters
305     ----------
306     mod_name : str
307         Module name.
308     attr_name : str, optional
309         Name of an attribute in the module.
310     attr_default : optional
311         Default value if the attribute does not exist in the module.
312
313     Returns
314     -------
315     A pair indicating whether the module could be imported and the module or
316     object or attribute value.
317     """
318     try:
319         module = __import__(mod_name, globals(), locals(), ["__package__"])
320         if attr_name is None:
321             return True, module
322         return True, getattr(module, str(attr_name), attr_default)
323     except ImportError:
324         return False, None
325
326
327 def generate(fevents, format, backends,
328              binary=None, probe_prefix=None):
329     """Generate the output for the given (format, backends) pair.
330
331     Parameters
332     ----------
333     fevents : file
334         Event description file.
335     format : str
336         Output format name.
337     backends : list
338         Output backend names.
339     binary : str or None
340         See tracetool.backend.dtrace.BINARY.
341     probe_prefix : str or None
342         See tracetool.backend.dtrace.PROBEPREFIX.
343     """
344     # fix strange python error (UnboundLocalError tracetool)
345     import tracetool
346
347     format = str(format)
348     if len(format) is 0:
349         raise TracetoolError("format not set")
350     if not tracetool.format.exists(format):
351         raise TracetoolError("unknown format: %s" % format)
352
353     if len(backends) is 0:
354         raise TracetoolError("no backends specified")
355     for backend in backends:
356         if not tracetool.backend.exists(backend):
357             raise TracetoolError("unknown backend: %s" % backend)
358     backend = tracetool.backend.Wrapper(backends, format)
359
360     import tracetool.backend.dtrace
361     tracetool.backend.dtrace.BINARY = binary
362     tracetool.backend.dtrace.PROBEPREFIX = probe_prefix
363
364     events = _read_events(fevents)
365
366     tracetool.format.generate(events, format, backend)