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 raise ValueError("Event format must not use new line character")
307 if len(fmt_trans) > 0:
308 fmt = [fmt_trans, fmt]
309 args = Arguments.build(groups["args"])
311 return Event(name, props, fmt, args, lineno, filename)
314 """Evaluable string representation for this object."""
315 if isinstance(self.fmt, str):
318 fmt = "%s, %s" % (self.fmt[0], self.fmt[1])
319 return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
323 # Star matching on PRI is dangerous as one might have multiple
324 # arguments with that format, hence the non-greedy version of it.
325 _FMT = re.compile(r"(%[\d\.]*\w+|%.*?PRI\S+)")
328 """List conversion specifiers in the argument print format string."""
329 assert not isinstance(self.fmt, list)
330 return self._FMT.findall(self.fmt)
332 QEMU_TRACE = "trace_%(name)s"
333 QEMU_TRACE_NOCHECK = "_nocheck__" + QEMU_TRACE
334 QEMU_TRACE_TCG = QEMU_TRACE + "_tcg"
335 QEMU_DSTATE = "_TRACE_%(NAME)s_DSTATE"
336 QEMU_BACKEND_DSTATE = "TRACE_%(NAME)s_BACKEND_DSTATE"
337 QEMU_EVENT = "_TRACE_%(NAME)s_EVENT"
339 def api(self, fmt=None):
341 fmt = Event.QEMU_TRACE
342 return fmt % {"name": self.name, "NAME": self.name.upper()}
345 def read_events(fobj, fname):
346 """Generate the output for the given (format, backends) pair.
351 Event description file.
355 Returns a list of Event objects
359 for lineno, line in enumerate(fobj, 1):
361 raise ValueError("%s does not end with a new line" % fname)
364 if line.lstrip().startswith('#'):
368 event
= Event
.build(line
, lineno
, fname
)
369 except ValueError as e
:
370 arg0
= 'Error at %s:%d: %s' % (fname
, lineno
, e
.args
[0])
371 e
.args
= (arg0
,) + e
.args
[1:]
379 class TracetoolError (Exception):
380 """Exception for calls to generate."""
384 def try_import(mod_name
, attr_name
=None, attr_default
=None):
385 """Try to import a module and get an attribute from it.
391 attr_name : str, optional
392 Name of an attribute in the module.
393 attr_default : optional
394 Default value if the attribute does not exist in the module.
398 A pair indicating whether the module could be imported and the module or
399 object or attribute value.
402 module
= __import__(mod_name
, globals(), locals(), ["__package__"])
403 if attr_name
is None:
405 return True, getattr(module
, str(attr_name
), attr_default
)
410 def generate(events
, group
, format
, backends
,
411 binary
=None, probe_prefix
=None):
412 """Generate the output for the given (format, backends) pair.
417 list of Event objects to generate for
419 Name of the tracing group
423 Output backend names.
425 See tracetool.backend.dtrace.BINARY.
426 probe_prefix : str or None
427 See tracetool.backend.dtrace.PROBEPREFIX.
429 # fix strange python error (UnboundLocalError tracetool)
434 raise TracetoolError("format not set")
435 if not tracetool
.format
.exists(format
):
436 raise TracetoolError("unknown format: %s" % format
)
438 if len(backends
) == 0:
439 raise TracetoolError("no backends specified")
440 for backend
in backends
:
441 if not tracetool
.backend
.exists(backend
):
442 raise TracetoolError("unknown backend: %s" % backend
)
443 backend
= tracetool
.backend
.Wrapper(backends
, format
)
445 import tracetool
.backend
.dtrace
446 tracetool
.backend
.dtrace
.BINARY
= binary
447 tracetool
.backend
.dtrace
.PROBEPREFIX
= probe_prefix
449 tracetool
.format
.generate(events
, format
, backend
, group
)