2 # -*- coding: utf-8 -*-
5 Machinery for generating tracing-related intermediate files.
8 __author__
= "Lluís Vilanova <vilanova@ac.upc.edu>"
9 __copyright__
= "Copyright 2012-2017, Lluís Vilanova <vilanova@ac.upc.edu>"
10 __license__
= "GPL version 2 or (at your option) any later version"
12 __maintainer__
= "Stefan Hajnoczi"
13 __email__
= "stefanha@linux.vnet.ibm.com"
20 import tracetool
.format
21 import tracetool
.backend
22 import tracetool
.transform
25 def error_write(*lines
):
26 """Write a set of error lines."""
27 sys
.stderr
.writelines("\n".join(lines
) + "\n")
30 """Write a set of error lines and exit."""
35 def out(*lines
, **kwargs
):
36 """Write a set of output lines.
38 You can use kwargs as a shorthand for mapping variables when formating all
41 lines
= [ l
% kwargs
for l
in lines
]
42 sys
.stdout
.writelines("\n".join(lines
) + "\n")
44 # We only want to allow standard C types or fixed sized
45 # integer types. We don't want QEMU specific types
46 # as we can't assume trace backends can resolve all the
69 # Magic substitution is done by tracetool
73 def validate_type(name
):
74 bits
= name
.split(" ")
76 bit
= re
.sub("\*", "", bit
)
81 if bit
not in ALLOWED_TYPES
:
82 raise ValueError("Argument type '%s' is not in whitelist. "
83 "Only standard C types and fixed size integer "
84 "types should be used. struct, union, and "
85 "other complex pointer types should be "
86 "declared as 'void *'" % name
)
89 """Event arguments description."""
91 def __init__(self
, args
):
96 List of (type, name) tuples or Arguments objects.
100 if isinstance(arg
, Arguments
):
101 self
._args
.extend(arg
._args
)
103 self
._args
.append(arg
)
106 """Create a new copy."""
107 return Arguments(list(self
._args
))
111 """Build and Arguments instance from an argument string.
116 String describing the event arguments.
119 for arg
in arg_str
.split(","):
122 raise ValueError("Empty argument (did you forget to use 'void'?)")
127 arg_type
, identifier
= arg
.rsplit('*', 1)
129 identifier
= identifier
.strip()
131 arg_type
, identifier
= arg
.rsplit(None, 1)
133 validate_type(arg_type
)
134 res
.append((arg_type
, identifier
))
135 return Arguments(res
)
137 def __getitem__(self
, index
):
138 if isinstance(index
, slice):
139 return Arguments(self
._args
[index
])
141 return self
._args
[index
]
144 """Iterate over the (type, name) pairs."""
145 return iter(self
._args
)
148 """Number of arguments."""
149 return len(self
._args
)
152 """String suitable for declaring function arguments."""
153 if len(self
._args
) == 0:
156 return ", ".join([ " ".join([t
, n
]) for t
,n
in self
._args
])
159 """Evaluable string representation for this object."""
160 return "Arguments(\"%s\")" % str(self
)
163 """List of argument names."""
164 return [ name
for _
, name
in self
._args
]
167 """List of argument types."""
168 return [ type_
for type_
, _
in self
._args
]
171 """List of argument names casted to their type."""
172 return ["(%s)%s" % (type_
, name
) for type_
, name
in self
._args
]
174 def transform(self
, *trans
):
175 """Return a new Arguments instance with transformed types.
177 The types in the resulting Arguments instance are transformed according
178 to tracetool.transform.transform_type.
181 for type_
, name
in self
._args
:
182 res
.append((tracetool
.transform
.transform_type(type_
, *trans
),
184 return Arguments(res
)
188 """Event description.
195 The event format string.
196 properties : set(str)
197 Properties of the event.
203 _CRE
= re
.compile("((?P<props>[\w\s]+)\s+)?"
205 "\((?P<args>[^)]*)\)"
207 "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?"
210 _VALID_PROPS
= set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"])
212 def __init__(self
, name
, props
, fmt
, args
, orig
=None,
213 event_trans
=None, event_exec
=None):
221 fmt : str, list of str
222 Event printing format string(s).
226 Original Event before transformation/generation.
227 event_trans : Event or None
228 Generated translation-time event ("tcg" property).
229 event_exec : Event or None
230 Generated execution-time event ("tcg" property).
234 self
.properties
= props
237 self
.event_trans
= event_trans
238 self
.event_exec
= event_exec
241 raise ValueError("Event '%s' has more than maximum permitted "
242 "argument count" % name
)
245 self
.original
= weakref
.ref(self
)
249 unknown_props
= set(self
.properties
) - self
._VALID
_PROPS
250 if len(unknown_props
) > 0:
251 raise ValueError("Unknown properties: %s"
252 % ", ".join(unknown_props
))
253 assert isinstance(self
.fmt
, str) or len(self
.fmt
) == 2
256 """Create a new copy."""
257 return Event(self
.name
, list(self
.properties
), self
.fmt
,
258 self
.args
.copy(), self
, self
.event_trans
, self
.event_exec
)
262 """Build an Event instance from a string.
267 Line describing the event.
269 m
= Event
._CRE
.match(line_str
)
271 groups
= m
.groupdict('')
273 name
= groups
["name"]
274 props
= groups
["props"].split()
276 fmt_trans
= groups
["fmt_trans"]
277 if fmt
.find("%m") != -1 or fmt_trans
.find("%m") != -1:
278 raise ValueError("Event format '%m' is forbidden, pass the error "
279 "as an explicit trace argument")
280 if fmt
.endswith(r
'\n"'):
281 raise ValueError("Event format must not end with a newline "
284 if len(fmt_trans
) > 0:
285 fmt
= [fmt_trans
, fmt
]
286 args
= Arguments
.build(groups
["args"])
288 if "tcg-trans" in props
:
289 raise ValueError("Invalid property 'tcg-trans'")
290 if "tcg-exec" in props
:
291 raise ValueError("Invalid property 'tcg-exec'")
292 if "tcg" not in props
and not isinstance(fmt
, str):
293 raise ValueError("Only events with 'tcg' property can have two format strings")
294 if "tcg" in props
and isinstance(fmt
, str):
295 raise ValueError("Events with 'tcg' property must have two format strings")
297 event
= Event(name
, props
, fmt
, args
)
299 # add implicit arguments when using the 'vcpu' property
300 import tracetool
.vcpu
301 event
= tracetool
.vcpu
.transform_event(event
)
306 """Evaluable string representation for this object."""
307 if isinstance(self
.fmt
, str):
310 fmt
= "%s, %s" % (self
.fmt
[0], self
.fmt
[1])
311 return "Event('%s %s(%s) %s')" % (" ".join(self
.properties
),
315 # Star matching on PRI is dangerous as one might have multiple
316 # arguments with that format, hence the non-greedy version of it.
317 _FMT
= re
.compile("(%[\d\.]*\w+|%.*?PRI\S+)")
320 """List conversion specifiers in the argument print format string."""
321 assert not isinstance(self
.fmt
, list)
322 return self
._FMT
.findall(self
.fmt
)
324 QEMU_TRACE
= "trace_%(name)s"
325 QEMU_TRACE_NOCHECK
= "_nocheck__" + QEMU_TRACE
326 QEMU_TRACE_TCG
= QEMU_TRACE
+ "_tcg"
327 QEMU_DSTATE
= "_TRACE_%(NAME)s_DSTATE"
328 QEMU_BACKEND_DSTATE
= "TRACE_%(NAME)s_BACKEND_DSTATE"
329 QEMU_EVENT
= "_TRACE_%(NAME)s_EVENT"
331 def api(self
, fmt
=None):
333 fmt
= Event
.QEMU_TRACE
334 return fmt
% {"name": self
.name
, "NAME": self
.name
.upper()}
336 def transform(self
, *trans
):
337 """Return a new Event with transformed Arguments."""
338 return Event(self
.name
,
339 list(self
.properties
),
341 self
.args
.transform(*trans
),
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
)
369 except ValueError as e
:
370 arg0
= 'Error at %s:%d: %s' % (fname
, lineno
, e
.args
[0])
371 e
.args
= (arg0
,) + e
.args
[1:]
374 # transform TCG-enabled events
375 if "tcg" not in event
.properties
:
378 event_trans
= event
.copy()
379 event_trans
.name
+= "_trans"
380 event_trans
.properties
+= ["tcg-trans"]
381 event_trans
.fmt
= event
.fmt
[0]
382 # ignore TCG arguments
384 for atrans
, aorig
in zip(
385 event_trans
.transform(tracetool
.transform
.TCG_2_HOST
).args
,
388 args_trans
.append(atrans
)
389 event_trans
.args
= Arguments(args_trans
)
391 event_exec
= event
.copy()
392 event_exec
.name
+= "_exec"
393 event_exec
.properties
+= ["tcg-exec"]
394 event_exec
.fmt
= event
.fmt
[1]
395 event_exec
.args
= event_exec
.args
.transform(tracetool
.transform
.TCG_2_HOST
)
397 new_event
= [event_trans
, event_exec
]
398 event
.event_trans
, event
.event_exec
= new_event
400 events
.extend(new_event
)
405 class TracetoolError (Exception):
406 """Exception for calls to generate."""
410 def try_import(mod_name
, attr_name
=None, attr_default
=None):
411 """Try to import a module and get an attribute from it.
417 attr_name : str, optional
418 Name of an attribute in the module.
419 attr_default : optional
420 Default value if the attribute does not exist in the module.
424 A pair indicating whether the module could be imported and the module or
425 object or attribute value.
428 module
= __import__(mod_name
, globals(), locals(), ["__package__"])
429 if attr_name
is None:
431 return True, getattr(module
, str(attr_name
), attr_default
)
436 def generate(events
, group
, format
, backends
,
437 binary
=None, probe_prefix
=None):
438 """Generate the output for the given (format, backends) pair.
443 list of Event objects to generate for
445 Name of the tracing group
449 Output backend names.
451 See tracetool.backend.dtrace.BINARY.
452 probe_prefix : str or None
453 See tracetool.backend.dtrace.PROBEPREFIX.
455 # fix strange python error (UnboundLocalError tracetool)
460 raise TracetoolError("format not set")
461 if not tracetool
.format
.exists(format
):
462 raise TracetoolError("unknown format: %s" % format
)
464 if len(backends
) == 0:
465 raise TracetoolError("no backends specified")
466 for backend
in backends
:
467 if not tracetool
.backend
.exists(backend
):
468 raise TracetoolError("unknown backend: %s" % backend
)
469 backend
= tracetool
.backend
.Wrapper(backends
, format
)
471 import tracetool
.backend
.dtrace
472 tracetool
.backend
.dtrace
.BINARY
= binary
473 tracetool
.backend
.dtrace
.PROBEPREFIX
= probe_prefix
475 tracetool
.format
.generate(events
, format
, backend
, group
)