These changes are the raw update to qemu-2.6.
[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-2016, 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 or Arguments objects.
54         """
55         self._args = []
56         for arg in args:
57             if isinstance(arg, Arguments):
58                 self._args.extend(arg._args)
59             else:
60                 self._args.append(arg)
61
62     def copy(self):
63         """Create a new copy."""
64         return Arguments(list(self._args))
65
66     @staticmethod
67     def build(arg_str):
68         """Build and Arguments instance from an argument string.
69
70         Parameters
71         ----------
72         arg_str : str
73             String describing the event arguments.
74         """
75         res = []
76         for arg in arg_str.split(","):
77             arg = arg.strip()
78             if arg == 'void':
79                 continue
80
81             if '*' in arg:
82                 arg_type, identifier = arg.rsplit('*', 1)
83                 arg_type += '*'
84                 identifier = identifier.strip()
85             else:
86                 arg_type, identifier = arg.rsplit(None, 1)
87
88             res.append((arg_type, identifier))
89         return Arguments(res)
90
91     def __getitem__(self, index):
92         if isinstance(index, slice):
93             return Arguments(self._args[index])
94         else:
95             return self._args[index]
96
97     def __iter__(self):
98         """Iterate over the (type, name) pairs."""
99         return iter(self._args)
100
101     def __len__(self):
102         """Number of arguments."""
103         return len(self._args)
104
105     def __str__(self):
106         """String suitable for declaring function arguments."""
107         if len(self._args) == 0:
108             return "void"
109         else:
110             return ", ".join([ " ".join([t, n]) for t,n in self._args ])
111
112     def __repr__(self):
113         """Evaluable string representation for this object."""
114         return "Arguments(\"%s\")" % str(self)
115
116     def names(self):
117         """List of argument names."""
118         return [ name for _, name in self._args ]
119
120     def types(self):
121         """List of argument types."""
122         return [ type_ for type_, _ in self._args ]
123
124     def casted(self):
125         """List of argument names casted to their type."""
126         return ["(%s)%s" % (type_, name) for type_, name in self._args]
127
128     def transform(self, *trans):
129         """Return a new Arguments instance with transformed types.
130
131         The types in the resulting Arguments instance are transformed according
132         to tracetool.transform.transform_type.
133         """
134         res = []
135         for type_, name in self._args:
136             res.append((tracetool.transform.transform_type(type_, *trans),
137                         name))
138         return Arguments(res)
139
140
141 class Event(object):
142     """Event description.
143
144     Attributes
145     ----------
146     name : str
147         The event name.
148     fmt : str
149         The event format string.
150     properties : set(str)
151         Properties of the event.
152     args : Arguments
153         The event arguments.
154
155     """
156
157     _CRE = re.compile("((?P<props>[\w\s]+)\s+)?"
158                       "(?P<name>\w+)"
159                       "\((?P<args>[^)]*)\)"
160                       "\s*"
161                       "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
162                       "\s*")
163
164     _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"])
165
166     def __init__(self, name, props, fmt, args, orig=None,
167                  event_trans=None, event_exec=None):
168         """
169         Parameters
170         ----------
171         name : string
172             Event name.
173         props : list of str
174             Property names.
175         fmt : str, list of str
176             Event printing format (or formats).
177         args : Arguments
178             Event arguments.
179         orig : Event or None
180             Original Event before transformation/generation.
181         event_trans : Event or None
182             Generated translation-time event ("tcg" property).
183         event_exec : Event or None
184             Generated execution-time event ("tcg" property).
185
186         """
187         self.name = name
188         self.properties = props
189         self.fmt = fmt
190         self.args = args
191         self.event_trans = event_trans
192         self.event_exec = event_exec
193
194         if orig is None:
195             self.original = weakref.ref(self)
196         else:
197             self.original = orig
198
199         unknown_props = set(self.properties) - self._VALID_PROPS
200         if len(unknown_props) > 0:
201             raise ValueError("Unknown properties: %s"
202                              % ", ".join(unknown_props))
203         assert isinstance(self.fmt, str) or len(self.fmt) == 2
204
205     def copy(self):
206         """Create a new copy."""
207         return Event(self.name, list(self.properties), self.fmt,
208                      self.args.copy(), self, self.event_trans, self.event_exec)
209
210     @staticmethod
211     def build(line_str):
212         """Build an Event instance from a string.
213
214         Parameters
215         ----------
216         line_str : str
217             Line describing the event.
218         """
219         m = Event._CRE.match(line_str)
220         assert m is not None
221         groups = m.groupdict('')
222
223         name = groups["name"]
224         props = groups["props"].split()
225         fmt = groups["fmt"]
226         fmt_trans = groups["fmt_trans"]
227         if len(fmt_trans) > 0:
228             fmt = [fmt_trans, fmt]
229         args = Arguments.build(groups["args"])
230
231         if "tcg-trans" in props:
232             raise ValueError("Invalid property 'tcg-trans'")
233         if "tcg-exec" in props:
234             raise ValueError("Invalid property 'tcg-exec'")
235         if "tcg" not in props and not isinstance(fmt, str):
236             raise ValueError("Only events with 'tcg' property can have two formats")
237         if "tcg" in props and isinstance(fmt, str):
238             raise ValueError("Events with 'tcg' property must have two formats")
239
240         event = Event(name, props, fmt, args)
241
242         # add implicit arguments when using the 'vcpu' property
243         import tracetool.vcpu
244         event = tracetool.vcpu.transform_event(event)
245
246         return event
247
248     def __repr__(self):
249         """Evaluable string representation for this object."""
250         if isinstance(self.fmt, str):
251             fmt = self.fmt
252         else:
253             fmt = "%s, %s" % (self.fmt[0], self.fmt[1])
254         return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
255                                           self.name,
256                                           self.args,
257                                           fmt)
258
259     _FMT = re.compile("(%[\d\.]*\w+|%.*PRI\S+)")
260
261     def formats(self):
262         """List of argument print formats."""
263         assert not isinstance(self.fmt, list)
264         return self._FMT.findall(self.fmt)
265
266     QEMU_TRACE               = "trace_%(name)s"
267     QEMU_TRACE_TCG           = QEMU_TRACE + "_tcg"
268
269     def api(self, fmt=None):
270         if fmt is None:
271             fmt = Event.QEMU_TRACE
272         return fmt % {"name": self.name}
273
274     def transform(self, *trans):
275         """Return a new Event with transformed Arguments."""
276         return Event(self.name,
277                      list(self.properties),
278                      self.fmt,
279                      self.args.transform(*trans),
280                      self)
281
282
283 def _read_events(fobj):
284     events = []
285     for line in fobj:
286         if not line.strip():
287             continue
288         if line.lstrip().startswith('#'):
289             continue
290
291         event = Event.build(line)
292
293         # transform TCG-enabled events
294         if "tcg" not in event.properties:
295             events.append(event)
296         else:
297             event_trans = event.copy()
298             event_trans.name += "_trans"
299             event_trans.properties += ["tcg-trans"]
300             event_trans.fmt = event.fmt[0]
301             # ignore TCG arguments
302             args_trans = []
303             for atrans, aorig in zip(
304                     event_trans.transform(tracetool.transform.TCG_2_HOST).args,
305                     event.args):
306                 if atrans == aorig:
307                     args_trans.append(atrans)
308             event_trans.args = Arguments(args_trans)
309
310             event_exec = event.copy()
311             event_exec.name += "_exec"
312             event_exec.properties += ["tcg-exec"]
313             event_exec.fmt = event.fmt[1]
314             event_exec.args = event_exec.args.transform(tracetool.transform.TCG_2_HOST)
315
316             new_event = [event_trans, event_exec]
317             event.event_trans, event.event_exec = new_event
318
319             events.extend(new_event)
320
321     return events
322
323
324 class TracetoolError (Exception):
325     """Exception for calls to generate."""
326     pass
327
328
329 def try_import(mod_name, attr_name=None, attr_default=None):
330     """Try to import a module and get an attribute from it.
331
332     Parameters
333     ----------
334     mod_name : str
335         Module name.
336     attr_name : str, optional
337         Name of an attribute in the module.
338     attr_default : optional
339         Default value if the attribute does not exist in the module.
340
341     Returns
342     -------
343     A pair indicating whether the module could be imported and the module or
344     object or attribute value.
345     """
346     try:
347         module = __import__(mod_name, globals(), locals(), ["__package__"])
348         if attr_name is None:
349             return True, module
350         return True, getattr(module, str(attr_name), attr_default)
351     except ImportError:
352         return False, None
353
354
355 def generate(fevents, format, backends,
356              binary=None, probe_prefix=None):
357     """Generate the output for the given (format, backends) pair.
358
359     Parameters
360     ----------
361     fevents : file
362         Event description file.
363     format : str
364         Output format name.
365     backends : list
366         Output backend names.
367     binary : str or None
368         See tracetool.backend.dtrace.BINARY.
369     probe_prefix : str or None
370         See tracetool.backend.dtrace.PROBEPREFIX.
371     """
372     # fix strange python error (UnboundLocalError tracetool)
373     import tracetool
374
375     format = str(format)
376     if len(format) is 0:
377         raise TracetoolError("format not set")
378     if not tracetool.format.exists(format):
379         raise TracetoolError("unknown format: %s" % format)
380
381     if len(backends) is 0:
382         raise TracetoolError("no backends specified")
383     for backend in backends:
384         if not tracetool.backend.exists(backend):
385             raise TracetoolError("unknown backend: %s" % backend)
386     backend = tracetool.backend.Wrapper(backends, format)
387
388     import tracetool.backend.dtrace
389     tracetool.backend.dtrace.BINARY = binary
390     tracetool.backend.dtrace.PROBEPREFIX = probe_prefix
391
392     events = _read_events(fevents)
393
394     tracetool.format.generate(events, format, backend)