]>
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]>" | |
7d08f0da | 9 | __copyright__ = "Copyright 2012-2014, 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 | |
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 | ||
ad7443e4 LV |
55 | def copy(self): |
56 | """Create a new copy.""" | |
57 | return Arguments(list(self._args)) | |
58 | ||
650ab98d LV |
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() | |
b3ef0ade | 71 | if arg == 'void': |
650ab98d | 72 | continue |
b3ef0ade SH |
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)) | |
650ab98d LV |
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: | |
9c24a52e LV |
151 | raise ValueError("Unknown properties: %s" |
152 | % ", ".join(unknown_props)) | |
650ab98d | 153 | |
ad7443e4 LV |
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 | ||
650ab98d LV |
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 | ||
7d08f0da LV |
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 | ||
650ab98d LV |
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 | ||
9c24a52e | 210 | def try_import(mod_name, attr_name=None, attr_default=None): |
650ab98d LV |
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: | |
45d6c787 | 228 | module = __import__(mod_name, globals(), locals(), ["__package__"]) |
650ab98d LV |
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 | ||
52ef093a | 236 | def generate(fevents, format, backend, |
9c24a52e | 237 | binary=None, probe_prefix=None): |
650ab98d LV |
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. | |
52ef093a LV |
248 | binary : str or None |
249 | See tracetool.backend.dtrace.BINARY. | |
250 | probe_prefix : str or None | |
251 | See tracetool.backend.dtrace.PROBEPREFIX. | |
650ab98d LV |
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") | |
53158adc | 259 | if not tracetool.format.exists(format): |
650ab98d | 260 | raise TracetoolError("unknown format: %s" % format) |
53158adc | 261 | format = format.replace("-", "_") |
650ab98d LV |
262 | |
263 | backend = str(backend) | |
264 | if len(backend) is 0: | |
265 | raise TracetoolError("backend not set") | |
53158adc | 266 | if not tracetool.backend.exists(backend): |
650ab98d | 267 | raise TracetoolError("unknown backend: %s" % backend) |
53158adc | 268 | backend = backend.replace("-", "_") |
1dad2ce9 | 269 | backend = tracetool.backend.Wrapper(backend, format) |
650ab98d | 270 | |
52ef093a LV |
271 | import tracetool.backend.dtrace |
272 | tracetool.backend.dtrace.BINARY = binary | |
273 | tracetool.backend.dtrace.PROBEPREFIX = probe_prefix | |
274 | ||
650ab98d LV |
275 | events = _read_events(fevents) |
276 | ||
1dad2ce9 | 277 | tracetool.format.generate(events, format, backend) |