]> Git Repo - qemu.git/blob - scripts/tracetool/__init__.py
trace: [tracetool] Cosmetic changes
[qemu.git] / scripts / tracetool / __init__.py
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]>"
9 __copyright__  = "Copyright 2012-2014, Lluís Vilanova <[email protected]>"
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
18
19 import tracetool.format
20 import tracetool.backend
21
22
23 def error_write(*lines):
24     """Write a set of error lines."""
25     sys.stderr.writelines("\n".join(lines) + "\n")
26
27 def error(*lines):
28     """Write a set of error lines and exit."""
29     error_write(*lines)
30     sys.exit(1)
31
32
33 def out(*lines, **kwargs):
34     """Write a set of output lines.
35
36     You can use kwargs as a shorthand for mapping variables when formating all
37     the strings in lines.
38     """
39     lines = [ l % kwargs for l in lines ]
40     sys.stdout.writelines("\n".join(lines) + "\n")
41
42
43 class Arguments:
44     """Event arguments description."""
45
46     def __init__(self, args):
47         """
48         Parameters
49         ----------
50         args :
51             List of (type, name) tuples.
52         """
53         self._args = args
54
55     def copy(self):
56         """Create a new copy."""
57         return Arguments(list(self._args))
58
59     @staticmethod
60     def build(arg_str):
61         """Build and Arguments instance from an argument string.
62
63         Parameters
64         ----------
65         arg_str : str
66             String describing the event arguments.
67         """
68         res = []
69         for arg in arg_str.split(","):
70             arg = arg.strip()
71             if arg == 'void':
72                 continue
73
74             if '*' in arg:
75                 arg_type, identifier = arg.rsplit('*', 1)
76                 arg_type += '*'
77                 identifier = identifier.strip()
78             else:
79                 arg_type, identifier = arg.rsplit(None, 1)
80
81             res.append((arg_type, identifier))
82         return Arguments(res)
83
84     def __iter__(self):
85         """Iterate over the (type, name) pairs."""
86         return iter(self._args)
87
88     def __len__(self):
89         """Number of arguments."""
90         return len(self._args)
91
92     def __str__(self):
93         """String suitable for declaring function arguments."""
94         if len(self._args) == 0:
95             return "void"
96         else:
97             return ", ".join([ " ".join([t, n]) for t,n in self._args ])
98
99     def __repr__(self):
100         """Evaluable string representation for this object."""
101         return "Arguments(\"%s\")" % str(self)
102
103     def names(self):
104         """List of argument names."""
105         return [ name for _, name in self._args ]
106
107     def types(self):
108         """List of argument types."""
109         return [ type_ for type_, _ in self._args ]
110
111
112 class Event(object):
113     """Event description.
114
115     Attributes
116     ----------
117     name : str
118         The event name.
119     fmt : str
120         The event format string.
121     properties : set(str)
122         Properties of the event.
123     args : Arguments
124         The event arguments.
125     """
126
127     _CRE = re.compile("((?P<props>.*)\s+)?(?P<name>[^(\s]+)\((?P<args>[^)]*)\)\s*(?P<fmt>\".*)?")
128
129     _VALID_PROPS = set(["disable"])
130
131     def __init__(self, name, props, fmt, args):
132         """
133         Parameters
134         ----------
135         name : string
136             Event name.
137         props : list of str
138             Property names.
139         fmt : str
140             Event printing format.
141         args : Arguments
142             Event arguments.
143         """
144         self.name = name
145         self.properties = props
146         self.fmt = fmt
147         self.args = args
148
149         unknown_props = set(self.properties) - self._VALID_PROPS
150         if len(unknown_props) > 0:
151             raise ValueError("Unknown properties: %s"
152                              % ", ".join(unknown_props))
153
154     def copy(self):
155         """Create a new copy."""
156         return Event(self.name, list(self.properties), self.fmt,
157                      self.args.copy(), self)
158
159     @staticmethod
160     def build(line_str):
161         """Build an Event instance from a string.
162
163         Parameters
164         ----------
165         line_str : str
166             Line describing the event.
167         """
168         m = Event._CRE.match(line_str)
169         assert m is not None
170         groups = m.groupdict('')
171
172         name = groups["name"]
173         props = groups["props"].split()
174         fmt = groups["fmt"]
175         args = Arguments.build(groups["args"])
176
177         return Event(name, props, fmt, args)
178
179     def __repr__(self):
180         """Evaluable string representation for this object."""
181         return "Event('%s %s(%s) %s')" % (" ".join(self.properties),
182                                           self.name,
183                                           self.args,
184                                           self.fmt)
185
186     QEMU_TRACE               = "trace_%(name)s"
187
188     def api(self, fmt=None):
189         if fmt is None:
190             fmt = Event.QEMU_TRACE
191         return fmt % {"name": self.name}
192
193
194 def _read_events(fobj):
195     res = []
196     for line in fobj:
197         if not line.strip():
198             continue
199         if line.lstrip().startswith('#'):
200             continue
201         res.append(Event.build(line))
202     return res
203
204
205 class TracetoolError (Exception):
206     """Exception for calls to generate."""
207     pass
208
209
210 def try_import(mod_name, attr_name=None, attr_default=None):
211     """Try to import a module and get an attribute from it.
212
213     Parameters
214     ----------
215     mod_name : str
216         Module name.
217     attr_name : str, optional
218         Name of an attribute in the module.
219     attr_default : optional
220         Default value if the attribute does not exist in the module.
221
222     Returns
223     -------
224     A pair indicating whether the module could be imported and the module or
225     object or attribute value.
226     """
227     try:
228         module = __import__(mod_name, globals(), locals(), ["__package__"])
229         if attr_name is None:
230             return True, module
231         return True, getattr(module, str(attr_name), attr_default)
232     except ImportError:
233         return False, None
234
235
236 def generate(fevents, format, backend,
237              binary=None, probe_prefix=None):
238     """Generate the output for the given (format, backend) pair.
239
240     Parameters
241     ----------
242     fevents : file
243         Event description file.
244     format : str
245         Output format name.
246     backend : str
247         Output backend name.
248     binary : str or None
249         See tracetool.backend.dtrace.BINARY.
250     probe_prefix : str or None
251         See tracetool.backend.dtrace.PROBEPREFIX.
252     """
253     # fix strange python error (UnboundLocalError tracetool)
254     import tracetool
255
256     format = str(format)
257     if len(format) is 0:
258         raise TracetoolError("format not set")
259     if not tracetool.format.exists(format):
260         raise TracetoolError("unknown format: %s" % format)
261     format = format.replace("-", "_")
262
263     backend = str(backend)
264     if len(backend) is 0:
265         raise TracetoolError("backend not set")
266     if not tracetool.backend.exists(backend):
267         raise TracetoolError("unknown backend: %s" % backend)
268     backend = backend.replace("-", "_")
269
270     if not tracetool.backend.compatible(backend, format):
271         raise TracetoolError("backend '%s' not compatible with format '%s'" %
272                              (backend, format))
273
274     import tracetool.backend.dtrace
275     tracetool.backend.dtrace.BINARY = binary
276     tracetool.backend.dtrace.PROBEPREFIX = probe_prefix
277
278     events = _read_events(fevents)
279
280     if backend == "nop":
281         ( e.properies.add("disable") for e in events )
282
283     tracetool.format.generate_begin(format, events)
284     tracetool.backend.generate("nop", format,
285                                [ e
286                                  for e in events
287                                  if "disable" in e.properties ])
288     tracetool.backend.generate(backend, format,
289                                [ e
290                                  for e in events
291                                  if "disable" not in e.properties ])
292     tracetool.format.generate_end(format, events)
This page took 0.041289 seconds and 4 git commands to generate.