2 ##########################################################################
4 # Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
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:
15 # The above copyright notice and this permission notice (including the
16 # next paragraph) shall be included in all copies or substantial portions
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.
27 ##########################################################################
31 import xml
.parsers
.expat
38 ELEMENT_START
, ELEMENT_END
, CHARACTER_DATA
, EOF
= range(4)
43 def __init__(self
, type, name_or_data
, attrs
= None, line
= None, column
= None):
44 assert type in (ELEMENT_START
, ELEMENT_END
, CHARACTER_DATA
, EOF
)
46 self
.name_or_data
= name_or_data
52 if self
.type == ELEMENT_START
:
53 return '<' + self
.name_or_data
+ ' ...>'
54 if self
.type == ELEMENT_END
:
55 return '</' + self
.name_or_data
+ '>'
56 if self
.type == CHARACTER_DATA
:
57 return self
.name_or_data
64 """Expat based XML tokenizer."""
66 def __init__(self
, fp
, skip_ws
= True):
71 self
.skip_ws
= skip_ws
73 self
.character_pos
= 0, 0
74 self
.character_data
= ''
76 self
.parser
= xml
.parsers
.expat
.ParserCreate()
77 self
.parser
.StartElementHandler
= self
.handle_element_start
78 self
.parser
.EndElementHandler
= self
.handle_element_end
79 self
.parser
.CharacterDataHandler
= self
.handle_character_data
81 def handle_element_start(self
, name
, attributes
):
82 self
.finish_character_data()
83 line
, column
= self
.pos()
84 token
= XmlToken(ELEMENT_START
, name
, attributes
, line
, column
)
85 self
.tokens
.append(token
)
87 def handle_element_end(self
, name
):
88 self
.finish_character_data()
89 line
, column
= self
.pos()
90 token
= XmlToken(ELEMENT_END
, name
, None, line
, column
)
91 self
.tokens
.append(token
)
93 def handle_character_data(self
, data
):
94 if not self
.character_data
:
95 self
.character_pos
= self
.pos()
96 self
.character_data
+= data
98 def finish_character_data(self
):
99 if self
.character_data
:
100 if not self
.skip_ws
or not self
.character_data
.isspace():
101 line
, column
= self
.character_pos
102 token
= XmlToken(CHARACTER_DATA
, self
.character_data
, None, line
, column
)
103 self
.tokens
.append(token
)
104 self
.character_data
= ''
108 while self
.index
>= len(self
.tokens
) and not self
.final
:
111 data
= self
.fp
.read(size
)
112 self
.final
= len(data
) < size
113 data
= data
.rstrip('\0')
115 self
.parser
.Parse(data
, self
.final
)
116 except xml
.parsers
.expat
.ExpatError
, e
:
117 #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS:
122 if self
.index
>= len(self
.tokens
):
123 line
, column
= self
.pos()
124 token
= XmlToken(EOF
, None, None, line
, column
)
126 token
= self
.tokens
[self
.index
]
131 return self
.parser
.CurrentLineNumber
, self
.parser
.CurrentColumnNumber
134 class TokenMismatch(Exception):
136 def __init__(self
, expected
, found
):
137 self
.expected
= expected
141 return '%u:%u: %s expected, %s found' % (self
.found
.line
, self
.found
.column
, str(self
.expected
), str(self
.found
))
146 """Base XML document parser."""
148 def __init__(self
, fp
):
149 self
.tokenizer
= XmlTokenizer(fp
)
153 self
.token
= self
.tokenizer
.next()
155 def match_element_start(self
, name
):
156 return self
.token
.type == ELEMENT_START
and self
.token
.name_or_data
== name
158 def match_element_end(self
, name
):
159 return self
.token
.type == ELEMENT_END
and self
.token
.name_or_data
== name
161 def element_start(self
, name
):
162 while self
.token
.type == CHARACTER_DATA
:
164 if self
.token
.type != ELEMENT_START
:
165 raise TokenMismatch(XmlToken(ELEMENT_START
, name
), self
.token
)
166 if self
.token
.name_or_data
!= name
:
167 raise TokenMismatch(XmlToken(ELEMENT_START
, name
), self
.token
)
168 attrs
= self
.token
.attrs
172 def element_end(self
, name
):
173 while self
.token
.type == CHARACTER_DATA
:
175 if self
.token
.type != ELEMENT_END
:
176 raise TokenMismatch(XmlToken(ELEMENT_END
, name
), self
.token
)
177 if self
.token
.name_or_data
!= name
:
178 raise TokenMismatch(XmlToken(ELEMENT_END
, name
), self
.token
)
181 def character_data(self
, strip
= True):
183 while self
.token
.type == CHARACTER_DATA
:
184 data
+= self
.token
.name_or_data
191 class TraceParser(XmlParser
):
193 def __init__(self
, fp
):
194 XmlParser
.__init
__(self
, fp
)
195 self
.last_call_no
= 0
198 self
.element_start('trace')
199 while self
.token
.type not in (ELEMENT_END
, EOF
):
200 call
= self
.parse_call()
201 self
.handle_call(call
)
202 if self
.token
.type != EOF
:
203 self
.element_end('trace')
205 def parse_call(self
):
206 attrs
= self
.element_start('call')
208 no
= int(attrs
['no'])
210 self
.last_call_no
+= 1
211 no
= self
.last_call_no
213 self
.last_call_no
= no
214 klass
= attrs
['class']
215 method
= attrs
['method']
218 while self
.token
.type == ELEMENT_START
:
219 if self
.token
.name_or_data
== 'arg':
220 arg
= self
.parse_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
228 raise TokenMismatch("<arg ...> or <ret ...>", self
.token
)
229 self
.element_end('call')
231 return Call(no
, klass
, method
, args
, ret
)
234 attrs
= self
.element_start('arg')
236 value
= self
.parse_value()
237 self
.element_end('arg')
242 attrs
= self
.element_start('ret')
243 value
= self
.parse_value()
244 self
.element_end('ret')
248 def parse_value(self
):
249 expected_tokens
= ('null', 'bool', 'int', 'uint', 'float', 'string', 'enum', 'array', 'struct', 'ptr', 'bytes')
250 if self
.token
.type == ELEMENT_START
:
251 if self
.token
.name_or_data
in expected_tokens
:
252 method
= getattr(self
, 'parse_' + self
.token
.name_or_data
)
254 raise TokenMismatch(" or " .join(expected_tokens
), self
.token
)
256 def parse_null(self
):
257 self
.element_start('null')
258 self
.element_end('null')
261 def parse_bool(self
):
262 self
.element_start('bool')
263 value
= int(self
.character_data())
264 self
.element_end('bool')
265 return Literal(value
)
268 self
.element_start('int')
269 value
= int(self
.character_data())
270 self
.element_end('int')
271 return Literal(value
)
273 def parse_uint(self
):
274 self
.element_start('uint')
275 value
= int(self
.character_data())
276 self
.element_end('uint')
277 return Literal(value
)
279 def parse_float(self
):
280 self
.element_start('float')
281 value
= float(self
.character_data())
282 self
.element_end('float')
283 return Literal(value
)
285 def parse_enum(self
):
286 self
.element_start('enum')
287 name
= self
.character_data()
288 self
.element_end('enum')
289 return NamedConstant(name
)
291 def parse_string(self
):
292 self
.element_start('string')
293 value
= self
.character_data()
294 self
.element_end('string')
295 return Literal(value
)
297 def parse_bytes(self
):
298 self
.element_start('bytes')
299 value
= binascii
.a2b_hex(self
.character_data())
300 self
.element_end('bytes')
301 return Literal(value
)
303 def parse_array(self
):
304 self
.element_start('array')
306 while self
.token
.type != ELEMENT_END
:
307 elems
.append(self
.parse_elem())
308 self
.element_end('array')
311 def parse_elem(self
):
312 self
.element_start('elem')
313 value
= self
.parse_value()
314 self
.element_end('elem')
317 def parse_struct(self
):
318 attrs
= self
.element_start('struct')
321 while self
.token
.type != ELEMENT_END
:
322 members
.append(self
.parse_member())
323 self
.element_end('struct')
324 return Struct(name
, members
)
326 def parse_member(self
):
327 attrs
= self
.element_start('member')
329 value
= self
.parse_value()
330 self
.element_end('member')
335 self
.element_start('ptr')
336 address
= self
.character_data()
337 self
.element_end('ptr')
339 return Pointer(address
)
341 def handle_call(self
, call
):
345 class TraceDumper(TraceParser
):
347 def __init__(self
, fp
):
348 TraceParser
.__init
__(self
, fp
)
349 self
.formatter
= format
.DefaultFormatter(sys
.stdout
)
350 self
.pretty_printer
= PrettyPrinter(self
.formatter
)
352 def handle_call(self
, call
):
353 call
.visit(self
.pretty_printer
)
354 self
.formatter
.newline()
358 '''Common main class for all retrace command line utilities.'''
364 optparser
= self
.get_optparser()
365 (options
, args
) = optparser
.parse_args(sys
.argv
[1:])
369 if arg
.endswith('.gz'):
370 from gzip
import GzipFile
371 stream
= GzipFile(arg
, 'rt')
372 elif arg
.endswith('.bz2'):
373 from bz2
import BZ2File
374 stream
= BZ2File(arg
, 'rU')
376 stream
= open(arg
, 'rt')
377 self
.process_arg(stream
, options
)
379 self
.process_arg(stream
, options
)
381 def get_optparser(self
):
382 optparser
= optparse
.OptionParser(
383 usage
="\n\t%prog [options] [traces] ...")
386 def process_arg(self
, stream
, options
):
387 parser
= TraceDumper(stream
)
391 if __name__
== '__main__':