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
23 def error_write(*lines
):
24 """Write a set of error lines."""
25 sys
.stderr
.writelines("\n".join(lines
) + "\n")
28 """Write a set of error lines and exit."""
34 out_filename
= '<none>'
37 def out_open(filename
):
38 global out_filename
, out_fobj
39 out_filename
= filename
40 out_fobj
= open(filename
, 'wt')
42 def out(*lines
, **kwargs
):
43 """Write a set of output lines.
45 You can use kwargs as a shorthand for mapping variables when formatting all
48 The 'out_lineno' kwarg is automatically added to reflect the current output
49 file line number. The 'out_next_lineno' kwarg is also automatically added
50 with the next output line number. The 'out_filename' kwarg is automatically
51 added with the output filename.
56 kwargs
['out_lineno'] = out_lineno
57 kwargs
['out_next_lineno'] = out_lineno
+ 1
58 kwargs
['out_filename'] = out_filename
59 output
.append(l
% kwargs
)
62 out_fobj
.writelines("\n".join(output
) + "\n")
64 # We only want to allow standard C types or fixed sized
65 # integer types. We don't want QEMU specific types
66 # as we can't assume trace backends can resolve all the
91 def validate_type(name
):
92 bits
= name
.split(" ")
94 bit
= re
.sub(r
"\*", "", bit
)
99 if bit
not in ALLOWED_TYPES
:
100 raise ValueError("Argument type '%s' is not allowed. "
101 "Only standard C types and fixed size integer "
102 "types should be used. struct, union, and "
103 "other complex pointer types should be "
104 "declared as 'void *'" % name
)
107 """Event arguments description."""
109 def __init__(self
, args
):
114 List of (type, name) tuples or Arguments objects.
118 if isinstance(arg
, Arguments
):
119 self
._args
.extend(arg
._args
)
121 self
._args
.append(arg
)
124 """Create a new copy."""
125 return Arguments(list(self
._args
))
129 """Build and Arguments instance from an argument string.
134 String describing the event arguments.
137 for arg
in arg_str
.split(","):
140 raise ValueError("Empty argument (did you forget to use 'void'?)")
145 arg_type
, identifier
= arg
.rsplit('*', 1)
147 identifier
= identifier
.strip()
149 arg_type
, identifier
= arg
.rsplit(None, 1)
151 validate_type(arg_type
)
152 res
.append((arg_type
, identifier
))
153 return Arguments(res
)
155 def __getitem__(self
, index
):
156 if isinstance(index
, slice):
157 return Arguments(self
._args
[index
])
159 return self
._args
[index
]
162 """Iterate over the (type, name) pairs."""
163 return iter(self
._args
)
166 """Number of arguments."""
167 return len(self
._args
)
170 """String suitable for declaring function arguments."""
171 if len(self
._args
) == 0:
174 return ", ".join([ " ".join([t
, n
]) for t
,n
in self
._args
])
177 """Evaluable string representation for this object."""
178 return "Arguments(\"%s\")" % str(self
)
181 """List of argument names."""
182 return [ name
for _
, name
in self
._args
]
185 """List of argument types."""
186 return [ type_
for type_
, _
in self
._args
]
189 """List of argument names casted to their type."""
190 return ["(%s)%s" % (type_
, name
) for type_
, name
in self
._args
]
194 """Event description.
201 The event format string.
202 properties : set(str)
203 Properties of the event.
207 The line number in the input file.
209 The path to the input file.
213 _CRE
= re
.compile(r
"((?P<props>[\w\s]+)\s+)?"
215 r
"\((?P<args>[^)]*)\)"
217 r
"(?:(?:(?P<fmt_trans>\".+),)?\s
*(?P
<fmt
>\".+))?
"
220 _VALID_PROPS = set(["disable
", "vcpu
"])
222 def __init__(self, name, props, fmt, args, lineno, filename, orig=None,
223 event_trans=None, event_exec=None):
231 fmt : str, list of str
232 Event printing format string(s).
236 The line number in the input file.
238 The path to the input file.
240 Original Event before transformation/generation.
241 event_trans : Event or None
242 Generated translation-time event ("tcg
" property).
243 event_exec : Event or None
244 Generated execution-time event ("tcg
" property).
248 self.properties = props
251 self.lineno = int(lineno)
252 self.filename = str(filename)
253 self.event_trans = event_trans
254 self.event_exec = event_exec
257 raise ValueError("Event
'%s' has more than maximum permitted
"
258 "argument count
" % name)
261 self.original = weakref.ref(self)
265 unknown_props = set(self.properties) - self._VALID_PROPS
266 if len(unknown_props) > 0:
267 raise ValueError("Unknown properties
: %s"
268 % ", ".join(unknown_props))
269 assert isinstance(self.fmt, str) or len(self.fmt) == 2
272 """Create a new copy."""
273 return Event(self.name, list(self.properties), self.fmt,
274 self.args.copy(), self.lineno, self.filename,
275 self, self.event_trans, self.event_exec)
278 def build(line_str, lineno, filename):
279 """Build an Event instance from a string.
284 Line describing the event.
286 Line number in input file.
290 m = Event._CRE.match(line_str)
292 groups = m.groupdict('')
294 name = groups["name
"]
295 props = groups["props
"].split()
297 fmt_trans = groups["fmt_trans
"]
298 if fmt.find("%m
") != -1 or fmt_trans.find("%m
") != -1:
299 raise ValueError("Event format
'%m' is forbidden
, pass the error
"
300 "as an explicit trace argument
")
301 if fmt.endswith(r'\n"'):
302 raise ValueError("Event format must not end with a newline "
305 if len(fmt_trans) > 0:
306 fmt = [fmt_trans, fmt]
307 args = Arguments.build(groups["args"])
309 event = Event(name, props, fmt, args, lineno, filename)
311 # add implicit arguments when using the 'vcpu
' property
312 import tracetool.vcpu
313 event = tracetool.vcpu.transform_event(event)
318 """Evaluable string representation for this object."""
319 if isinstance(self.fmt, str):
322 fmt = "%s, %s" % (self.fmt[0], self.fmt[1])
323 return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
327 # Star matching on PRI is dangerous as one might have multiple
328 # arguments with that format, hence the non-greedy version of it.
329 _FMT = re.compile(r"(%[\d\.]*\w+|%.*?PRI\S+)")
332 """List conversion specifiers in the argument print format string."""
333 assert not isinstance(self.fmt, list)
334 return self._FMT.findall(self.fmt)
336 QEMU_TRACE = "trace_%(name)s"
337 QEMU_TRACE_NOCHECK = "_nocheck__" + QEMU_TRACE
338 QEMU_TRACE_TCG = QEMU_TRACE + "_tcg"
339 QEMU_DSTATE = "_TRACE_%(NAME)s_DSTATE"
340 QEMU_BACKEND_DSTATE = "TRACE_%(NAME)s_BACKEND_DSTATE"
341 QEMU_EVENT = "_TRACE_%(NAME)s_EVENT"
343 def api(self, fmt=None):
345 fmt = Event.QEMU_TRACE
346 return fmt % {"name": self.name, "NAME": self.name.upper()}
349 def read_events(fobj, fname):
350 """Generate the output for the given (format, backends) pair.
355 Event description file.
359 Returns a list of Event objects
363 for lineno, line in enumerate(fobj, 1):
365 raise ValueError("%s does not end with a new line" % fname)
368 if line.lstrip().startswith('#'):
372 event
= Event
.build(line
, lineno
, fname
)
373 except ValueError as e
:
374 arg0
= 'Error at %s:%d: %s' % (fname
, lineno
, e
.args
[0])
375 e
.args
= (arg0
,) + e
.args
[1:]
383 class TracetoolError (Exception):
384 """Exception for calls to generate."""
388 def try_import(mod_name
, attr_name
=None, attr_default
=None):
389 """Try to import a module and get an attribute from it.
395 attr_name : str, optional
396 Name of an attribute in the module.
397 attr_default : optional
398 Default value if the attribute does not exist in the module.
402 A pair indicating whether the module could be imported and the module or
403 object or attribute value.
406 module
= __import__(mod_name
, globals(), locals(), ["__package__"])
407 if attr_name
is None:
409 return True, getattr(module
, str(attr_name
), attr_default
)
414 def generate(events
, group
, format
, backends
,
415 binary
=None, probe_prefix
=None):
416 """Generate the output for the given (format, backends) pair.
421 list of Event objects to generate for
423 Name of the tracing group
427 Output backend names.
429 See tracetool.backend.dtrace.BINARY.
430 probe_prefix : str or None
431 See tracetool.backend.dtrace.PROBEPREFIX.
433 # fix strange python error (UnboundLocalError tracetool)
438 raise TracetoolError("format not set")
439 if not tracetool
.format
.exists(format
):
440 raise TracetoolError("unknown format: %s" % format
)
442 if len(backends
) == 0:
443 raise TracetoolError("no backends specified")
444 for backend
in backends
:
445 if not tracetool
.backend
.exists(backend
):
446 raise TracetoolError("unknown backend: %s" % backend
)
447 backend
= tracetool
.backend
.Wrapper(backends
, format
)
449 import tracetool
.backend
.dtrace
450 tracetool
.backend
.dtrace
.BINARY
= binary
451 tracetool
.backend
.dtrace
.PROBEPREFIX
= probe_prefix
453 tracetool
.format
.generate(events
, format
, backend
, group
)