1 # -*- coding: utf-8 -*-
4 Machinery for generating tracing-related intermediate files.
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"
11 __maintainer__
= "Stefan Hajnoczi"
12 __email__
= "stefanha@redhat.com"
19 import tracetool
.format
20 import tracetool
.backend
21 import tracetool
.transform
24 def error_write(*lines
):
25 """Write a set of error lines."""
26 sys
.stderr
.writelines("\n".join(lines
) + "\n")
29 """Write a set of error lines and exit."""
35 out_filename
= '<none>'
38 def out_open(filename
):
39 global out_filename
, out_fobj
40 out_filename
= filename
41 out_fobj
= open(filename
, 'wt')
43 def out(*lines
, **kwargs
):
44 """Write a set of output lines.
46 You can use kwargs as a shorthand for mapping variables when formatting all
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.
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
)
63 out_fobj
.writelines("\n".join(output
) + "\n")
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
90 # Magic substitution is done by tracetool
94 def validate_type(name
):
95 bits
= name
.split(" ")
97 bit
= re
.sub("\*", "", bit
)
102 if bit
not in ALLOWED_TYPES
:
103 raise ValueError("Argument type '%s' is not allowed. "
104 "Only standard C types and fixed size integer "
105 "types should be used. struct, union, and "
106 "other complex pointer types should be "
107 "declared as 'void *'" % name
)
110 """Event arguments description."""
112 def __init__(self
, args
):
117 List of (type, name) tuples or Arguments objects.
121 if isinstance(arg
, Arguments
):
122 self
._args
.extend(arg
._args
)
124 self
._args
.append(arg
)
127 """Create a new copy."""
128 return Arguments(list(self
._args
))
132 """Build and Arguments instance from an argument string.
137 String describing the event arguments.
140 for arg
in arg_str
.split(","):
143 raise ValueError("Empty argument (did you forget to use 'void'?)")
148 arg_type
, identifier
= arg
.rsplit('*', 1)
150 identifier
= identifier
.strip()
152 arg_type
, identifier
= arg
.rsplit(None, 1)
154 validate_type(arg_type
)
155 res
.append((arg_type
, identifier
))
156 return Arguments(res
)
158 def __getitem__(self
, index
):
159 if isinstance(index
, slice):
160 return Arguments(self
._args
[index
])
162 return self
._args
[index
]
165 """Iterate over the (type, name) pairs."""
166 return iter(self
._args
)
169 """Number of arguments."""
170 return len(self
._args
)
173 """String suitable for declaring function arguments."""
174 if len(self
._args
) == 0:
177 return ", ".join([ " ".join([t
, n
]) for t
,n
in self
._args
])
180 """Evaluable string representation for this object."""
181 return "Arguments(\"%s\")" % str(self
)
184 """List of argument names."""
185 return [ name
for _
, name
in self
._args
]
188 """List of argument types."""
189 return [ type_
for type_
, _
in self
._args
]
192 """List of argument names casted to their type."""
193 return ["(%s)%s" % (type_
, name
) for type_
, name
in self
._args
]
195 def transform(self
, *trans
):
196 """Return a new Arguments instance with transformed types.
198 The types in the resulting Arguments instance are transformed according
199 to tracetool.transform.transform_type.
202 for type_
, name
in self
._args
:
203 res
.append((tracetool
.transform
.transform_type(type_
, *trans
),
205 return Arguments(res
)
209 """Event description.
216 The event format string.
217 properties : set(str)
218 Properties of the event.
222 The line number in the input file.
224 The path to the input file.
228 _CRE
= re
.compile("((?P<props>[\w\s]+)\s+)?"
230 "\((?P<args>[^)]*)\)"
232 "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
235 _VALID_PROPS
= set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"])
237 def __init__(self
, name
, props
, fmt
, args
, lineno
, filename
, orig
=None,
238 event_trans
=None, event_exec
=None):
246 fmt : str, list of str
247 Event printing format string(s).
251 The line number in the input file.
253 The path to the input file.
255 Original Event before transformation/generation.
256 event_trans : Event or None
257 Generated translation-time event ("tcg" property).
258 event_exec : Event or None
259 Generated execution-time event ("tcg" property).
263 self
.properties
= props
266 self
.lineno
= int(lineno
)
267 self
.filename
= str(filename
)
268 self
.event_trans
= event_trans
269 self
.event_exec
= event_exec
272 raise ValueError("Event '%s' has more than maximum permitted "
273 "argument count" % name
)
276 self
.original
= weakref
.ref(self
)
280 unknown_props
= set(self
.properties
) - self
._VALID
_PROPS
281 if len(unknown_props
) > 0:
282 raise ValueError("Unknown properties: %s"
283 % ", ".join(unknown_props
))
284 assert isinstance(self
.fmt
, str) or len(self
.fmt
) == 2
287 """Create a new copy."""
288 return Event(self
.name
, list(self
.properties
), self
.fmt
,
289 self
.args
.copy(), self
.lineno
, self
.filename
,
290 self
, self
.event_trans
, self
.event_exec
)
293 def build(line_str
, lineno
, filename
):
294 """Build an Event instance from a string.
299 Line describing the event.
301 Line number in input file.
305 m
= Event
._CRE
.match(line_str
)
307 groups
= m
.groupdict('')
309 name
= groups
["name"]
310 props
= groups
["props"].split()
312 fmt_trans
= groups
["fmt_trans"]
313 if fmt
.find("%m") != -1 or fmt_trans
.find("%m") != -1:
314 raise ValueError("Event format '%m' is forbidden, pass the error "
315 "as an explicit trace argument")
316 if fmt
.endswith(r
'\n"'):
317 raise ValueError("Event format must not end with a newline "
320 if len(fmt_trans
) > 0:
321 fmt
= [fmt_trans
, fmt
]
322 args
= Arguments
.build(groups
["args"])
324 if "tcg-trans" in props
:
325 raise ValueError("Invalid property 'tcg-trans'")
326 if "tcg-exec" in props
:
327 raise ValueError("Invalid property 'tcg-exec'")
328 if "tcg" not in props
and not isinstance(fmt
, str):
329 raise ValueError("Only events with 'tcg' property can have two format strings")
330 if "tcg" in props
and isinstance(fmt
, str):
331 raise ValueError("Events with 'tcg' property must have two format strings")
333 event
= Event(name
, props
, fmt
, args
, lineno
, filename
)
335 # add implicit arguments when using the 'vcpu' property
336 import tracetool
.vcpu
337 event
= tracetool
.vcpu
.transform_event(event
)
342 """Evaluable string representation for this object."""
343 if isinstance(self
.fmt
, str):
346 fmt
= "%s, %s" % (self
.fmt
[0], self
.fmt
[1])
347 return "Event('%s %s(%s) %s')" % (" ".join(self
.properties
),
351 # Star matching on PRI is dangerous as one might have multiple
352 # arguments with that format, hence the non-greedy version of it.
353 _FMT
= re
.compile("(%[\d\.]*\w+|%.*?PRI\S+)")
356 """List conversion specifiers in the argument print format string."""
357 assert not isinstance(self
.fmt
, list)
358 return self
._FMT
.findall(self
.fmt
)
360 QEMU_TRACE
= "trace_%(name)s"
361 QEMU_TRACE_NOCHECK
= "_nocheck__" + QEMU_TRACE
362 QEMU_TRACE_TCG
= QEMU_TRACE
+ "_tcg"
363 QEMU_DSTATE
= "_TRACE_%(NAME)s_DSTATE"
364 QEMU_BACKEND_DSTATE
= "TRACE_%(NAME)s_BACKEND_DSTATE"
365 QEMU_EVENT
= "_TRACE_%(NAME)s_EVENT"
367 def api(self
, fmt
=None):
369 fmt
= Event
.QEMU_TRACE
370 return fmt
% {"name": self
.name
, "NAME": self
.name
.upper()}
372 def transform(self
, *trans
):
373 """Return a new Event with transformed Arguments."""
374 return Event(self
.name
,
375 list(self
.properties
),
377 self
.args
.transform(*trans
),
383 def read_events(fobj
, fname
):
384 """Generate the output for the given (format, backends) pair.
389 Event description file.
393 Returns a list of Event objects
397 for lineno
, line
in enumerate(fobj
, 1):
399 raise ValueError("%s does not end with a new line" % fname
)
402 if line
.lstrip().startswith('#'):
406 event
= Event
.build(line
, lineno
, fname
)
407 except ValueError as e
:
408 arg0
= 'Error at %s:%d: %s' % (fname
, lineno
, e
.args
[0])
409 e
.args
= (arg0
,) + e
.args
[1:]
412 # transform TCG-enabled events
413 if "tcg" not in event
.properties
:
416 event_trans
= event
.copy()
417 event_trans
.name
+= "_trans"
418 event_trans
.properties
+= ["tcg-trans"]
419 event_trans
.fmt
= event
.fmt
[0]
420 # ignore TCG arguments
422 for atrans
, aorig
in zip(
423 event_trans
.transform(tracetool
.transform
.TCG_2_HOST
).args
,
426 args_trans
.append(atrans
)
427 event_trans
.args
= Arguments(args_trans
)
429 event_exec
= event
.copy()
430 event_exec
.name
+= "_exec"
431 event_exec
.properties
+= ["tcg-exec"]
432 event_exec
.fmt
= event
.fmt
[1]
433 event_exec
.args
= event_exec
.args
.transform(tracetool
.transform
.TCG_2_HOST
)
435 new_event
= [event_trans
, event_exec
]
436 event
.event_trans
, event
.event_exec
= new_event
438 events
.extend(new_event
)
443 class TracetoolError (Exception):
444 """Exception for calls to generate."""
448 def try_import(mod_name
, attr_name
=None, attr_default
=None):
449 """Try to import a module and get an attribute from it.
455 attr_name : str, optional
456 Name of an attribute in the module.
457 attr_default : optional
458 Default value if the attribute does not exist in the module.
462 A pair indicating whether the module could be imported and the module or
463 object or attribute value.
466 module
= __import__(mod_name
, globals(), locals(), ["__package__"])
467 if attr_name
is None:
469 return True, getattr(module
, str(attr_name
), attr_default
)
474 def generate(events
, group
, format
, backends
,
475 binary
=None, probe_prefix
=None):
476 """Generate the output for the given (format, backends) pair.
481 list of Event objects to generate for
483 Name of the tracing group
487 Output backend names.
489 See tracetool.backend.dtrace.BINARY.
490 probe_prefix : str or None
491 See tracetool.backend.dtrace.PROBEPREFIX.
493 # fix strange python error (UnboundLocalError tracetool)
498 raise TracetoolError("format not set")
499 if not tracetool
.format
.exists(format
):
500 raise TracetoolError("unknown format: %s" % format
)
502 if len(backends
) == 0:
503 raise TracetoolError("no backends specified")
504 for backend
in backends
:
505 if not tracetool
.backend
.exists(backend
):
506 raise TracetoolError("unknown backend: %s" % backend
)
507 backend
= tracetool
.backend
.Wrapper(backends
, format
)
509 import tracetool
.backend
.dtrace
510 tracetool
.backend
.dtrace
.BINARY
= binary
511 tracetool
.backend
.dtrace
.PROBEPREFIX
= probe_prefix
513 tracetool
.format
.generate(events
, format
, backend
, group
)