2 ##########################################################################
4 # Copyright 2008 VMware, Inc.
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 VMWARE 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
37 ELEMENT_START
, ELEMENT_END
, CHARACTER_DATA
, EOF
= range(4)
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
)
45 self
.name_or_data
= name_or_data
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
63 """Expat based XML tokenizer."""
65 def __init__(self
, fp
, skip_ws
= True):
70 self
.skip_ws
= skip_ws
72 self
.character_pos
= 0, 0
73 self
.character_data
= ''
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
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
)
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
)
92 def handle_character_data(self
, data
):
93 if not self
.character_data
:
94 self
.character_pos
= self
.pos()
95 self
.character_data
+= data
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
= ''
107 while self
.index
>= len(self
.tokens
) and not self
.final
:
110 data
= self
.fp
.read(size
)
111 self
.final
= len(data
) < size
112 data
= data
.rstrip('\0')
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:
121 if self
.index
>= len(self
.tokens
):
122 line
, column
= self
.pos()
123 token
= XmlToken(EOF
, None, None, line
, column
)
125 token
= self
.tokens
[self
.index
]
130 return self
.parser
.CurrentLineNumber
, self
.parser
.CurrentColumnNumber
133 class TokenMismatch(Exception):
135 def __init__(self
, expected
, found
):
136 self
.expected
= expected
140 return '%u:%u: %s expected, %s found' % (self
.found
.line
, self
.found
.column
, str(self
.expected
), str(self
.found
))
145 """Base XML document parser."""
147 def __init__(self
, fp
):
148 self
.tokenizer
= XmlTokenizer(fp
)
152 self
.token
= self
.tokenizer
.next()
154 def match_element_start(self
, name
):
155 return self
.token
.type == ELEMENT_START
and self
.token
.name_or_data
== name
157 def match_element_end(self
, name
):
158 return self
.token
.type == ELEMENT_END
and self
.token
.name_or_data
== name
160 def element_start(self
, name
):
161 while self
.token
.type == CHARACTER_DATA
:
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
171 def element_end(self
, name
):
172 while self
.token
.type == CHARACTER_DATA
:
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
)
180 def character_data(self
, strip
= True):
182 while self
.token
.type == CHARACTER_DATA
:
183 data
+= self
.token
.name_or_data
190 class TraceParser(XmlParser
):
192 def __init__(self
, fp
):
193 XmlParser
.__init
__(self
, fp
)
194 self
.last_call_no
= 0
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')
204 def parse_call(self
):
205 attrs
= self
.element_start('call')
207 no
= int(attrs
['no'])
209 self
.last_call_no
+= 1
210 no
= self
.last_call_no
212 self
.last_call_no
= no
213 klass
= attrs
['class']
214 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
227 elif self
.token
.name_or_data
== 'time':
228 time
= self
.parse_time()
230 raise TokenMismatch("<arg ...> or <ret ...>", self
.token
)
231 self
.element_end('call')
233 return Call(no
, klass
, method
, args
, ret
, time
)
236 attrs
= self
.element_start('arg')
238 value
= self
.parse_value()
239 self
.element_end('arg')
244 attrs
= self
.element_start('ret')
245 value
= self
.parse_value()
246 self
.element_end('ret')
250 def parse_time(self
):
251 attrs
= self
.element_start('time')
252 time
= self
.parse_value();
253 self
.element_end('time')
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
)
262 raise TokenMismatch(" or " .join(expected_tokens
), self
.token
)
264 def parse_null(self
):
265 self
.element_start('null')
266 self
.element_end('null')
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
)
276 self
.element_start('int')
277 value
= int(self
.character_data())
278 self
.element_end('int')
279 return Literal(value
)
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
)
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
)
293 def parse_enum(self
):
294 self
.element_start('enum')
295 name
= self
.character_data()
296 self
.element_end('enum')
297 return NamedConstant(name
)
299 def parse_string(self
):
300 self
.element_start('string')
301 value
= self
.character_data()
302 self
.element_end('string')
303 return Literal(value
)
305 def parse_bytes(self
):
306 self
.element_start('bytes')
307 value
= self
.character_data()
308 self
.element_end('bytes')
311 def parse_array(self
):
312 self
.element_start('array')
314 while self
.token
.type != ELEMENT_END
:
315 elems
.append(self
.parse_elem())
316 self
.element_end('array')
319 def parse_elem(self
):
320 self
.element_start('elem')
321 value
= self
.parse_value()
322 self
.element_end('elem')
325 def parse_struct(self
):
326 attrs
= self
.element_start('struct')
329 while self
.token
.type != ELEMENT_END
:
330 members
.append(self
.parse_member())
331 self
.element_end('struct')
332 return Struct(name
, members
)
334 def parse_member(self
):
335 attrs
= self
.element_start('member')
337 value
= self
.parse_value()
338 self
.element_end('member')
343 self
.element_start('ptr')
344 address
= self
.character_data()
345 self
.element_end('ptr')
347 return Pointer(address
)
349 def handle_call(self
, call
):
353 class TraceDumper(TraceParser
):
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
)
360 def handle_call(self
, call
):
361 call
.visit(self
.pretty_printer
)
362 self
.formatter
.newline()
366 '''Common main class for all retrace command line utilities.'''
372 optparser
= self
.get_optparser()
373 (options
, args
) = optparser
.parse_args(sys
.argv
[1:])
376 optparser
.error('insufficient number of arguments')
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')
386 stream
= open(arg
, 'rt')
387 self
.process_arg(stream
, options
)
389 def get_optparser(self
):
390 optparser
= optparse
.OptionParser(
391 usage
="\n\t%prog [options] TRACE [...]")
394 def process_arg(self
, stream
, options
):
395 parser
= TraceDumper(stream
)
399 if __name__
== '__main__':