]>
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]>" | |
9 | __copyright__ = "Copyright 2012, 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 | @staticmethod | |
56 | def build(arg_str): | |
57 | """Build and Arguments instance from an argument string. | |
58 | ||
59 | Parameters | |
60 | ---------- | |
61 | arg_str : str | |
62 | String describing the event arguments. | |
63 | """ | |
64 | res = [] | |
65 | for arg in arg_str.split(","): | |
66 | arg = arg.strip() | |
b3ef0ade | 67 | if arg == 'void': |
650ab98d | 68 | continue |
b3ef0ade SH |
69 | |
70 | if '*' in arg: | |
71 | arg_type, identifier = arg.rsplit('*', 1) | |
72 | arg_type += '*' | |
73 | identifier = identifier.strip() | |
74 | else: | |
75 | arg_type, identifier = arg.rsplit(None, 1) | |
76 | ||
77 | res.append((arg_type, identifier)) | |
650ab98d LV |
78 | return Arguments(res) |
79 | ||
80 | def __iter__(self): | |
81 | """Iterate over the (type, name) pairs.""" | |
82 | return iter(self._args) | |
83 | ||
84 | def __len__(self): | |
85 | """Number of arguments.""" | |
86 | return len(self._args) | |
87 | ||
88 | def __str__(self): | |
89 | """String suitable for declaring function arguments.""" | |
90 | if len(self._args) == 0: | |
91 | return "void" | |
92 | else: | |
93 | return ", ".join([ " ".join([t, n]) for t,n in self._args ]) | |
94 | ||
95 | def __repr__(self): | |
96 | """Evaluable string representation for this object.""" | |
97 | return "Arguments(\"%s\")" % str(self) | |
98 | ||
99 | def names(self): | |
100 | """List of argument names.""" | |
101 | return [ name for _, name in self._args ] | |
102 | ||
103 | def types(self): | |
104 | """List of argument types.""" | |
105 | return [ type_ for type_, _ in self._args ] | |
106 | ||
107 | ||
108 | class Event(object): | |
109 | """Event description. | |
110 | ||
111 | Attributes | |
112 | ---------- | |
113 | name : str | |
114 | The event name. | |
115 | fmt : str | |
116 | The event format string. | |
117 | properties : set(str) | |
118 | Properties of the event. | |
119 | args : Arguments | |
120 | The event arguments. | |
121 | """ | |
122 | ||
123 | _CRE = re.compile("((?P<props>.*)\s+)?(?P<name>[^(\s]+)\((?P<args>[^)]*)\)\s*(?P<fmt>\".*)?") | |
124 | ||
125 | _VALID_PROPS = set(["disable"]) | |
126 | ||
127 | def __init__(self, name, props, fmt, args): | |
128 | """ | |
129 | Parameters | |
130 | ---------- | |
131 | name : string | |
132 | Event name. | |
133 | props : list of str | |
134 | Property names. | |
135 | fmt : str | |
136 | Event printing format. | |
137 | args : Arguments | |
138 | Event arguments. | |
139 | """ | |
140 | self.name = name | |
141 | self.properties = props | |
142 | self.fmt = fmt | |
143 | self.args = args | |
144 | ||
145 | unknown_props = set(self.properties) - self._VALID_PROPS | |
146 | if len(unknown_props) > 0: | |
147 | raise ValueError("Unknown properties: %s" % ", ".join(unknown_props)) | |
148 | ||
149 | @staticmethod | |
150 | def build(line_str): | |
151 | """Build an Event instance from a string. | |
152 | ||
153 | Parameters | |
154 | ---------- | |
155 | line_str : str | |
156 | Line describing the event. | |
157 | """ | |
158 | m = Event._CRE.match(line_str) | |
159 | assert m is not None | |
160 | groups = m.groupdict('') | |
161 | ||
162 | name = groups["name"] | |
163 | props = groups["props"].split() | |
164 | fmt = groups["fmt"] | |
165 | args = Arguments.build(groups["args"]) | |
166 | ||
167 | return Event(name, props, fmt, args) | |
168 | ||
169 | def __repr__(self): | |
170 | """Evaluable string representation for this object.""" | |
171 | return "Event('%s %s(%s) %s')" % (" ".join(self.properties), | |
172 | self.name, | |
173 | self.args, | |
174 | self.fmt) | |
175 | ||
176 | def _read_events(fobj): | |
177 | res = [] | |
178 | for line in fobj: | |
179 | if not line.strip(): | |
180 | continue | |
181 | if line.lstrip().startswith('#'): | |
182 | continue | |
183 | res.append(Event.build(line)) | |
184 | return res | |
185 | ||
186 | ||
187 | class TracetoolError (Exception): | |
188 | """Exception for calls to generate.""" | |
189 | pass | |
190 | ||
191 | ||
192 | def try_import(mod_name, attr_name = None, attr_default = None): | |
193 | """Try to import a module and get an attribute from it. | |
194 | ||
195 | Parameters | |
196 | ---------- | |
197 | mod_name : str | |
198 | Module name. | |
199 | attr_name : str, optional | |
200 | Name of an attribute in the module. | |
201 | attr_default : optional | |
202 | Default value if the attribute does not exist in the module. | |
203 | ||
204 | Returns | |
205 | ------- | |
206 | A pair indicating whether the module could be imported and the module or | |
207 | object or attribute value. | |
208 | """ | |
209 | try: | |
45d6c787 | 210 | module = __import__(mod_name, globals(), locals(), ["__package__"]) |
650ab98d LV |
211 | if attr_name is None: |
212 | return True, module | |
213 | return True, getattr(module, str(attr_name), attr_default) | |
214 | except ImportError: | |
215 | return False, None | |
216 | ||
217 | ||
52ef093a LV |
218 | def generate(fevents, format, backend, |
219 | binary = None, probe_prefix = None): | |
650ab98d LV |
220 | """Generate the output for the given (format, backend) pair. |
221 | ||
222 | Parameters | |
223 | ---------- | |
224 | fevents : file | |
225 | Event description file. | |
226 | format : str | |
227 | Output format name. | |
228 | backend : str | |
229 | Output backend name. | |
52ef093a LV |
230 | binary : str or None |
231 | See tracetool.backend.dtrace.BINARY. | |
232 | probe_prefix : str or None | |
233 | See tracetool.backend.dtrace.PROBEPREFIX. | |
650ab98d LV |
234 | """ |
235 | # fix strange python error (UnboundLocalError tracetool) | |
236 | import tracetool | |
237 | ||
238 | format = str(format) | |
239 | if len(format) is 0: | |
240 | raise TracetoolError("format not set") | |
241 | mformat = format.replace("-", "_") | |
242 | if not tracetool.format.exists(mformat): | |
243 | raise TracetoolError("unknown format: %s" % format) | |
244 | ||
245 | backend = str(backend) | |
246 | if len(backend) is 0: | |
247 | raise TracetoolError("backend not set") | |
248 | mbackend = backend.replace("-", "_") | |
249 | if not tracetool.backend.exists(mbackend): | |
250 | raise TracetoolError("unknown backend: %s" % backend) | |
251 | ||
252 | if not tracetool.backend.compatible(mbackend, mformat): | |
253 | raise TracetoolError("backend '%s' not compatible with format '%s'" % | |
254 | (backend, format)) | |
255 | ||
52ef093a LV |
256 | import tracetool.backend.dtrace |
257 | tracetool.backend.dtrace.BINARY = binary | |
258 | tracetool.backend.dtrace.PROBEPREFIX = probe_prefix | |
259 | ||
650ab98d LV |
260 | events = _read_events(fevents) |
261 | ||
262 | if backend == "nop": | |
263 | ( e.properies.add("disable") for e in events ) | |
264 | ||
265 | tracetool.format.generate_begin(mformat, events) | |
266 | tracetool.backend.generate("nop", format, | |
267 | [ e | |
268 | for e in events | |
269 | if "disable" in e.properties ]) | |
270 | tracetool.backend.generate(backend, format, | |
271 | [ e | |
272 | for e in events | |
273 | if "disable" not in e.properties ]) | |
274 | tracetool.format.generate_end(mformat, events) |