2 ##########################################################################
4 # Copyright 2011 Jose Fonseca
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the 'Software'), to deal
9 # in the Software without restriction, including without limitation the rights
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
17 # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 ##########################################################################/
34 from cStringIO import StringIO
36 from StringIO import StringIO
41 class GzipFile(gzip.GzipFile):
44 # Ignore incomplete files
46 gzip.GzipFile._read_eof(self)
53 EVENT_ENTER, EVENT_LEAVE = range(2)
55 CALL_END, CALL_ARG, CALL_RET, CALL_THREAD = range(4)
57 TYPE_NULL, TYPE_FALSE, TYPE_TRUE, TYPE_SINT, TYPE_UINT, TYPE_FLOAT, TYPE_DOUBLE, TYPE_STRING, TYPE_BLOB, TYPE_ENUM, TYPE_BITMASK, TYPE_ARRAY, TYPE_STRUCT, TYPE_OPAQUE = range(14)
61 '''Dummy class for signatures.'''
68 def visit(self, visitor):
69 raise NotImplementedError
71 def pretty_print(self, formatter):
72 pretty_printer = PrettyPrinter(formatter)
73 self.visit(pretty_printer)
77 formatter = format.Formatter(stream)
78 self.pretty_print(formatter)
79 return stream.getvalue()
81 def __eq__(self, other):
82 raise NotImplementedError
85 raise NotImplementedError
90 def __init__(self, value):
93 def visit(self, visitor):
94 visitor.visit_literal(self)
96 def __eq__(self, other):
98 self.__class__ == other.__class__ and \
99 self.value == other.value
102 return hash(self.value)
107 def __init__(self, name, value):
111 def visit(self, visitor):
112 visitor.visit_enum(self)
114 def __eq__(self, other):
116 self.__class__ == other.__class__ and \
117 self.name == other.name and \
118 self.value == other.value
121 return hash(self.value)
126 def __init__(self, elements):
127 self.elements = tuple(elements)
129 def visit(self, visitor):
130 visitor.visit_array(self)
132 def __eq__(self, other):
134 self.__class__ == other.__class__ and \
135 self.elements == other.elements
138 return hash(self.elements)
143 def __init__(self, value):
146 def visit(self, visitor):
147 visitor.visit_pointer(self)
149 def __eq__(self, other):
151 self.__class__ == other.__class__ and \
152 self.value == other.value
155 return hash(self.value)
159 return Enum("NULL", 0)
161 def Bitmask(sig, value):
162 return Literal(value)
165 return Literal('blob(%u)' % len(buf))
170 def __init__(self, sig, members):
172 self.members = tuple(members)
174 def visit(self, visitor):
175 visitor.visit_struct(self)
177 def __eq__(self, other):
179 self.__class__ == other.__class__ and \
180 self.sig.member_names == other.sig.member_names and \
181 self.members == other.members
184 return hash(self.sig.member_names) ^ hash(self.members)
189 def __init__(self, sig):
191 self.args = [None] * len(sig.arg_names)
197 name = property(get_name)
199 def visit(self, visitor):
200 visitor.visit_call(self)
202 def __eq__(self, other):
204 self.__class__ == other.__class__ and \
205 self.sig.name == other.sig.name and \
206 self.args == other.args and \
207 self.ret == other.ret
210 return hash(self.sig.name) ^ hash(tuple(self.args)) ^ hash(self.ret)
215 def __init__(self, calls):
218 def visit(self, visitor):
219 visitor.visit_trace(self)
224 def visit_literal(self, node):
225 raise NotImplementedError
227 def visit_enum(self, node):
228 raise NotImplementedError
230 def visit_array(self, node):
231 raise NotImplementedError
233 def visit_struct(self, node):
234 raise NotImplementedError
236 def visit_pointer(self, node):
237 raise NotImplementedError
239 def visit_call(self, node):
240 raise NotImplementedError
242 def visit_trace(self, node):
243 raise NotImplementedError
248 def __init__(self, formatter):
249 self.formatter = formatter
251 def visit_literal(self, node):
252 if isinstance(node.value, basestring):
253 if len(node.value) >= 4096 or node.value.strip(string.printable):
254 self.formatter.text('...')
257 self.formatter.literal('"' + node.value + '"')
260 self.formatter.literal(repr(node.value))
262 def visit_enum(self, node):
263 self.formatter.literal(node.name)
265 def visit_array(self, node):
266 self.formatter.text('{')
268 for value in node.elements:
269 self.formatter.text(sep)
272 self.formatter.text('}')
274 def visit_struct(self, node):
275 self.formatter.text('{')
277 for name, value in zip(node.sig.member_names, node.members):
278 self.formatter.text(sep)
279 self.formatter.variable(name)
280 self.formatter.text(' = ')
283 self.formatter.text('}')
285 def visit_pointer(self, node):
286 self.formatter.address(node.value)
288 def visit_call(self, node):
289 #self.formatter.text('%s ' % node.no)
290 self.formatter.function(node.name)
291 self.formatter.text('(')
293 for name, value in zip(node.sig.arg_names, node.args):
294 self.formatter.text(sep)
295 self.formatter.variable(name)
296 self.formatter.text(' = ')
299 self.formatter.text(')')
300 if node.ret is not None:
301 self.formatter.text(' = ')
304 def visit_trace(self, node):
305 for call in node.calls:
307 self.formatter.newline()
316 self.next_call_no = 0
326 def open(self, filename):
327 self.file = GzipFile(filename, "rb")
331 version = self.read_uint()
332 if version > TRACE_VERSION:
333 sys.stderr.write("error: unsupported trace format version %u\n" % version)
338 def parse_call(self):
345 elif c == EVENT_LEAVE:
346 return self.parse_leave()
350 sys.stderr.write("error: unknown event %i\n" % c)
353 def parse_enter(self):
354 id = self.read_uint()
357 sig = self.functions[id]
360 sig.name = self.read_string()
361 num_args = self.read_uint()
362 sig.arg_names = tuple([self.read_string() for i in range(num_args)])
363 self.functions[id] = sig
366 call.no = self.next_call_no
367 self.next_call_no += 1
369 if self.parse_call_details(call):
370 self.calls.append(call)
374 def parse_leave(self):
375 call_no = self.read_uint()
377 for i in range(len(self.calls)):
378 if self.calls[i].no == call_no:
379 call = self.calls.pop(i)
384 if self.parse_call_details(call):
390 def parse_call_details(self, call):
398 call.ret = self.parse_value()
400 sys.stderr.write("error: unknown call detail %i\n" % c)
403 def parse_arg(self, call):
404 index = self.read_uint()
405 value = self.parse_value()
406 if index >= len(call.args):
407 call.args.resize(index + 1)
408 call.args[index] = value
410 sys.stderr.write("\tARG %i %s\n" % (index, value))
412 def parse_value(self):
416 elif c == TYPE_FALSE:
417 value = Literal(False)
419 value = Literal(True)
421 value = self.parse_sint()
423 value = self.parse_uint()
424 elif c == TYPE_FLOAT:
425 value = self.parse_float()
426 elif c == TYPE_DOUBLE:
427 value = self.parse_double()
428 elif c == TYPE_STRING:
429 value = self.parse_string()
431 value = self.parse_enum()
432 elif c == TYPE_BITMASK:
433 value = self.parse_bitmask()
434 elif c == TYPE_ARRAY:
435 value = self.parse_array()
436 elif c == TYPE_STRUCT:
437 value = self.parse_struct()
439 value = self.parse_blob()
440 elif c == TYPE_OPAQUE:
441 value = self.parse_opaque()
443 sys.stderr.write("error: unknown type %i\n" % c)
446 sys.stderr.write("\tVALUE %s\n" % value)
449 def parse_sint(self):
450 return Literal(-self.read_uint())
452 def parse_uint(self):
453 return Literal(self.read_uint())
455 def parse_float(self):
456 value = self.file.read(4)
457 value, = struct.unpack('f', value)
458 return Literal(value)
460 def parse_double(self):
461 value = self.file.read(8)
462 value, = struct.unpack('d', value)
463 return Literal(value)
465 def parse_string(self):
466 return Literal(self.read_string())
468 def parse_enum(self):
469 id = self.read_uint()
471 enum = self.enums[id]
473 name = self.read_string()
474 value = self.parse_value()
475 enum = Enum(name, value)
476 self.enums[id] = enum
479 def parse_bitmask(self):
480 id = self.read_uint()
482 sig = self.bitmasks[id]
485 num_flags = self.read_uint()
487 for i in range(num_flags):
488 name = self.read_string()
489 value = self.read_uint()
491 sys.stderr.write("warning: bitmask %s is zero but is not first flag\n" % name)
493 sig.flags.append(flag)
494 self.bitmasks[id] = sig
497 value = self.read_uint()
499 return Bitmask(sig, value)
501 def parse_array(self):
502 size = self.read_uint()
503 elements = [self.parse_value() for i in range(size)]
504 return Array(elements)
506 def parse_blob(self):
507 size = self.read_uint()
509 buf = self.file.read(size)
514 def parse_struct(self):
515 id = self.read_uint()
518 sig = self.structs[id]
521 sig.name = self.read_string()
522 num_members = self.read_uint()
523 sig.member_names = tuple([self.read_string() for i in range(num_members)])
524 self.structs[id] = sig
526 members = [self.parse_value() for i in range(len(sig.member_names))]
527 value = Struct(sig, members)
531 def parse_opaque(self):
532 addr = self.read_uint()
535 def read_string(self):
536 size = self.read_uint()
538 value = self.file.read(size)
542 sys.stderr.write("\tSTRING \"%s\"\n" % value)
549 c = self.file.read(1)
553 value |= (c & 0x7f) << shift
558 sys.stderr.write("\tUINT %u\n" % value)
562 c = self.file.read(1)
565 sys.stderr.write("\tEOF\n")
570 sys.stderr.write("\tBYTE 0x%x\n" % c)
575 formatter = format.DefaultFormatter(sys.stdout)
576 for arg in sys.argv[1:]:
579 call = parser.parse_call()
581 formatter.text('%u ' % call.no)
582 call.pretty_print(formatter)
584 call = parser.parse_call()
587 if __name__ == '__main__':