]> Git Repo - qemu.git/blob - scripts/tracetool/__init__.py
trace: remove use of QEMU specific types from trace probes
[qemu.git] / 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 <[email protected]>"
9 __copyright__  = "Copyright 2012-2017, Lluís Vilanova <[email protected]>"
10 __license__    = "GPL version 2 or (at your option) any later version"
11
12 __maintainer__ = "Stefan Hajnoczi"
13 __email__      = "[email protected]"
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 not arg:
79                 raise ValueError("Empty argument (did you forget to use 'void'?)")
80             if arg == 'void':
81                 continue
82
83             if '*' in arg:
84                 arg_type, identifier = arg.rsplit('*', 1)
85                 arg_type += '*'
86                 identifier = identifier.strip()
87             else:
88                 arg_type, identifier = arg.rsplit(None, 1)
89
90             res.append((arg_type, identifier))
91         return Arguments(res)
92
93     def __getitem__(self, index):
94         if isinstance(index, slice):
95             return Arguments(self._args[index])
96         else:
97             return self._args[index]
98
99     def __iter__(self):
100         """Iterate over the (type, name) pairs."""
101         return iter(self._args)
102
103     def __len__(self):
104         """Number of arguments."""
105         return len(self._args)
106
107     def __str__(self):
108         """String suitable for declaring function arguments."""
109         if len(self._args) == 0:
110             return "void"
111         else:
112             return ", ".join([ " ".join([t, n]) for t,n in self._args ])
113
114     def __repr__(self):
115         """Evaluable string representation for this object."""
116         return "Arguments(\"%s\")" % str(self)
117
118     def names(self):
119         """List of argument names."""
120         return [ name for _, name in self._args ]
121
122     def types(self):
123         """List of argument types."""
124         return [ type_ for type_, _ in self._args ]
125
126     def casted(self):
127         """List of argument names casted to their type."""
128         return ["(%s)%s" % (type_, name) for type_, name in self._args]
129
130     def transform(self, *trans):
131         """Return a new Arguments instance with transformed types.
132
133         The types in the resulting Arguments instance are transformed according
134         to tracetool.transform.transform_type.
135         """
136         res = []
137         for type_, name in self._args:
138             res.append((tracetool.transform.transform_type(type_, *trans),
139                         name))
140         return Arguments(res)
141
142
143 class Event(object):
144     """Event description.
145
146     Attributes
147     ----------
148     name : str
149         The event name.
150     fmt : str
151         The event format string.
152     properties : set(str)
153         Properties of the event.
154     args : Arguments
155         The event arguments.
156
157     """
158
159     _CRE = re.compile("((?P<props>[\w\s]+)\s+)?"
160                       "(?P<name>\w+)"
161                       "\((?P<args>[^)]*)\)"
162                       "\s*"
163                       "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
164                       "\s*")
165
166     _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"])
167
168     def __init__(self, name, props, fmt, args, orig=None,
169                  event_trans=None, event_exec=None):
170         """
171         Parameters
172         ----------
173         name : string
174             Event name.
175         props : list of str
176             Property names.
177         fmt : str, list of str
178             Event printing format string(s).
179         args : Arguments
180             Event arguments.
181         orig : Event or None
182             Original Event before transformation/generation.
183         event_trans : Event or None
184             Generated translation-time event ("tcg" property).
185         event_exec : Event or None
186             Generated execution-time event ("tcg" property).
187
188         """
189         self.name = name
190         self.properties = props
191         self.fmt = fmt
192         self.args = args
193         self.event_trans = event_trans
194         self.event_exec = event_exec
195
196         if len(args) > 10:
197             raise ValueError("Event '%s' has more than maximum permitted "
198                              "argument count" % name)
199
200         if orig is None:
201             self.original = weakref.ref(self)
202         else:
203             self.original = orig
204
205         unknown_props = set(self.properties) - self._VALID_PROPS
206         if len(unknown_props) > 0:
207             raise ValueError("Unknown properties: %s"
208                              % ", ".join(unknown_props))
209         assert isinstance(self.fmt, str) or len(self.fmt) == 2
210
211     def copy(self):
212         """Create a new copy."""
213         return Event(self.name, list(self.properties), self.fmt,
214                      self.args.copy(), self, self.event_trans, self.event_exec)
215
216     @staticmethod
217     def build(line_str):
218         """Build an Event instance from a string.
219
220         Parameters
221         ----------
222         line_str : str
223             Line describing the event.
224         """
225         m = Event._CRE.match(line_str)
226         assert m is not None
227         groups = m.groupdict('')
228
229         name = groups["name"]
230         props = groups["props"].split()
231         fmt = groups["fmt"]
232         fmt_trans = groups["fmt_trans"]
233         if len(fmt_trans) > 0:
234             fmt = [fmt_trans, fmt]
235         args = Arguments.build(groups["args"])
236
237         if "tcg-trans" in props:
238             raise ValueError("Invalid property 'tcg-trans'")
239         if "tcg-exec" in props:
240             raise ValueError("Invalid property 'tcg-exec'")
241         if "tcg" not in props and not isinstance(fmt, str):
242             raise ValueError("Only events with 'tcg' property can have two format strings")
243         if "tcg" in props and isinstance(fmt, str):
244             raise ValueError("Events with 'tcg' property must have two format strings")
245
246         event = Event(name, props, fmt, args)
247
248         # add implicit arguments when using the 'vcpu' property
249         import tracetool.vcpu
250         event = tracetool.vcpu.transform_event(event)
251
252         return event
253
254     def __repr__(self):
255         """Evaluable string representation for this object."""
256         if isinstance(self.fmt, str):
257             fmt = self.fmt
258         else:
259             fmt = "%s, %s" % (self.fmt[0], self.fmt[1])
260         return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
261                                           self.name,
262                                           self.args,
263                                           fmt)
264     # Star matching on PRI is dangerous as one might have multiple
265     # arguments with that format, hence the non-greedy version of it.
266     _FMT = re.compile("(%[\d\.]*\w+|%.*?PRI\S+)")
267
268     def formats(self):
269         """List conversion specifiers in the argument print format string."""
270         assert not isinstance(self.fmt, list)
271         return self._FMT.findall(self.fmt)
272
273     QEMU_TRACE               = "trace_%(name)s"
274     QEMU_TRACE_NOCHECK       = "_nocheck__" + QEMU_TRACE
275     QEMU_TRACE_TCG           = QEMU_TRACE + "_tcg"
276     QEMU_DSTATE              = "_TRACE_%(NAME)s_DSTATE"
277     QEMU_BACKEND_DSTATE      = "TRACE_%(NAME)s_BACKEND_DSTATE"
278     QEMU_EVENT               = "_TRACE_%(NAME)s_EVENT"
279
280     def api(self, fmt=None):
281         if fmt is None:
282             fmt = Event.QEMU_TRACE
283         return fmt % {"name": self.name, "NAME": self.name.upper()}
284
285     def transform(self, *trans):
286         """Return a new Event with transformed Arguments."""
287         return Event(self.name,
288                      list(self.properties),
289                      self.fmt,
290                      self.args.transform(*trans),
291                      self)
292
293
294 def read_events(fobj, fname):
295     """Generate the output for the given (format, backends) pair.
296
297     Parameters
298     ----------
299     fobj : file
300         Event description file.
301     fname : str
302         Name of event file
303
304     Returns a list of Event objects
305     """
306
307     events = []
308     for lineno, line in enumerate(fobj, 1):
309         if not line.strip():
310             continue
311         if line.lstrip().startswith('#'):
312             continue
313
314         try:
315             event = Event.build(line)
316         except ValueError as e:
317             arg0 = 'Error at %s:%d: %s' % (fname, lineno, e.args[0])
318             e.args = (arg0,) + e.args[1:]
319             raise
320
321         # transform TCG-enabled events
322         if "tcg" not in event.properties:
323             events.append(event)
324         else:
325             event_trans = event.copy()
326             event_trans.name += "_trans"
327             event_trans.properties += ["tcg-trans"]
328             event_trans.fmt = event.fmt[0]
329             # ignore TCG arguments
330             args_trans = []
331             for atrans, aorig in zip(
332                     event_trans.transform(tracetool.transform.TCG_2_HOST).args,
333                     event.args):
334                 if atrans == aorig:
335                     args_trans.append(atrans)
336             event_trans.args = Arguments(args_trans)
337
338             event_exec = event.copy()
339             event_exec.name += "_exec"
340             event_exec.properties += ["tcg-exec"]
341             event_exec.fmt = event.fmt[1]
342             event_exec.args = event_exec.args.transform(tracetool.transform.TCG_2_HOST)
343
344             new_event = [event_trans, event_exec]
345             event.event_trans, event.event_exec = new_event
346
347             events.extend(new_event)
348
349     return events
350
351
352 class TracetoolError (Exception):
353     """Exception for calls to generate."""
354     pass
355
356
357 def try_import(mod_name, attr_name=None, attr_default=None):
358     """Try to import a module and get an attribute from it.
359
360     Parameters
361     ----------
362     mod_name : str
363         Module name.
364     attr_name : str, optional
365         Name of an attribute in the module.
366     attr_default : optional
367         Default value if the attribute does not exist in the module.
368
369     Returns
370     -------
371     A pair indicating whether the module could be imported and the module or
372     object or attribute value.
373     """
374     try:
375         module = __import__(mod_name, globals(), locals(), ["__package__"])
376         if attr_name is None:
377             return True, module
378         return True, getattr(module, str(attr_name), attr_default)
379     except ImportError:
380         return False, None
381
382
383 def generate(events, group, format, backends,
384              binary=None, probe_prefix=None):
385     """Generate the output for the given (format, backends) pair.
386
387     Parameters
388     ----------
389     events : list
390         list of Event objects to generate for
391     group: str
392         Name of the tracing group
393     format : str
394         Output format name.
395     backends : list
396         Output backend names.
397     binary : str or None
398         See tracetool.backend.dtrace.BINARY.
399     probe_prefix : str or None
400         See tracetool.backend.dtrace.PROBEPREFIX.
401     """
402     # fix strange python error (UnboundLocalError tracetool)
403     import tracetool
404
405     format = str(format)
406     if len(format) is 0:
407         raise TracetoolError("format not set")
408     if not tracetool.format.exists(format):
409         raise TracetoolError("unknown format: %s" % format)
410
411     if len(backends) is 0:
412         raise TracetoolError("no backends specified")
413     for backend in backends:
414         if not tracetool.backend.exists(backend):
415             raise TracetoolError("unknown backend: %s" % backend)
416     backend = tracetool.backend.Wrapper(backends, format)
417
418     import tracetool.backend.dtrace
419     tracetool.backend.dtrace.BINARY = binary
420     tracetool.backend.dtrace.PROBEPREFIX = probe_prefix
421
422     tracetool.format.generate(events, format, backend, group)
This page took 0.046856 seconds and 4 git commands to generate.