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
92 def validate_type(name
):
93 bits
= name
.split(" ")
95 bit
= re
.sub("\*", "", bit
)
100 if bit
not in ALLOWED_TYPES
:
101 raise ValueError("Argument type '%s' is not allowed. "
102 "Only standard C types and fixed size integer "
103 "types should be used. struct, union, and "
104 "other complex pointer types should be "
105 "declared as 'void *'" % name
)
108 """Event arguments description."""
110 def __init__(self
, args
):
115 List of (type, name) tuples or Arguments objects.
119 if isinstance(arg
, Arguments
):
120 self
._args
.extend(arg
._args
)
122 self
._args
.append(arg
)
125 """Create a new copy."""
126 return Arguments(list(self
._args
))
130 """Build and Arguments instance from an argument string.
135 String describing the event arguments.
138 for arg
in arg_str
.split(","):
141 raise ValueError("Empty argument (did you forget to use 'void'?)")
146 arg_type
, identifier
= arg
.rsplit('*', 1)
148 identifier
= identifier
.strip()
150 arg_type
, identifier
= arg
.rsplit(None, 1)
152 validate_type(arg_type
)
153 res
.append((arg_type
, identifier
))
154 return Arguments(res
)
156 def __getitem__(self
, index
):
157 if isinstance(index
, slice):
158 return Arguments(self
._args
[index
])
160 return self
._args
[index
]
163 """Iterate over the (type, name) pairs."""
164 return iter(self
._args
)
167 """Number of arguments."""
168 return len(self
._args
)
171 """String suitable for declaring function arguments."""
172 if len(self
._args
) == 0:
175 return ", ".join([ " ".join([t
, n
]) for t
,n
in self
._args
])
178 """Evaluable string representation for this object."""
179 return "Arguments(\"%s\")" % str(self
)
182 """List of argument names."""
183 return [ name
for _
, name
in self
._args
]
186 """List of argument types."""
187 return [ type_
for type_
, _
in self
._args
]
190 """List of argument names casted to their type."""
191 return ["(%s)%s" % (type_
, name
) for type_
, name
in self
._args
]
193 def transform(self
, *trans
):
194 """Return a new Arguments instance with transformed types.
196 The types in the resulting Arguments instance are transformed according
197 to tracetool.transform.transform_type.
200 for type_
, name
in self
._args
:
201 res
.append((tracetool
.transform
.transform_type(type_
, *trans
),
203 return Arguments(res
)
207 """Event description.
214 The event format string.
215 properties : set(str)
216 Properties of the event.
220 The line number in the input file.
222 The path to the input file.
226 _CRE
= re
.compile("((?P<props>[\w\s]+)\s+)?"
228 "\((?P<args>[^)]*)\)"
230 "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
233 _VALID_PROPS
= set(["disable", "vcpu"])
235 def __init__(self
, name
, props
, fmt
, args
, lineno
, filename
, orig
=None,
236 event_trans
=None, event_exec
=None):
244 fmt : str, list of str
245 Event printing format string(s).
249 The line number in the input file.
251 The path to the input file.
253 Original Event before transformation/generation.
254 event_trans : Event or None
255 Generated translation-time event ("tcg" property).
256 event_exec : Event or None
257 Generated execution-time event ("tcg" property).
261 self
.properties
= props
264 self
.lineno
= int(lineno
)
265 self
.filename
= str(filename
)
266 self
.event_trans
= event_trans
267 self
.event_exec
= event_exec
270 raise ValueError("Event '%s' has more than maximum permitted "
271 "argument count" % name
)
274 self
.original
= weakref
.ref(self
)
278 unknown_props
= set(self
.properties
) - self
._VALID
_PROPS
279 if len(unknown_props
) > 0:
280 raise ValueError("Unknown properties: %s"
281 % ", ".join(unknown_props
))
282 assert isinstance(self
.fmt
, str) or len(self
.fmt
) == 2
285 """Create a new copy."""
286 return Event(self
.name
, list(self
.properties
), self
.fmt
,
287 self
.args
.copy(), self
.lineno
, self
.filename
,
288 self
, self
.event_trans
, self
.event_exec
)
291 def build(line_str
, lineno
, filename
):
292 """Build an Event instance from a string.
297 Line describing the event.
299 Line number in input file.
303 m
= Event
._CRE
.match(line_str
)
305 groups
= m
.groupdict('')
307 name
= groups
["name"]
308 props
= groups
["props"].split()
310 fmt_trans
= groups
["fmt_trans"]
311 if fmt
.find("%m") != -1 or fmt_trans
.find("%m") != -1:
312 raise ValueError("Event format '%m' is forbidden, pass the error "
313 "as an explicit trace argument")
314 if fmt
.endswith(r
'\n"'):
315 raise ValueError("Event format must not end with a newline "
318 if len(fmt_trans
) > 0:
319 fmt
= [fmt_trans
, fmt
]
320 args
= Arguments
.build(groups
["args"])
322 event
= Event(name
, props
, fmt
, args
, lineno
, filename
)
324 # add implicit arguments when using the 'vcpu' property
325 import tracetool
.vcpu
326 event
= tracetool
.vcpu
.transform_event(event
)
331 """Evaluable string representation for this object."""
332 if isinstance(self
.fmt
, str):
335 fmt
= "%s, %s" % (self
.fmt
[0], self
.fmt
[1])
336 return "Event('%s %s(%s) %s')" % (" ".join(self
.properties
),
340 # Star matching on PRI is dangerous as one might have multiple
341 # arguments with that format, hence the non-greedy version of it.
342 _FMT
= re
.compile("(%[\d\.]*\w+|%.*?PRI\S+)")
345 """List conversion specifiers in the argument print format string."""
346 assert not isinstance(self
.fmt
, list)
347 return self
._FMT
.findall(self
.fmt
)
349 QEMU_TRACE
= "trace_%(name)s"
350 QEMU_TRACE_NOCHECK
= "_nocheck__" + QEMU_TRACE
351 QEMU_TRACE_TCG
= QEMU_TRACE
+ "_tcg"
352 QEMU_DSTATE
= "_TRACE_%(NAME)s_DSTATE"
353 QEMU_BACKEND_DSTATE
= "TRACE_%(NAME)s_BACKEND_DSTATE"
354 QEMU_EVENT
= "_TRACE_%(NAME)s_EVENT"
356 def api(self
, fmt
=None):
358 fmt
= Event
.QEMU_TRACE
359 return fmt
% {"name": self
.name
, "NAME": self
.name
.upper()}
361 def transform(self
, *trans
):
362 """Return a new Event with transformed Arguments."""
363 return Event(self
.name
,
364 list(self
.properties
),
366 self
.args
.transform(*trans
),
372 def read_events(fobj
, fname
):
373 """Generate the output for the given (format, backends) pair.
378 Event description file.
382 Returns a list of Event objects
386 for lineno
, line
in enumerate(fobj
, 1):
388 raise ValueError("%s does not end with a new line" % fname
)
391 if line
.lstrip().startswith('#'):
395 event
= Event
.build(line
, lineno
, fname
)
396 except ValueError as e
:
397 arg0
= 'Error at %s:%d: %s' % (fname
, lineno
, e
.args
[0])
398 e
.args
= (arg0
,) + e
.args
[1:]
406 class TracetoolError (Exception):
407 """Exception for calls to generate."""
411 def try_import(mod_name
, attr_name
=None, attr_default
=None):
412 """Try to import a module and get an attribute from it.
418 attr_name : str, optional
419 Name of an attribute in the module.
420 attr_default : optional
421 Default value if the attribute does not exist in the module.
425 A pair indicating whether the module could be imported and the module or
426 object or attribute value.
429 module
= __import__(mod_name
, globals(), locals(), ["__package__"])
430 if attr_name
is None:
432 return True, getattr(module
, str(attr_name
), attr_default
)
437 def generate(events
, group
, format
, backends
,
438 binary
=None, probe_prefix
=None):
439 """Generate the output for the given (format, backends) pair.
444 list of Event objects to generate for
446 Name of the tracing group
450 Output backend names.
452 See tracetool.backend.dtrace.BINARY.
453 probe_prefix : str or None
454 See tracetool.backend.dtrace.PROBEPREFIX.
456 # fix strange python error (UnboundLocalError tracetool)
461 raise TracetoolError("format not set")
462 if not tracetool
.format
.exists(format
):
463 raise TracetoolError("unknown format: %s" % format
)
465 if len(backends
) == 0:
466 raise TracetoolError("no backends specified")
467 for backend
in backends
:
468 if not tracetool
.backend
.exists(backend
):
469 raise TracetoolError("unknown backend: %s" % backend
)
470 backend
= tracetool
.backend
.Wrapper(backends
, format
)
472 import tracetool
.backend
.dtrace
473 tracetool
.backend
.dtrace
.BINARY
= binary
474 tracetool
.backend
.dtrace
.PROBEPREFIX
= probe_prefix
476 tracetool
.format
.generate(events
, format
, backend
, group
)