Go to most recent revision | Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
4358 | Serge | 1 | #!/usr/bin/env python |
2 | ########################################################################## |
||
3 | # |
||
4 | # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. |
||
5 | # All Rights Reserved. |
||
6 | # |
||
7 | # Permission is hereby granted, free of charge, to any person obtaining a |
||
8 | # copy of this software and associated documentation files (the |
||
9 | # "Software"), to deal in the Software without restriction, including |
||
10 | # without limitation the rights to use, copy, modify, merge, publish, |
||
11 | # distribute, sub license, and/or sell copies of the Software, and to |
||
12 | # permit persons to whom the Software is furnished to do so, subject to |
||
13 | # the following conditions: |
||
14 | # |
||
15 | # The above copyright notice and this permission notice (including the |
||
16 | # next paragraph) shall be included in all copies or substantial portions |
||
17 | # of the Software. |
||
18 | # |
||
19 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||
20 | # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||
21 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. |
||
22 | # IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR |
||
23 | # ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
||
24 | # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
||
25 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||
26 | # |
||
27 | ########################################################################## |
||
28 | |||
29 | |||
30 | import sys |
||
31 | import xml.parsers.expat |
||
32 | import optparse |
||
33 | |||
34 | from model import * |
||
35 | |||
36 | |||
37 | ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4) |
||
38 | |||
39 | |||
40 | class XmlToken: |
||
41 | |||
42 | def __init__(self, type, name_or_data, attrs = None, line = None, column = None): |
||
43 | assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF) |
||
44 | self.type = type |
||
45 | self.name_or_data = name_or_data |
||
46 | self.attrs = attrs |
||
47 | self.line = line |
||
48 | self.column = column |
||
49 | |||
50 | def __str__(self): |
||
51 | if self.type == ELEMENT_START: |
||
52 | return '<' + self.name_or_data + ' ...>' |
||
53 | if self.type == ELEMENT_END: |
||
54 | return '' + self.name_or_data + '>' |
||
55 | if self.type == CHARACTER_DATA: |
||
56 | return self.name_or_data |
||
57 | if self.type == EOF: |
||
58 | return 'end of file' |
||
59 | assert 0 |
||
60 | |||
61 | |||
62 | class XmlTokenizer: |
||
63 | """Expat based XML tokenizer.""" |
||
64 | |||
65 | def __init__(self, fp, skip_ws = True): |
||
66 | self.fp = fp |
||
67 | self.tokens = [] |
||
68 | self.index = 0 |
||
69 | self.final = False |
||
70 | self.skip_ws = skip_ws |
||
71 | |||
72 | self.character_pos = 0, 0 |
||
73 | self.character_data = '' |
||
74 | |||
75 | self.parser = xml.parsers.expat.ParserCreate() |
||
76 | self.parser.StartElementHandler = self.handle_element_start |
||
77 | self.parser.EndElementHandler = self.handle_element_end |
||
78 | self.parser.CharacterDataHandler = self.handle_character_data |
||
79 | |||
80 | def handle_element_start(self, name, attributes): |
||
81 | self.finish_character_data() |
||
82 | line, column = self.pos() |
||
83 | token = XmlToken(ELEMENT_START, name, attributes, line, column) |
||
84 | self.tokens.append(token) |
||
85 | |||
86 | def handle_element_end(self, name): |
||
87 | self.finish_character_data() |
||
88 | line, column = self.pos() |
||
89 | token = XmlToken(ELEMENT_END, name, None, line, column) |
||
90 | self.tokens.append(token) |
||
91 | |||
92 | def handle_character_data(self, data): |
||
93 | if not self.character_data: |
||
94 | self.character_pos = self.pos() |
||
95 | self.character_data += data |
||
96 | |||
97 | def finish_character_data(self): |
||
98 | if self.character_data: |
||
99 | if not self.skip_ws or not self.character_data.isspace(): |
||
100 | line, column = self.character_pos |
||
101 | token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column) |
||
102 | self.tokens.append(token) |
||
103 | self.character_data = '' |
||
104 | |||
105 | def next(self): |
||
106 | size = 16*1024 |
||
107 | while self.index >= len(self.tokens) and not self.final: |
||
108 | self.tokens = [] |
||
109 | self.index = 0 |
||
110 | data = self.fp.read(size) |
||
111 | self.final = len(data) < size |
||
112 | data = data.rstrip('\0') |
||
113 | try: |
||
114 | self.parser.Parse(data, self.final) |
||
115 | except xml.parsers.expat.ExpatError, e: |
||
116 | #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS: |
||
117 | if e.code == 3: |
||
118 | pass |
||
119 | else: |
||
120 | raise e |
||
121 | if self.index >= len(self.tokens): |
||
122 | line, column = self.pos() |
||
123 | token = XmlToken(EOF, None, None, line, column) |
||
124 | else: |
||
125 | token = self.tokens[self.index] |
||
126 | self.index += 1 |
||
127 | return token |
||
128 | |||
129 | def pos(self): |
||
130 | return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber |
||
131 | |||
132 | |||
133 | class TokenMismatch(Exception): |
||
134 | |||
135 | def __init__(self, expected, found): |
||
136 | self.expected = expected |
||
137 | self.found = found |
||
138 | |||
139 | def __str__(self): |
||
140 | return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found)) |
||
141 | |||
142 | |||
143 | |||
144 | class XmlParser: |
||
145 | """Base XML document parser.""" |
||
146 | |||
147 | def __init__(self, fp): |
||
148 | self.tokenizer = XmlTokenizer(fp) |
||
149 | self.consume() |
||
150 | |||
151 | def consume(self): |
||
152 | self.token = self.tokenizer.next() |
||
153 | |||
154 | def match_element_start(self, name): |
||
155 | return self.token.type == ELEMENT_START and self.token.name_or_data == name |
||
156 | |||
157 | def match_element_end(self, name): |
||
158 | return self.token.type == ELEMENT_END and self.token.name_or_data == name |
||
159 | |||
160 | def element_start(self, name): |
||
161 | while self.token.type == CHARACTER_DATA: |
||
162 | self.consume() |
||
163 | if self.token.type != ELEMENT_START: |
||
164 | raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token) |
||
165 | if self.token.name_or_data != name: |
||
166 | raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token) |
||
167 | attrs = self.token.attrs |
||
168 | self.consume() |
||
169 | return attrs |
||
170 | |||
171 | def element_end(self, name): |
||
172 | while self.token.type == CHARACTER_DATA: |
||
173 | self.consume() |
||
174 | if self.token.type != ELEMENT_END: |
||
175 | raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token) |
||
176 | if self.token.name_or_data != name: |
||
177 | raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token) |
||
178 | self.consume() |
||
179 | |||
180 | def character_data(self, strip = True): |
||
181 | data = '' |
||
182 | while self.token.type == CHARACTER_DATA: |
||
183 | data += self.token.name_or_data |
||
184 | self.consume() |
||
185 | if strip: |
||
186 | data = data.strip() |
||
187 | return data |
||
188 | |||
189 | |||
190 | class TraceParser(XmlParser): |
||
191 | |||
192 | def __init__(self, fp): |
||
193 | XmlParser.__init__(self, fp) |
||
194 | self.last_call_no = 0 |
||
195 | |||
196 | def parse(self): |
||
197 | self.element_start('trace') |
||
198 | while self.token.type not in (ELEMENT_END, EOF): |
||
199 | call = self.parse_call() |
||
200 | self.handle_call(call) |
||
201 | if self.token.type != EOF: |
||
202 | self.element_end('trace') |
||
203 | |||
204 | def parse_call(self): |
||
205 | attrs = self.element_start('call') |
||
206 | try: |
||
207 | no = int(attrs['no']) |
||
208 | except KeyError: |
||
209 | self.last_call_no += 1 |
||
210 | no = self.last_call_no |
||
211 | else: |
||
212 | self.last_call_no = no |
||
213 | klass = attrs['class'] |
||
214 | method = attrs['method'] |
||
215 | args = [] |
||
216 | ret = None |
||
217 | time = None |
||
218 | while self.token.type == ELEMENT_START: |
||
219 | if self.token.name_or_data == 'arg': |
||
220 | arg = self.parse_arg() |
||
221 | args.append(arg) |
||
222 | elif self.token.name_or_data == 'ret': |
||
223 | ret = self.parse_ret() |
||
224 | elif self.token.name_or_data == 'call': |
||
225 | # ignore nested function calls |
||
226 | self.parse_call() |
||
227 | elif self.token.name_or_data == 'time': |
||
228 | time = self.parse_time() |
||
229 | else: |
||
230 | raise TokenMismatch(" |
||
231 | self.element_end('call') |
||
232 | |||
233 | return Call(no, klass, method, args, ret, time) |
||
234 | |||
235 | def parse_arg(self): |
||
236 | attrs = self.element_start('arg') |
||
237 | name = attrs['name'] |
||
238 | value = self.parse_value() |
||
239 | self.element_end('arg') |
||
240 | |||
241 | return name, value |
||
242 | |||
243 | def parse_ret(self): |
||
244 | attrs = self.element_start('ret') |
||
245 | value = self.parse_value() |
||
246 | self.element_end('ret') |
||
247 | |||
248 | return value |
||
249 | |||
250 | def parse_time(self): |
||
251 | attrs = self.element_start('time') |
||
252 | time = self.parse_value(); |
||
253 | self.element_end('time') |
||
254 | return time |
||
255 | |||
256 | def parse_value(self): |
||
257 | expected_tokens = ('null', 'bool', 'int', 'uint', 'float', 'string', 'enum', 'array', 'struct', 'ptr', 'bytes') |
||
258 | if self.token.type == ELEMENT_START: |
||
259 | if self.token.name_or_data in expected_tokens: |
||
260 | method = getattr(self, 'parse_' + self.token.name_or_data) |
||
261 | return method() |
||
262 | raise TokenMismatch(" or " .join(expected_tokens), self.token) |
||
263 | |||
264 | def parse_null(self): |
||
265 | self.element_start('null') |
||
266 | self.element_end('null') |
||
267 | return Literal(None) |
||
268 | |||
269 | def parse_bool(self): |
||
270 | self.element_start('bool') |
||
271 | value = int(self.character_data()) |
||
272 | self.element_end('bool') |
||
273 | return Literal(value) |
||
274 | |||
275 | def parse_int(self): |
||
276 | self.element_start('int') |
||
277 | value = int(self.character_data()) |
||
278 | self.element_end('int') |
||
279 | return Literal(value) |
||
280 | |||
281 | def parse_uint(self): |
||
282 | self.element_start('uint') |
||
283 | value = int(self.character_data()) |
||
284 | self.element_end('uint') |
||
285 | return Literal(value) |
||
286 | |||
287 | def parse_float(self): |
||
288 | self.element_start('float') |
||
289 | value = float(self.character_data()) |
||
290 | self.element_end('float') |
||
291 | return Literal(value) |
||
292 | |||
293 | def parse_enum(self): |
||
294 | self.element_start('enum') |
||
295 | name = self.character_data() |
||
296 | self.element_end('enum') |
||
297 | return NamedConstant(name) |
||
298 | |||
299 | def parse_string(self): |
||
300 | self.element_start('string') |
||
301 | value = self.character_data() |
||
302 | self.element_end('string') |
||
303 | return Literal(value) |
||
304 | |||
305 | def parse_bytes(self): |
||
306 | self.element_start('bytes') |
||
307 | value = self.character_data() |
||
308 | self.element_end('bytes') |
||
309 | return Blob(value) |
||
310 | |||
311 | def parse_array(self): |
||
312 | self.element_start('array') |
||
313 | elems = [] |
||
314 | while self.token.type != ELEMENT_END: |
||
315 | elems.append(self.parse_elem()) |
||
316 | self.element_end('array') |
||
317 | return Array(elems) |
||
318 | |||
319 | def parse_elem(self): |
||
320 | self.element_start('elem') |
||
321 | value = self.parse_value() |
||
322 | self.element_end('elem') |
||
323 | return value |
||
324 | |||
325 | def parse_struct(self): |
||
326 | attrs = self.element_start('struct') |
||
327 | name = attrs['name'] |
||
328 | members = [] |
||
329 | while self.token.type != ELEMENT_END: |
||
330 | members.append(self.parse_member()) |
||
331 | self.element_end('struct') |
||
332 | return Struct(name, members) |
||
333 | |||
334 | def parse_member(self): |
||
335 | attrs = self.element_start('member') |
||
336 | name = attrs['name'] |
||
337 | value = self.parse_value() |
||
338 | self.element_end('member') |
||
339 | |||
340 | return name, value |
||
341 | |||
342 | def parse_ptr(self): |
||
343 | self.element_start('ptr') |
||
344 | address = self.character_data() |
||
345 | self.element_end('ptr') |
||
346 | |||
347 | return Pointer(address) |
||
348 | |||
349 | def handle_call(self, call): |
||
350 | pass |
||
351 | |||
352 | |||
353 | class TraceDumper(TraceParser): |
||
354 | |||
355 | def __init__(self, fp, outStream = sys.stdout): |
||
356 | TraceParser.__init__(self, fp) |
||
357 | self.formatter = format.DefaultFormatter(outStream) |
||
358 | self.pretty_printer = PrettyPrinter(self.formatter) |
||
359 | |||
360 | def handle_call(self, call): |
||
361 | call.visit(self.pretty_printer) |
||
362 | self.formatter.newline() |
||
363 | |||
364 | |||
365 | class Main: |
||
366 | '''Common main class for all retrace command line utilities.''' |
||
367 | |||
368 | def __init__(self): |
||
369 | pass |
||
370 | |||
371 | def main(self): |
||
372 | optparser = self.get_optparser() |
||
373 | (options, args) = optparser.parse_args(sys.argv[1:]) |
||
374 | |||
375 | if not args: |
||
376 | optparser.error('insufficient number of arguments') |
||
377 | |||
378 | for arg in args: |
||
379 | if arg.endswith('.gz'): |
||
380 | from gzip import GzipFile |
||
381 | stream = GzipFile(arg, 'rt') |
||
382 | elif arg.endswith('.bz2'): |
||
383 | from bz2 import BZ2File |
||
384 | stream = BZ2File(arg, 'rU') |
||
385 | else: |
||
386 | stream = open(arg, 'rt') |
||
387 | self.process_arg(stream, options) |
||
388 | |||
389 | def get_optparser(self): |
||
390 | optparser = optparse.OptionParser( |
||
391 | usage="\n\t%prog [options] TRACE [...]") |
||
392 | return optparser |
||
393 | |||
394 | def process_arg(self, stream, options): |
||
395 | parser = TraceDumper(stream) |
||
396 | parser.parse() |
||
397 | |||
398 | |||
399 | if __name__ == '__main__': |
||
400 | Main().main()> |