]> git.cworth.org Git - apitrace/blob - scripts/trace.py
tracediff: Allow to control the call limit.
[apitrace] / scripts / trace.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2011 Jose Fonseca
5 # All Rights Reserved.
6 #
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:
13 #
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
16 #
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
23 # THE SOFTWARE.
24 #
25 ##########################################################################/
26
27
28 import gzip
29 import string
30 import struct
31 import sys
32
33 try:
34     from cStringIO import StringIO
35 except ImportError:
36     from StringIO import StringIO
37
38 import format
39
40
41 class GzipFile(gzip.GzipFile):
42
43     def _read_eof(self):
44         # Ignore incomplete files
45         try:
46             gzip.GzipFile._read_eof(self)
47         except IOError:
48             pass
49
50
51 TRACE_VERSION = 1
52
53 EVENT_ENTER, EVENT_LEAVE = range(2)
54
55 CALL_END, CALL_ARG, CALL_RET, CALL_THREAD = range(4)
56
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)
58
59
60 class Signature:
61     '''Dummy class for signatures.'''
62
63     pass
64
65
66 class Node:
67
68     def visit(self, visitor):
69         raise NotImplementedError
70
71     def pretty_print(self, formatter):
72         pretty_printer = PrettyPrinter(formatter)
73         self.visit(pretty_printer)
74
75     def __str__(self):
76         stream = StringIO()
77         formatter = format.Formatter(stream)
78         self.pretty_print(formatter)
79         return stream.getvalue()
80
81     def __eq__(self, other):
82         raise NotImplementedError
83
84     def __hash__(self):
85         raise NotImplementedError
86
87
88 class Literal(Node):
89
90     def __init__(self, value):
91         self.value = value
92
93     def visit(self, visitor):
94         visitor.visit_literal(self)
95
96     def __eq__(self, other):
97         return \
98             self.__class__ == other.__class__ and \
99             self.value == other.value
100
101     def __hash__(self):
102         return hash(self.value)
103
104
105 class Enum(Node):
106
107     def __init__(self, name, value):
108         self.name = name
109         self.value = value
110
111     def visit(self, visitor):
112         visitor.visit_enum(self)
113
114     def __eq__(self, other):
115         return \
116             self.__class__ == other.__class__ and \
117             self.name == other.name and \
118             self.value == other.value
119
120     def __hash__(self):
121         return hash(self.value)
122
123
124 class Array(Node):
125
126     def __init__(self, elements):
127         self.elements = tuple(elements)
128
129     def visit(self, visitor):
130         visitor.visit_array(self)
131
132     def __eq__(self, other):
133         return \
134             self.__class__ == other.__class__ and \
135             self.elements == other.elements
136
137     def __hash__(self):
138         return hash(self.elements)
139
140
141 class Pointer(Node):
142
143     def __init__(self, value):
144         self.value = value
145
146     def visit(self, visitor):
147         visitor.visit_pointer(self)
148
149     def __eq__(self, other):
150         return \
151             self.__class__ == other.__class__ and \
152             self.value == other.value
153
154     def __hash__(self):
155         return hash(self.value)
156
157
158 def Null():
159     return Enum("NULL", 0)
160
161 def Bitmask(sig, value):
162     return Literal(value)
163
164 def Blob(buf):
165     return Literal('blob(%u)' % len(buf))
166
167
168 class Struct(Node):
169
170     def __init__(self, sig, members):
171         self.sig = sig
172         self.members = tuple(members)
173
174     def visit(self, visitor):
175         visitor.visit_struct(self)
176
177     def __eq__(self, other):
178         return \
179             self.__class__ == other.__class__ and \
180             self.sig.member_names == other.sig.member_names and \
181             self.members == other.members
182
183     def __hash__(self):
184         return hash(self.sig.member_names) ^ hash(self.members)
185
186
187 class Call(Node):
188
189     def __init__(self, sig):
190         self.sig = sig
191         self.args = [None] * len(sig.arg_names)
192         self.ret = None
193
194     def get_name(self):
195         return self.sig.name
196
197     name = property(get_name)
198
199     def visit(self, visitor):
200         visitor.visit_call(self)
201
202     def __eq__(self, other):
203         return \
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
208
209     def __hash__(self):
210         return hash(self.sig.name) ^ hash(tuple(self.args)) ^ hash(self.ret)
211
212
213 class Trace(Node):
214
215     def __init__(self, calls):
216         self.calls = calls
217
218     def visit(self, visitor):
219         visitor.visit_trace(self)
220
221
222 class Visitor:
223
224     def visit_literal(self, node):
225         raise NotImplementedError
226
227     def visit_enum(self, node):
228         raise NotImplementedError
229
230     def visit_array(self, node):
231         raise NotImplementedError
232
233     def visit_struct(self, node):
234         raise NotImplementedError
235
236     def visit_pointer(self, node):
237         raise NotImplementedError
238
239     def visit_call(self, node):
240         raise NotImplementedError
241
242     def visit_trace(self, node):
243         raise NotImplementedError
244
245
246 class PrettyPrinter:
247
248     def __init__(self, formatter):
249         self.formatter = formatter
250
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('...')
255                 return
256
257             self.formatter.literal('"' + node.value + '"')
258             return
259
260         self.formatter.literal(repr(node.value))
261
262     def visit_enum(self, node):
263         self.formatter.literal(node.name)
264
265     def visit_array(self, node):
266         self.formatter.text('{')
267         sep = ''
268         for value in node.elements:
269             self.formatter.text(sep)
270             value.visit(self)
271             sep = ', '
272         self.formatter.text('}')
273
274     def visit_struct(self, node):
275         self.formatter.text('{')
276         sep = ''
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(' = ')
281             value.visit(self)
282             sep = ', '
283         self.formatter.text('}')
284
285     def visit_pointer(self, node):
286         self.formatter.address(node.value)
287
288     def visit_call(self, node):
289         #self.formatter.text('%s ' % node.no)
290         self.formatter.function(node.name)
291         self.formatter.text('(')
292         sep = ''
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(' = ')
297             value.visit(self)
298             sep = ', '
299         self.formatter.text(')')
300         if node.ret is not None:
301             self.formatter.text(' = ')
302             node.ret.visit(self)
303
304     def visit_trace(self, node):
305         for call in node.calls:
306             call.visit(self)
307             self.formatter.newline()
308
309
310 class Parser:
311
312     debug = False
313
314     def __init__(self):
315         self.file = None
316         self.next_call_no = 0
317         self.version = 0
318
319         self.functions = {}
320         self.enums = {}
321         self.bitmasks = {}
322         self.structs = {}
323
324         self.calls = []
325
326     def open(self, filename):
327         self.file = GzipFile(filename, "rb")
328         if not self.file:
329             return False
330
331         version = self.read_uint()
332         if version > TRACE_VERSION:
333             sys.stderr.write("error: unsupported trace format version %u\n" % version)
334             return False
335
336         return True
337
338     def parse_call(self):
339         if self.debug:
340             sys.stdout.flush()
341         while True:
342             c = self.read_byte()
343             if c == EVENT_ENTER:
344                 self.parse_enter()
345             elif c == EVENT_LEAVE:
346                 return self.parse_leave()
347             elif c == -1:
348                 return None
349             else:
350                 sys.stderr.write("error: unknown event %i\n" % c)
351                 sys.exit(1)
352
353     def parse_enter(self):
354         id = self.read_uint()
355
356         try:
357             sig = self.functions[id]
358         except KeyError:
359             sig = Signature()
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
364
365         call = Call(sig)
366         call.no = self.next_call_no
367         self.next_call_no += 1
368
369         if self.parse_call_details(call):
370             self.calls.append(call)
371         else:
372             del call
373
374     def parse_leave(self):
375         call_no = self.read_uint()
376         call = None
377         for i in range(len(self.calls)):
378             if self.calls[i].no == call_no:
379                 call = self.calls.pop(i)
380                 break
381         if call is None:
382             return None
383
384         if self.parse_call_details(call):
385             return call
386         else:
387             del call
388             return None
389
390     def parse_call_details(self, call):
391         while True:
392             c = self.read_byte()
393             if c == CALL_END:
394                 return True
395             elif c == CALL_ARG:
396                 self.parse_arg(call)
397             elif c == CALL_RET:
398                 call.ret = self.parse_value()
399             else:
400                 sys.stderr.write("error: unknown call detail %i\n" % c)
401                 sys.exit(1)
402
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
409         if self.debug:
410             sys.stderr.write("\tARG %i %s\n" % (index, value))
411
412     def parse_value(self):
413         c = self.read_byte()
414         if c == TYPE_NULL:
415             value = Null()
416         elif c == TYPE_FALSE:
417             value = Literal(False)
418         elif c == TYPE_TRUE:
419             value = Literal(True)
420         elif c == TYPE_SINT:
421             value = self.parse_sint()
422         elif c == TYPE_UINT:
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()
430         elif c == TYPE_ENUM:
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()
438         elif c == TYPE_BLOB:
439             value = self.parse_blob()
440         elif c == TYPE_OPAQUE:
441             value = self.parse_opaque()
442         else:
443             sys.stderr.write("error: unknown type %i\n" % c)
444             sys.exit(1)
445         if self.debug:
446             sys.stderr.write("\tVALUE %s\n" % value)
447         return value
448
449     def parse_sint(self):
450         return Literal(-self.read_uint())
451
452     def parse_uint(self):
453         return Literal(self.read_uint())
454
455     def parse_float(self):
456         value = self.file.read(4)
457         value, = struct.unpack('f', value)
458         return Literal(value)
459
460     def parse_double(self):
461         value = self.file.read(8)
462         value, = struct.unpack('d', value)
463         return Literal(value)
464
465     def parse_string(self):
466         return Literal(self.read_string())
467
468     def parse_enum(self):
469         id = self.read_uint()
470         try:
471             enum = self.enums[id]
472         except KeyError:
473             name = self.read_string()
474             value = self.parse_value()
475             enum = Enum(name, value)
476             self.enums[id] = enum
477         return enum
478
479     def parse_bitmask(self):
480         id = self.read_uint()
481         try:
482             sig = self.bitmasks[id]
483         except KeyError:
484             sig = Signature()
485             num_flags = self.read_uint()
486             sig.flags = []
487             for i in range(num_flags):
488                 name = self.read_string()
489                 value = self.read_uint()
490                 if value == 0 and i:
491                     sys.stderr.write("warning: bitmask %s is zero but is not first flag\n" % name)
492                 flag = name, value
493                 sig.flags.append(flag)
494             self.bitmasks[id] = sig
495         assert sig
496
497         value = self.read_uint()
498
499         return Bitmask(sig, value)
500
501     def parse_array(self):
502         size = self.read_uint()
503         elements = [self.parse_value() for i in range(size)]
504         return Array(elements)
505
506     def parse_blob(self):
507         size = self.read_uint()
508         if size:
509             buf = self.file.read(size)
510         else:
511             buf = ""
512         return Blob(buf)
513
514     def parse_struct(self):
515         id = self.read_uint()
516
517         try:
518             sig = self.structs[id]
519         except KeyError:
520             sig = Signature()
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
525
526         members = [self.parse_value() for i in range(len(sig.member_names))]
527         value = Struct(sig, members)
528
529         return value
530
531     def parse_opaque(self):
532         addr = self.read_uint()
533         return Pointer(addr)
534
535     def read_string(self):
536         size = self.read_uint()
537         if size:
538             value = self.file.read(size)
539         else:
540             value = ''
541         if self.debug:
542             sys.stderr.write("\tSTRING \"%s\"\n" % value)
543         return value
544
545     def read_uint(self):
546         value = 0
547         shift = 0
548         while True:
549             c = self.file.read(1)
550             if c == "":
551                 return 0
552             c = ord(c)
553             value |= (c & 0x7f) << shift
554             shift += 7
555             if c & 0x80 == 0:
556                 break
557         if self.debug:
558             sys.stderr.write("\tUINT %u\n" % value)
559         return value
560
561     def read_byte(self):
562         c = self.file.read(1)
563         if c == "":
564             if self.debug:
565                 sys.stderr.write("\tEOF\n")
566             return -1
567         else:
568             c = ord(c)
569             if self.debug:
570                 sys.stderr.write("\tBYTE 0x%x\n" % c)
571         return c
572
573
574 def main():
575     formatter = format.DefaultFormatter(sys.stdout)
576     for arg in sys.argv[1:]:
577         parser = Parser()
578         parser.open(arg)
579         call = parser.parse_call()
580         while call:
581             formatter.text('%u ' % call.no)
582             call.pretty_print(formatter)
583             formatter.text('\n')
584             call = parser.parse_call()
585
586
587 if __name__ == '__main__':
588     main()