trace: [tcg] Argument type transformation machinery
[qemu.git] / scripts / tracetool / __init__.py
blobbd3fd85d7842aee986ebcc2bd0ed2d3989d0836b
1 #!/usr/bin/env python
2 # -*- coding: utf-8 -*-
4 """
5 Machinery for generating tracing-related intermediate files.
6 """
8 __author__ = "Lluís Vilanova <vilanova@ac.upc.edu>"
9 __copyright__ = "Copyright 2012-2014, 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"
16 import re
17 import sys
18 import weakref
20 import tracetool.format
21 import tracetool.backend
24 def error_write(*lines):
25 """Write a set of error lines."""
26 sys.stderr.writelines("\n".join(lines) + "\n")
28 def error(*lines):
29 """Write a set of error lines and exit."""
30 error_write(*lines)
31 sys.exit(1)
34 def out(*lines, **kwargs):
35 """Write a set of output lines.
37 You can use kwargs as a shorthand for mapping variables when formating all
38 the strings in lines.
39 """
40 lines = [ l % kwargs for l in lines ]
41 sys.stdout.writelines("\n".join(lines) + "\n")
44 class Arguments:
45 """Event arguments description."""
47 def __init__(self, args):
48 """
49 Parameters
50 ----------
51 args :
52 List of (type, name) tuples.
53 """
54 self._args = args
56 def copy(self):
57 """Create a new copy."""
58 return Arguments(list(self._args))
60 @staticmethod
61 def build(arg_str):
62 """Build and Arguments instance from an argument string.
64 Parameters
65 ----------
66 arg_str : str
67 String describing the event arguments.
68 """
69 res = []
70 for arg in arg_str.split(","):
71 arg = arg.strip()
72 if arg == 'void':
73 continue
75 if '*' in arg:
76 arg_type, identifier = arg.rsplit('*', 1)
77 arg_type += '*'
78 identifier = identifier.strip()
79 else:
80 arg_type, identifier = arg.rsplit(None, 1)
82 res.append((arg_type, identifier))
83 return Arguments(res)
85 def __iter__(self):
86 """Iterate over the (type, name) pairs."""
87 return iter(self._args)
89 def __len__(self):
90 """Number of arguments."""
91 return len(self._args)
93 def __str__(self):
94 """String suitable for declaring function arguments."""
95 if len(self._args) == 0:
96 return "void"
97 else:
98 return ", ".join([ " ".join([t, n]) for t,n in self._args ])
100 def __repr__(self):
101 """Evaluable string representation for this object."""
102 return "Arguments(\"%s\")" % str(self)
104 def names(self):
105 """List of argument names."""
106 return [ name for _, name in self._args ]
108 def types(self):
109 """List of argument types."""
110 return [ type_ for type_, _ in self._args ]
112 def transform(self, *trans):
113 """Return a new Arguments instance with transformed types.
115 The types in the resulting Arguments instance are transformed according
116 to tracetool.transform.transform_type.
118 res = []
119 for type_, name in self._args:
120 res.append((tracetool.transform.transform_type(type_, *trans),
121 name))
122 return Arguments(res)
125 class Event(object):
126 """Event description.
128 Attributes
129 ----------
130 name : str
131 The event name.
132 fmt : str
133 The event format string.
134 properties : set(str)
135 Properties of the event.
136 args : Arguments
137 The event arguments.
140 _CRE = re.compile("((?P<props>.*)\s+)?(?P<name>[^(\s]+)\((?P<args>[^)]*)\)\s*(?P<fmt>\".*)?")
142 _VALID_PROPS = set(["disable"])
144 def __init__(self, name, props, fmt, args, orig=None):
146 Parameters
147 ----------
148 name : string
149 Event name.
150 props : list of str
151 Property names.
152 fmt : str
153 Event printing format.
154 args : Arguments
155 Event arguments.
156 orig : Event or None
157 Original Event before transformation.
159 self.name = name
160 self.properties = props
161 self.fmt = fmt
162 self.args = args
164 if orig is None:
165 self.original = weakref.ref(self)
166 else:
167 self.original = orig
169 unknown_props = set(self.properties) - self._VALID_PROPS
170 if len(unknown_props) > 0:
171 raise ValueError("Unknown properties: %s"
172 % ", ".join(unknown_props))
174 def copy(self):
175 """Create a new copy."""
176 return Event(self.name, list(self.properties), self.fmt,
177 self.args.copy(), self)
179 @staticmethod
180 def build(line_str):
181 """Build an Event instance from a string.
183 Parameters
184 ----------
185 line_str : str
186 Line describing the event.
188 m = Event._CRE.match(line_str)
189 assert m is not None
190 groups = m.groupdict('')
192 name = groups["name"]
193 props = groups["props"].split()
194 fmt = groups["fmt"]
195 args = Arguments.build(groups["args"])
197 return Event(name, props, fmt, args)
199 def __repr__(self):
200 """Evaluable string representation for this object."""
201 return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
202 self.name,
203 self.args,
204 self.fmt)
206 QEMU_TRACE = "trace_%(name)s"
208 def api(self, fmt=None):
209 if fmt is None:
210 fmt = Event.QEMU_TRACE
211 return fmt % {"name": self.name}
213 def transform(self, *trans):
214 """Return a new Event with transformed Arguments."""
215 return Event(self.name,
216 list(self.properties),
217 self.fmt,
218 self.args.transform(*trans),
219 self)
222 def _read_events(fobj):
223 res = []
224 for line in fobj:
225 if not line.strip():
226 continue
227 if line.lstrip().startswith('#'):
228 continue
229 res.append(Event.build(line))
230 return res
233 class TracetoolError (Exception):
234 """Exception for calls to generate."""
235 pass
238 def try_import(mod_name, attr_name=None, attr_default=None):
239 """Try to import a module and get an attribute from it.
241 Parameters
242 ----------
243 mod_name : str
244 Module name.
245 attr_name : str, optional
246 Name of an attribute in the module.
247 attr_default : optional
248 Default value if the attribute does not exist in the module.
250 Returns
251 -------
252 A pair indicating whether the module could be imported and the module or
253 object or attribute value.
255 try:
256 module = __import__(mod_name, globals(), locals(), ["__package__"])
257 if attr_name is None:
258 return True, module
259 return True, getattr(module, str(attr_name), attr_default)
260 except ImportError:
261 return False, None
264 def generate(fevents, format, backends,
265 binary=None, probe_prefix=None):
266 """Generate the output for the given (format, backends) pair.
268 Parameters
269 ----------
270 fevents : file
271 Event description file.
272 format : str
273 Output format name.
274 backends : list
275 Output backend names.
276 binary : str or None
277 See tracetool.backend.dtrace.BINARY.
278 probe_prefix : str or None
279 See tracetool.backend.dtrace.PROBEPREFIX.
281 # fix strange python error (UnboundLocalError tracetool)
282 import tracetool
284 format = str(format)
285 if len(format) is 0:
286 raise TracetoolError("format not set")
287 if not tracetool.format.exists(format):
288 raise TracetoolError("unknown format: %s" % format)
290 if len(backends) is 0:
291 raise TracetoolError("no backends specified")
292 for backend in backends:
293 if not tracetool.backend.exists(backend):
294 raise TracetoolError("unknown backend: %s" % backend)
295 backend = tracetool.backend.Wrapper(backends, format)
297 import tracetool.backend.dtrace
298 tracetool.backend.dtrace.BINARY = binary
299 tracetool.backend.dtrace.PROBEPREFIX = probe_prefix
301 events = _read_events(fevents)
303 tracetool.format.generate(events, format, backend)