2 # -*- coding: utf-8 -*-
5 Machinery for generating tracing-related intermediate files.
10 __license__ = "GPL version 2 or (at your option) any later version"
12 __maintainer__ = "Stefan Hajnoczi"
20 import tracetool.format
21 import tracetool.backend
22 import tracetool.transform
25 def error_write(*lines):
26 """Write a set of error lines."""
27 sys.stderr.writelines("\n".join(lines) + "\n")
30 """Write a set of error lines and exit."""
35 def out(*lines, **kwargs):
36 """Write a set of output lines.
38 You can use kwargs as a shorthand for mapping variables when formating all
41 lines = [ l % kwargs for l in lines ]
42 sys.stdout.writelines("\n".join(lines) + "\n")
46 """Event arguments description."""
48 def __init__(self, args):
53 List of (type, name) tuples or Arguments objects.
57 if isinstance(arg, Arguments):
58 self._args.extend(arg._args)
60 self._args.append(arg)
63 """Create a new copy."""
64 return Arguments(list(self._args))
68 """Build and Arguments instance from an argument string.
73 String describing the event arguments.
76 for arg in arg_str.split(","):
79 raise ValueError("Empty argument (did you forget to use 'void'?)")
84 arg_type, identifier = arg.rsplit('*', 1)
86 identifier = identifier.strip()
88 arg_type, identifier = arg.rsplit(None, 1)
90 res.append((arg_type, identifier))
93 def __getitem__(self, index):
94 if isinstance(index, slice):
95 return Arguments(self._args[index])
97 return self._args[index]
100 """Iterate over the (type, name) pairs."""
101 return iter(self._args)
104 """Number of arguments."""
105 return len(self._args)
108 """String suitable for declaring function arguments."""
109 if len(self._args) == 0:
112 return ", ".join([ " ".join([t, n]) for t,n in self._args ])
115 """Evaluable string representation for this object."""
116 return "Arguments(\"%s\")" % str(self)
119 """List of argument names."""
120 return [ name for _, name in self._args ]
123 """List of argument types."""
124 return [ type_ for type_, _ in self._args ]
127 """List of argument names casted to their type."""
128 return ["(%s)%s" % (type_, name) for type_, name in self._args]
130 def transform(self, *trans):
131 """Return a new Arguments instance with transformed types.
133 The types in the resulting Arguments instance are transformed according
134 to tracetool.transform.transform_type.
137 for type_, name in self._args:
138 res.append((tracetool.transform.transform_type(type_, *trans),
140 return Arguments(res)
144 """Event description.
151 The event format string.
152 properties : set(str)
153 Properties of the event.
159 _CRE = re.compile("((?P<props>[\w\s]+)\s+)?"
161 "\((?P<args>[^)]*)\)"
163 "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
166 _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"])
168 def __init__(self, name, props, fmt, args, orig=None,
169 event_trans=None, event_exec=None):
177 fmt : str, list of str
178 Event printing format string(s).
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).
190 self.properties = props
193 self.event_trans = event_trans
194 self.event_exec = event_exec
197 raise ValueError("Event '%s' has more than maximum permitted "
198 "argument count" % name)
201 self.original = weakref.ref(self)
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
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)
218 """Build an Event instance from a string.
223 Line describing the event.
225 m = Event._CRE.match(line_str)
227 groups = m.groupdict('')
229 name = groups["name"]
230 props = groups["props"].split()
232 fmt_trans = groups["fmt_trans"]
233 if len(fmt_trans) > 0:
234 fmt = [fmt_trans, fmt]
235 args = Arguments.build(groups["args"])
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")
246 event = Event(name, props, fmt, args)
248 # add implicit arguments when using the 'vcpu' property
249 import tracetool.vcpu
250 event = tracetool.vcpu.transform_event(event)
255 """Evaluable string representation for this object."""
256 if isinstance(self.fmt, str):
259 fmt = "%s, %s" % (self.fmt[0], self.fmt[1])
260 return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
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+)")
269 """List conversion specifiers in the argument print format string."""
270 assert not isinstance(self.fmt, list)
271 return self._FMT.findall(self.fmt)
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"
280 def api(self, fmt=None):
282 fmt = Event.QEMU_TRACE
283 return fmt % {"name": self.name, "NAME": self.name.upper()}
285 def transform(self, *trans):
286 """Return a new Event with transformed Arguments."""
287 return Event(self.name,
288 list(self.properties),
290 self.args.transform(*trans),
294 def read_events(fobj, fname):
295 """Generate the output for the given (format, backends) pair.
300 Event description file.
304 Returns a list of Event objects
308 for lineno, line in enumerate(fobj, 1):
311 if line.lstrip().startswith('#'):
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:]
321 # transform TCG-enabled events
322 if "tcg" not in event.properties:
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
331 for atrans, aorig in zip(
332 event_trans.transform(tracetool.transform.TCG_2_HOST).args,
335 args_trans.append(atrans)
336 event_trans.args = Arguments(args_trans)
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)
344 new_event = [event_trans, event_exec]
345 event.event_trans, event.event_exec = new_event
347 events.extend(new_event)
352 class TracetoolError (Exception):
353 """Exception for calls to generate."""
357 def try_import(mod_name, attr_name=None, attr_default=None):
358 """Try to import a module and get an attribute from it.
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.
371 A pair indicating whether the module could be imported and the module or
372 object or attribute value.
375 module = __import__(mod_name, globals(), locals(), ["__package__"])
376 if attr_name is None:
378 return True, getattr(module, str(attr_name), attr_default)
383 def generate(events, group, format, backends,
384 binary=None, probe_prefix=None):
385 """Generate the output for the given (format, backends) pair.
390 list of Event objects to generate for
392 Name of the tracing group
396 Output backend names.
398 See tracetool.backend.dtrace.BINARY.
399 probe_prefix : str or None
400 See tracetool.backend.dtrace.PROBEPREFIX.
402 # fix strange python error (UnboundLocalError tracetool)
407 raise TracetoolError("format not set")
408 if not tracetool.format.exists(format):
409 raise TracetoolError("unknown format: %s" % format)
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)
418 import tracetool.backend.dtrace
419 tracetool.backend.dtrace.BINARY = binary
420 tracetool.backend.dtrace.PROBEPREFIX = probe_prefix
422 tracetool.format.generate(events, format, backend, group)