]>
Commit | Line | Data |
---|---|---|
650ab98d LV |
1 | #!/usr/bin/env python |
2 | # -*- coding: utf-8 -*- | |
3 | ||
4 | """ | |
5 | Machinery for generating tracing-related intermediate files. | |
6 | """ | |
7 | ||
8 | __author__ = "Lluís Vilanova <[email protected]>" | |
864a2178 | 9 | __copyright__ = "Copyright 2012-2017, Lluís Vilanova <[email protected]>" |
650ab98d LV |
10 | __license__ = "GPL version 2 or (at your option) any later version" |
11 | ||
12 | __maintainer__ = "Stefan Hajnoczi" | |
13 | __email__ = "[email protected]" | |
14 | ||
15 | ||
16 | import re | |
17 | import sys | |
b55835ac | 18 | import weakref |
650ab98d LV |
19 | |
20 | import tracetool.format | |
21 | import tracetool.backend | |
b2b36c22 | 22 | import tracetool.transform |
650ab98d LV |
23 | |
24 | ||
25 | def error_write(*lines): | |
26 | """Write a set of error lines.""" | |
27 | sys.stderr.writelines("\n".join(lines) + "\n") | |
28 | ||
29 | def error(*lines): | |
30 | """Write a set of error lines and exit.""" | |
31 | error_write(*lines) | |
32 | sys.exit(1) | |
33 | ||
34 | ||
35 | def out(*lines, **kwargs): | |
36 | """Write a set of output lines. | |
37 | ||
38 | You can use kwargs as a shorthand for mapping variables when formating all | |
39 | the strings in lines. | |
40 | """ | |
41 | lines = [ l % kwargs for l in lines ] | |
42 | sys.stdout.writelines("\n".join(lines) + "\n") | |
43 | ||
44 | ||
45 | class Arguments: | |
46 | """Event arguments description.""" | |
47 | ||
48 | def __init__(self, args): | |
49 | """ | |
50 | Parameters | |
51 | ---------- | |
52 | args : | |
3596f524 | 53 | List of (type, name) tuples or Arguments objects. |
650ab98d | 54 | """ |
3596f524 LV |
55 | self._args = [] |
56 | for arg in args: | |
57 | if isinstance(arg, Arguments): | |
58 | self._args.extend(arg._args) | |
59 | else: | |
60 | self._args.append(arg) | |
650ab98d | 61 | |
ad7443e4 LV |
62 | def copy(self): |
63 | """Create a new copy.""" | |
64 | return Arguments(list(self._args)) | |
65 | ||
650ab98d LV |
66 | @staticmethod |
67 | def build(arg_str): | |
68 | """Build and Arguments instance from an argument string. | |
69 | ||
70 | Parameters | |
71 | ---------- | |
72 | arg_str : str | |
73 | String describing the event arguments. | |
74 | """ | |
75 | res = [] | |
76 | for arg in arg_str.split(","): | |
77 | arg = arg.strip() | |
24f4d3d3 SH |
78 | if not arg: |
79 | raise ValueError("Empty argument (did you forget to use 'void'?)") | |
b3ef0ade | 80 | if arg == 'void': |
650ab98d | 81 | continue |
b3ef0ade SH |
82 | |
83 | if '*' in arg: | |
84 | arg_type, identifier = arg.rsplit('*', 1) | |
85 | arg_type += '*' | |
86 | identifier = identifier.strip() | |
87 | else: | |
88 | arg_type, identifier = arg.rsplit(None, 1) | |
89 | ||
90 | res.append((arg_type, identifier)) | |
650ab98d LV |
91 | return Arguments(res) |
92 | ||
3596f524 LV |
93 | def __getitem__(self, index): |
94 | if isinstance(index, slice): | |
95 | return Arguments(self._args[index]) | |
96 | else: | |
97 | return self._args[index] | |
98 | ||
650ab98d LV |
99 | def __iter__(self): |
100 | """Iterate over the (type, name) pairs.""" | |
101 | return iter(self._args) | |
102 | ||
103 | def __len__(self): | |
104 | """Number of arguments.""" | |
105 | return len(self._args) | |
106 | ||
107 | def __str__(self): | |
108 | """String suitable for declaring function arguments.""" | |
109 | if len(self._args) == 0: | |
110 | return "void" | |
111 | else: | |
112 | return ", ".join([ " ".join([t, n]) for t,n in self._args ]) | |
113 | ||
114 | def __repr__(self): | |
115 | """Evaluable string representation for this object.""" | |
116 | return "Arguments(\"%s\")" % str(self) | |
117 | ||
118 | def names(self): | |
119 | """List of argument names.""" | |
120 | return [ name for _, name in self._args ] | |
121 | ||
122 | def types(self): | |
123 | """List of argument types.""" | |
124 | return [ type_ for type_, _ in self._args ] | |
125 | ||
bc9beb47 LV |
126 | def casted(self): |
127 | """List of argument names casted to their type.""" | |
128 | return ["(%s)%s" % (type_, name) for type_, name in self._args] | |
129 | ||
b55835ac LV |
130 | def transform(self, *trans): |
131 | """Return a new Arguments instance with transformed types. | |
132 | ||
133 | The types in the resulting Arguments instance are transformed according | |
134 | to tracetool.transform.transform_type. | |
135 | """ | |
136 | res = [] | |
137 | for type_, name in self._args: | |
138 | res.append((tracetool.transform.transform_type(type_, *trans), | |
139 | name)) | |
140 | return Arguments(res) | |
141 | ||
650ab98d LV |
142 | |
143 | class Event(object): | |
144 | """Event description. | |
145 | ||
146 | Attributes | |
147 | ---------- | |
148 | name : str | |
149 | The event name. | |
150 | fmt : str | |
151 | The event format string. | |
152 | properties : set(str) | |
153 | Properties of the event. | |
154 | args : Arguments | |
155 | The event arguments. | |
23214429 | 156 | |
650ab98d LV |
157 | """ |
158 | ||
f9bbba95 SH |
159 | _CRE = re.compile("((?P<props>[\w\s]+)\s+)?" |
160 | "(?P<name>\w+)" | |
b2b36c22 LV |
161 | "\((?P<args>[^)]*)\)" |
162 | "\s*" | |
163 | "(?:(?:(?P<fmt_trans>\".+),)?\s*(?P<fmt>\".+))?" | |
164 | "\s*") | |
650ab98d | 165 | |
3d211d9f | 166 | _VALID_PROPS = set(["disable", "tcg", "tcg-trans", "tcg-exec", "vcpu"]) |
650ab98d | 167 | |
4ade0541 LV |
168 | def __init__(self, name, props, fmt, args, orig=None, |
169 | event_trans=None, event_exec=None): | |
650ab98d LV |
170 | """ |
171 | Parameters | |
172 | ---------- | |
173 | name : string | |
174 | Event name. | |
175 | props : list of str | |
176 | Property names. | |
b2b36c22 | 177 | fmt : str, list of str |
6e497fa1 | 178 | Event printing format string(s). |
650ab98d LV |
179 | args : Arguments |
180 | Event arguments. | |
b55835ac | 181 | orig : Event or None |
4ade0541 LV |
182 | Original Event before transformation/generation. |
183 | event_trans : Event or None | |
184 | Generated translation-time event ("tcg" property). | |
185 | event_exec : Event or None | |
186 | Generated execution-time event ("tcg" property). | |
41ef7b00 | 187 | |
650ab98d LV |
188 | """ |
189 | self.name = name | |
190 | self.properties = props | |
191 | self.fmt = fmt | |
192 | self.args = args | |
4ade0541 LV |
193 | self.event_trans = event_trans |
194 | self.event_exec = event_exec | |
650ab98d | 195 | |
f3fddaf6 DB |
196 | if len(args) > 10: |
197 | raise ValueError("Event '%s' has more than maximum permitted " | |
198 | "argument count" % name) | |
199 | ||
b55835ac LV |
200 | if orig is None: |
201 | self.original = weakref.ref(self) | |
202 | else: | |
203 | self.original = orig | |
204 | ||
650ab98d LV |
205 | unknown_props = set(self.properties) - self._VALID_PROPS |
206 | if len(unknown_props) > 0: | |
9c24a52e LV |
207 | raise ValueError("Unknown properties: %s" |
208 | % ", ".join(unknown_props)) | |
b2b36c22 | 209 | assert isinstance(self.fmt, str) or len(self.fmt) == 2 |
650ab98d | 210 | |
ad7443e4 LV |
211 | def copy(self): |
212 | """Create a new copy.""" | |
213 | return Event(self.name, list(self.properties), self.fmt, | |
4ade0541 | 214 | self.args.copy(), self, self.event_trans, self.event_exec) |
ad7443e4 | 215 | |
650ab98d LV |
216 | @staticmethod |
217 | def build(line_str): | |
218 | """Build an Event instance from a string. | |
219 | ||
220 | Parameters | |
221 | ---------- | |
222 | line_str : str | |
223 | Line describing the event. | |
224 | """ | |
225 | m = Event._CRE.match(line_str) | |
226 | assert m is not None | |
227 | groups = m.groupdict('') | |
228 | ||
229 | name = groups["name"] | |
230 | props = groups["props"].split() | |
231 | fmt = groups["fmt"] | |
b2b36c22 LV |
232 | fmt_trans = groups["fmt_trans"] |
233 | if len(fmt_trans) > 0: | |
234 | fmt = [fmt_trans, fmt] | |
650ab98d LV |
235 | args = Arguments.build(groups["args"]) |
236 | ||
b2b36c22 LV |
237 | if "tcg-trans" in props: |
238 | raise ValueError("Invalid property 'tcg-trans'") | |
239 | if "tcg-exec" in props: | |
240 | raise ValueError("Invalid property 'tcg-exec'") | |
241 | if "tcg" not in props and not isinstance(fmt, str): | |
6e497fa1 | 242 | raise ValueError("Only events with 'tcg' property can have two format strings") |
b2b36c22 | 243 | if "tcg" in props and isinstance(fmt, str): |
6e497fa1 | 244 | raise ValueError("Events with 'tcg' property must have two format strings") |
b2b36c22 | 245 | |
3d211d9f LV |
246 | event = Event(name, props, fmt, args) |
247 | ||
248 | # add implicit arguments when using the 'vcpu' property | |
249 | import tracetool.vcpu | |
250 | event = tracetool.vcpu.transform_event(event) | |
251 | ||
252 | return event | |
650ab98d LV |
253 | |
254 | def __repr__(self): | |
255 | """Evaluable string representation for this object.""" | |
b2b36c22 LV |
256 | if isinstance(self.fmt, str): |
257 | fmt = self.fmt | |
258 | else: | |
259 | fmt = "%s, %s" % (self.fmt[0], self.fmt[1]) | |
650ab98d LV |
260 | return "Event('%s %s(%s) %s')" % (" ".join(self.properties), |
261 | self.name, | |
262 | self.args, | |
b2b36c22 | 263 | fmt) |
fb1a66bc JEJ |
264 | # Star matching on PRI is dangerous as one might have multiple |
265 | # arguments with that format, hence the non-greedy version of it. | |
266 | _FMT = re.compile("(%[\d\.]*\w+|%.*?PRI\S+)") | |
23214429 LV |
267 | |
268 | def formats(self): | |
6e497fa1 | 269 | """List conversion specifiers in the argument print format string.""" |
23214429 LV |
270 | assert not isinstance(self.fmt, list) |
271 | return self._FMT.findall(self.fmt) | |
272 | ||
7d08f0da | 273 | QEMU_TRACE = "trace_%(name)s" |
864a2178 | 274 | QEMU_TRACE_NOCHECK = "_nocheck__" + QEMU_TRACE |
707c8a98 | 275 | QEMU_TRACE_TCG = QEMU_TRACE + "_tcg" |
93977402 | 276 | QEMU_DSTATE = "_TRACE_%(NAME)s_DSTATE" |
3932ef3f | 277 | QEMU_BACKEND_DSTATE = "TRACE_%(NAME)s_BACKEND_DSTATE" |
79218be4 | 278 | QEMU_EVENT = "_TRACE_%(NAME)s_EVENT" |
7d08f0da LV |
279 | |
280 | def api(self, fmt=None): | |
281 | if fmt is None: | |
282 | fmt = Event.QEMU_TRACE | |
93977402 | 283 | return fmt % {"name": self.name, "NAME": self.name.upper()} |
7d08f0da | 284 | |
b55835ac LV |
285 | def transform(self, *trans): |
286 | """Return a new Event with transformed Arguments.""" | |
287 | return Event(self.name, | |
288 | list(self.properties), | |
289 | self.fmt, | |
290 | self.args.transform(*trans), | |
291 | self) | |
292 | ||
7d08f0da | 293 | |
86b5aacf | 294 | def read_events(fobj, fname): |
d1b97bce DB |
295 | """Generate the output for the given (format, backends) pair. |
296 | ||
297 | Parameters | |
298 | ---------- | |
299 | fobj : file | |
300 | Event description file. | |
86b5aacf DB |
301 | fname : str |
302 | Name of event file | |
d1b97bce DB |
303 | |
304 | Returns a list of Event objects | |
305 | """ | |
306 | ||
776ec96f | 307 | events = [] |
5069b561 | 308 | for lineno, line in enumerate(fobj, 1): |
650ab98d LV |
309 | if not line.strip(): |
310 | continue | |
311 | if line.lstrip().startswith('#'): | |
312 | continue | |
776ec96f | 313 | |
5069b561 SH |
314 | try: |
315 | event = Event.build(line) | |
316 | except ValueError as e: | |
86b5aacf | 317 | arg0 = 'Error at %s:%d: %s' % (fname, lineno, e.args[0]) |
5069b561 SH |
318 | e.args = (arg0,) + e.args[1:] |
319 | raise | |
776ec96f CS |
320 | |
321 | # transform TCG-enabled events | |
322 | if "tcg" not in event.properties: | |
323 | events.append(event) | |
324 | else: | |
325 | event_trans = event.copy() | |
326 | event_trans.name += "_trans" | |
327 | event_trans.properties += ["tcg-trans"] | |
328 | event_trans.fmt = event.fmt[0] | |
3d211d9f | 329 | # ignore TCG arguments |
776ec96f CS |
330 | args_trans = [] |
331 | for atrans, aorig in zip( | |
332 | event_trans.transform(tracetool.transform.TCG_2_HOST).args, | |
333 | event.args): | |
334 | if atrans == aorig: | |
335 | args_trans.append(atrans) | |
336 | event_trans.args = Arguments(args_trans) | |
776ec96f CS |
337 | |
338 | event_exec = event.copy() | |
339 | event_exec.name += "_exec" | |
340 | event_exec.properties += ["tcg-exec"] | |
341 | event_exec.fmt = event.fmt[1] | |
56797b1f | 342 | event_exec.args = event_exec.args.transform(tracetool.transform.TCG_2_HOST) |
776ec96f CS |
343 | |
344 | new_event = [event_trans, event_exec] | |
345 | event.event_trans, event.event_exec = new_event | |
346 | ||
347 | events.extend(new_event) | |
348 | ||
349 | return events | |
650ab98d LV |
350 | |
351 | ||
352 | class TracetoolError (Exception): | |
353 | """Exception for calls to generate.""" | |
354 | pass | |
355 | ||
356 | ||
9c24a52e | 357 | def try_import(mod_name, attr_name=None, attr_default=None): |
650ab98d LV |
358 | """Try to import a module and get an attribute from it. |
359 | ||
360 | Parameters | |
361 | ---------- | |
362 | mod_name : str | |
363 | Module name. | |
364 | attr_name : str, optional | |
365 | Name of an attribute in the module. | |
366 | attr_default : optional | |
367 | Default value if the attribute does not exist in the module. | |
368 | ||
369 | Returns | |
370 | ------- | |
371 | A pair indicating whether the module could be imported and the module or | |
372 | object or attribute value. | |
373 | """ | |
374 | try: | |
45d6c787 | 375 | module = __import__(mod_name, globals(), locals(), ["__package__"]) |
650ab98d LV |
376 | if attr_name is None: |
377 | return True, module | |
378 | return True, getattr(module, str(attr_name), attr_default) | |
379 | except ImportError: | |
380 | return False, None | |
381 | ||
382 | ||
80dd5c49 | 383 | def generate(events, group, format, backends, |
9c24a52e | 384 | binary=None, probe_prefix=None): |
5b808275 | 385 | """Generate the output for the given (format, backends) pair. |
650ab98d LV |
386 | |
387 | Parameters | |
388 | ---------- | |
9096b78a DB |
389 | events : list |
390 | list of Event objects to generate for | |
80dd5c49 DB |
391 | group: str |
392 | Name of the tracing group | |
650ab98d LV |
393 | format : str |
394 | Output format name. | |
5b808275 LV |
395 | backends : list |
396 | Output backend names. | |
52ef093a LV |
397 | binary : str or None |
398 | See tracetool.backend.dtrace.BINARY. | |
399 | probe_prefix : str or None | |
400 | See tracetool.backend.dtrace.PROBEPREFIX. | |
650ab98d LV |
401 | """ |
402 | # fix strange python error (UnboundLocalError tracetool) | |
403 | import tracetool | |
404 | ||
405 | format = str(format) | |
406 | if len(format) is 0: | |
407 | raise TracetoolError("format not set") | |
53158adc | 408 | if not tracetool.format.exists(format): |
650ab98d | 409 | raise TracetoolError("unknown format: %s" % format) |
5b808275 LV |
410 | |
411 | if len(backends) is 0: | |
412 | raise TracetoolError("no backends specified") | |
413 | for backend in backends: | |
414 | if not tracetool.backend.exists(backend): | |
415 | raise TracetoolError("unknown backend: %s" % backend) | |
416 | backend = tracetool.backend.Wrapper(backends, format) | |
650ab98d | 417 | |
52ef093a LV |
418 | import tracetool.backend.dtrace |
419 | tracetool.backend.dtrace.BINARY = binary | |
420 | tracetool.backend.dtrace.PROBEPREFIX = probe_prefix | |
421 | ||
80dd5c49 | 422 | tracetool.format.generate(events, format, backend, group) |