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."""
34 def out(*lines
, **kwargs
):
35 """Write a set of output lines.
37 You can use kwargs as a shorthand for mapping variables when formating all
40 lines
= [ l
% kwargs
for l
in lines
]
41 sys
.stdout
.writelines("\n".join(lines
) + "\n")
43 # We only want to allow standard C types or fixed sized
44 # integer types. We don't want QEMU specific types
45 # as we can't assume trace backends can resolve all the
68 # Magic substitution is done by tracetool
72 def validate_type(name
):
73 bits
= name
.split(" ")
75 bit
= re
.sub("\*", "", bit
)
80 if bit
not in ALLOWED_TYPES
:
81 raise ValueError("Argument type '%s' is not in whitelist. "
82 "Only standard C types and fixed size integer "
83 "types should be used. struct, union, and "
84 "other complex pointer types should be "
85 "declared as 'void *'" % name
)
88 """Event arguments description."""
90 def __init__(self
, args
):
95 List of (type, name) tuples or Arguments objects.
99 if isinstance(arg
, Arguments
):
100 self
._args
.extend(arg
._args
)
102 self
._args
.append(arg
)
105 """Create a new copy."""
106 return Arguments(list(self
._args
))
110 """Build and Arguments instance from an argument string.
115 String describing the event arguments.
118 for arg
in arg_str
.split(","):
121 raise ValueError("Empty argument (did you forget to use 'void'?)")
126 arg_type
, identifier
= arg
.rsplit('*', 1)
128 identifier
= identifier
.strip()
130 arg_type
, identifier
= arg
.rsplit(None, 1)
132 validate_type(arg_type
)
133 res
.append((arg_type
, identifier
))
134 return Arguments(res
)
136 def __getitem__(self
, index
):
137 if isinstance(index
, slice):
138 return Arguments(self
._args
[index
])
140 return self
._args
[index
]
143 """Iterate over the (type, name) pairs."""
144 return iter(self
._args
)
147 """Number of arguments."""
148 return len(self
._args
)
151 """String suitable for declaring function arguments."""
152 if len(self
._args
) == 0:
155 return ", ".join([ " ".join([t
, n
]) for t
,n
in self
._args
])
158 """Evaluable string representation for this object."""
159 return "Arguments(\"%s\")" % str(self
)
162 """List of argument names."""
163 return [ name
for _
, name
in self
._args
]
166 """List of argument types."""
167 return [ type_
for type_
, _
in self
._args
]
170 """List of argument names casted to their type."""
171 return ["(%s)%s" % (type_
, name
) for type_
, name
in self
._args
]
173 def transform(self
, *trans
):
174 """Return a new Arguments instance with transformed types.
176 The types in the resulting Arguments instance are transformed according
177 to tracetool.transform.transform_type.
180 for type_
, name
in self
._args
:
181 res
.append((tracetool
.transform
.transform_type(type_
, *trans
),
183 return Arguments(res
)
187 """Event description.
194 The event format string.
195 properties : set(str)
196 Properties of the event.
202 _CRE
= re
.compile("((?P<props>[\w\s]+)\s+)?"
204 "\((?P<args>[^)]*)\)"
206 "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
209 _VALID_PROPS
= set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"])
211 def __init__(self
, name
, props
, fmt
, args
, orig
=None,
212 event_trans
=None, event_exec
=None):
220 fmt : str, list of str
221 Event printing format string(s).
225 Original Event before transformation/generation.
226 event_trans : Event or None
227 Generated translation-time event ("tcg" property).
228 event_exec : Event or None
229 Generated execution-time event ("tcg" property).
233 self
.properties
= props
236 self
.event_trans
= event_trans
237 self
.event_exec
= event_exec
240 raise ValueError("Event '%s' has more than maximum permitted "
241 "argument count" % name
)
244 self
.original
= weakref
.ref(self
)
248 unknown_props
= set(self
.properties
) - self
._VALID
_PROPS
249 if len(unknown_props
) > 0:
250 raise ValueError("Unknown properties: %s"
251 % ", ".join(unknown_props
))
252 assert isinstance(self
.fmt
, str) or len(self
.fmt
) == 2
255 """Create a new copy."""
256 return Event(self
.name
, list(self
.properties
), self
.fmt
,
257 self
.args
.copy(), self
, self
.event_trans
, self
.event_exec
)
261 """Build an Event instance from a string.
266 Line describing the event.
268 m
= Event
._CRE
.match(line_str
)
270 groups
= m
.groupdict('')
272 name
= groups
["name"]
273 props
= groups
["props"].split()
275 fmt_trans
= groups
["fmt_trans"]
276 if fmt
.find("%m") != -1 or fmt_trans
.find("%m") != -1:
277 raise ValueError("Event format '%m' is forbidden, pass the error "
278 "as an explicit trace argument")
279 if fmt
.endswith(r
'\n"'):
280 raise ValueError("Event format must not end with a newline "
283 if len(fmt_trans
) > 0:
284 fmt
= [fmt_trans
, fmt
]
285 args
= Arguments
.build(groups
["args"])
287 if "tcg-trans" in props
:
288 raise ValueError("Invalid property 'tcg-trans'")
289 if "tcg-exec" in props
:
290 raise ValueError("Invalid property 'tcg-exec'")
291 if "tcg" not in props
and not isinstance(fmt
, str):
292 raise ValueError("Only events with 'tcg' property can have two format strings")
293 if "tcg" in props
and isinstance(fmt
, str):
294 raise ValueError("Events with 'tcg' property must have two format strings")
296 event
= Event(name
, props
, fmt
, args
)
298 # add implicit arguments when using the 'vcpu' property
299 import tracetool
.vcpu
300 event
= tracetool
.vcpu
.transform_event(event
)
305 """Evaluable string representation for this object."""
306 if isinstance(self
.fmt
, str):
309 fmt
= "%s, %s" % (self
.fmt
[0], self
.fmt
[1])
310 return "Event('%s %s(%s) %s')" % (" ".join(self
.properties
),
314 # Star matching on PRI is dangerous as one might have multiple
315 # arguments with that format, hence the non-greedy version of it.
316 _FMT
= re
.compile("(%[\d\.]*\w+|%.*?PRI\S+)")
319 """List conversion specifiers in the argument print format string."""
320 assert not isinstance(self
.fmt
, list)
321 return self
._FMT
.findall(self
.fmt
)
323 QEMU_TRACE
= "trace_%(name)s"
324 QEMU_TRACE_NOCHECK
= "_nocheck__" + QEMU_TRACE
325 QEMU_TRACE_TCG
= QEMU_TRACE
+ "_tcg"
326 QEMU_DSTATE
= "_TRACE_%(NAME)s_DSTATE"
327 QEMU_BACKEND_DSTATE
= "TRACE_%(NAME)s_BACKEND_DSTATE"
328 QEMU_EVENT
= "_TRACE_%(NAME)s_EVENT"
330 def api(self
, fmt
=None):
332 fmt
= Event
.QEMU_TRACE
333 return fmt
% {"name": self
.name
, "NAME": self
.name
.upper()}
335 def transform(self
, *trans
):
336 """Return a new Event with transformed Arguments."""
337 return Event(self
.name
,
338 list(self
.properties
),
340 self
.args
.transform(*trans
),
344 def read_events(fobj
, fname
):
345 """Generate the output for the given (format, backends) pair.
350 Event description file.
354 Returns a list of Event objects
358 for lineno
, line
in enumerate(fobj
, 1):
360 raise ValueError("%s does not end with a new line" % fname
)
363 if line
.lstrip().startswith('#'):
367 event
= Event
.build(line
)
368 except ValueError as e
:
369 arg0
= 'Error at %s:%d: %s' % (fname
, lineno
, e
.args
[0])
370 e
.args
= (arg0
,) + e
.args
[1:]
373 # transform TCG-enabled events
374 if "tcg" not in event
.properties
:
377 event_trans
= event
.copy()
378 event_trans
.name
+= "_trans"
379 event_trans
.properties
+= ["tcg-trans"]
380 event_trans
.fmt
= event
.fmt
[0]
381 # ignore TCG arguments
383 for atrans
, aorig
in zip(
384 event_trans
.transform(tracetool
.transform
.TCG_2_HOST
).args
,
387 args_trans
.append(atrans
)
388 event_trans
.args
= Arguments(args_trans
)
390 event_exec
= event
.copy()
391 event_exec
.name
+= "_exec"
392 event_exec
.properties
+= ["tcg-exec"]
393 event_exec
.fmt
= event
.fmt
[1]
394 event_exec
.args
= event_exec
.args
.transform(tracetool
.transform
.TCG_2_HOST
)
396 new_event
= [event_trans
, event_exec
]
397 event
.event_trans
, event
.event_exec
= new_event
399 events
.extend(new_event
)
404 class TracetoolError (Exception):
405 """Exception for calls to generate."""
409 def try_import(mod_name
, attr_name
=None, attr_default
=None):
410 """Try to import a module and get an attribute from it.
416 attr_name : str, optional
417 Name of an attribute in the module.
418 attr_default : optional
419 Default value if the attribute does not exist in the module.
423 A pair indicating whether the module could be imported and the module or
424 object or attribute value.
427 module
= __import__(mod_name
, globals(), locals(), ["__package__"])
428 if attr_name
is None:
430 return True, getattr(module
, str(attr_name
), attr_default
)
435 def generate(events
, group
, format
, backends
,
436 binary
=None, probe_prefix
=None):
437 """Generate the output for the given (format, backends) pair.
442 list of Event objects to generate for
444 Name of the tracing group
448 Output backend names.
450 See tracetool.backend.dtrace.BINARY.
451 probe_prefix : str or None
452 See tracetool.backend.dtrace.PROBEPREFIX.
454 # fix strange python error (UnboundLocalError tracetool)
459 raise TracetoolError("format not set")
460 if not tracetool
.format
.exists(format
):
461 raise TracetoolError("unknown format: %s" % format
)
463 if len(backends
) == 0:
464 raise TracetoolError("no backends specified")
465 for backend
in backends
:
466 if not tracetool
.backend
.exists(backend
):
467 raise TracetoolError("unknown backend: %s" % backend
)
468 backend
= tracetool
.backend
.Wrapper(backends
, format
)
470 import tracetool
.backend
.dtrace
471 tracetool
.backend
.dtrace
.BINARY
= binary
472 tracetool
.backend
.dtrace
.PROBEPREFIX
= probe_prefix
474 tracetool
.format
.generate(events
, format
, backend
, group
)