]> git.cworth.org Git - apitrace/blob - scripts/trace.py
55499106f499433c7b4e2e63d150b89cf1add424
[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     def __init__(self):
313         self.file = None
314         self.next_call_no = 0
315         self.version = 0
316
317         self.functions = {}
318         self.enums = {}
319         self.bitmasks = {}
320         self.structs = {}
321
322         self.calls = []
323
324     def open(self, filename):
325         self.file = GzipFile(filename, "rb")
326         if not self.file:
327             return False
328
329         version = self.read_uint()
330         if version > TRACE_VERSION:
331             sys.stderr.write("error: unsupported trace format version %u\n" % version)
332             return False
333
334         return True
335
336     def parse_call(self):
337         while True:
338             c = self.read_byte()
339             if c == EVENT_ENTER:
340                 self.parse_enter()
341             elif c == EVENT_LEAVE:
342                 return self.parse_leave()
343             elif c == -1:
344                 return None
345             else:
346                 sys.stderr.write("error: unknown event %i\n" % c)
347                 sys.exit(1)
348
349     def parse_enter(self):
350         id = self.read_uint()
351
352         try:
353             sig = self.functions[id]
354         except KeyError:
355             sig = Signature()
356             sig.name = self.read_string()
357             num_args = self.read_uint()
358             sig.arg_names = tuple([self.read_string() for i in range(num_args)])
359             self.functions[id] = sig
360
361         call = Call(sig)
362         call.no = self.next_call_no
363         self.next_call_no += 1
364
365         if self.parse_call_details(call):
366             self.calls.append(call)
367         else:
368             del call
369
370     def parse_leave(self):
371         call_no = self.read_uint()
372         call = None
373         for i in range(len(self.calls)):
374             if self.calls[i].no == call_no:
375                 call = self.calls.pop(i)
376                 break
377         if call is None:
378             return None
379
380         if self.parse_call_details(call):
381             return call
382         else:
383             del call
384             return None
385
386     def parse_call_details(self, call):
387         while True:
388             c = self.read_byte()
389             if c == CALL_END:
390                 return True
391             elif c == CALL_ARG:
392                 self.parse_arg(call)
393             elif c == CALL_RET:
394                 call.ret = self.parse_value()
395             else:
396                 sys.stderr.write("error: unknown call detail %i\n" % c)
397                 sys.exit(1)
398
399     def parse_arg(self, call):
400         index = self.read_uint()
401         value = self.parse_value()
402         if index >= len(call.args):
403             call.args.resize(index + 1)
404         call.args[index] = value
405
406     def parse_value(self):
407         c = self.read_byte()
408         if c == TYPE_NULL:
409             value = Null()
410         elif c == TYPE_FALSE:
411             value = Literal(False)
412         elif c == TYPE_TRUE:
413             value = Literal(True)
414         elif c == TYPE_SINT:
415             value = self.parse_sint()
416         elif c == TYPE_UINT:
417             value = self.parse_uint()
418         elif c == TYPE_FLOAT:
419             value = self.parse_float()
420         elif c == TYPE_DOUBLE:
421             value = self.parse_double()
422         elif c == TYPE_STRING:
423             value = self.parse_string()
424         elif c == TYPE_ENUM:
425             value = self.parse_enum()
426         elif c == TYPE_BITMASK:
427             value = self.parse_bitmask()
428         elif c == TYPE_ARRAY:
429             value = self.parse_array()
430         elif c == TYPE_STRUCT:
431             value = self.parse_struct()
432         elif c == TYPE_BLOB:
433             value = self.parse_blob()
434         elif c == TYPE_OPAQUE:
435             value = self.parse_opaque()
436         else:
437             sys.stderr.write("error: unknown type %i\n" % c)
438             sys.exit(1)
439         #self.debug("\tVALUE %s\n" % value)
440         return value
441
442     def parse_sint(self):
443         return Literal(-self.read_uint())
444
445     def parse_uint(self):
446         return Literal(self.read_uint())
447
448     def parse_float(self):
449         value = self.file.read(4)
450         value, = struct.unpack('f', value)
451         return Literal(value)
452
453     def parse_double(self):
454         value = self.file.read(8)
455         value, = struct.unpack('d', value)
456         return Literal(value)
457
458     def parse_string(self):
459         return Literal(self.read_string())
460
461     def parse_enum(self):
462         id = self.read_uint()
463         try:
464             enum = self.enums[id]
465         except KeyError:
466             name = self.read_string()
467             value = self.parse_value()
468             enum = Enum(name, value)
469             self.enums[id] = enum
470         return enum
471
472     def parse_bitmask(self):
473         id = self.read_uint()
474         try:
475             sig = self.bitmasks[id]
476         except KeyError:
477             sig = Signature()
478             num_flags = self.read_uint()
479             sig.flags = []
480             for i in range(num_flags):
481                 name = self.read_string()
482                 value = self.read_uint()
483                 if value == 0 and i:
484                     sys.stderr.write("warning: bitmask %s is zero but is not first flag\n" % name)
485                 flag = name, value
486                 sig.flags.append(flag)
487             self.bitmasks[id] = sig
488         assert sig
489
490         value = self.read_uint()
491
492         return Bitmask(sig, value)
493
494     def parse_array(self):
495         size = self.read_uint()
496         elements = [self.parse_value() for i in range(size)]
497         return Array(elements)
498
499     def parse_blob(self):
500         size = self.read_uint()
501         if size:
502             buf = self.file.read(size)
503         else:
504             buf = ""
505         return Blob(buf)
506
507     def parse_struct(self):
508         id = self.read_uint()
509
510         try:
511             sig = self.structs[id]
512         except KeyError:
513             sig = Signature()
514             sig.name = self.read_string()
515             num_members = self.read_uint()
516             sig.member_names = tuple([self.read_string() for i in range(num_members)])
517             self.structs[id] = sig
518
519         members = [self.parse_value() for i in range(len(sig.member_names))]
520         value = Struct(sig, members)
521
522         return value
523
524     def parse_opaque(self):
525         addr = self.read_uint()
526         return Pointer(addr)
527
528     def read_string(self):
529         size = self.read_uint()
530         if size:
531             value = self.file.read(size)
532         else:
533             value = ''
534         #self.debug("\tSTRING \"%s\"\n" % value)
535         return value
536
537     def read_uint(self):
538         value = 0
539         shift = 0
540         while True:
541             c = self.file.read(1)
542             if c == "":
543                 return 0
544             c = ord(c)
545             value |= (c & 0x7f) << shift
546             shift += 7
547             if c & 0x80 == 0:
548                 break
549         #self.debug("\tUINT %u\n" % value)
550         return value
551
552     def read_byte(self):
553         c = self.file.read(1)
554         if c == "":
555             #self.debug("\tEOF\n")
556             return -1
557         else:
558             c = ord(c)
559             #self.debug("\tBYTE 0x%x\n" % c)
560         return c
561
562     def debug(self, s):
563         sys.stderr.write(s)
564
565
566 def main():
567     formatter = format.DefaultFormatter(sys.stdout)
568     for arg in sys.argv[1:]:
569         parser = Parser()
570         parser.open(arg)
571         call = parser.parse_call()
572         while call:
573             formatter.text('%u ' % call.no)
574             call.pretty_print(formatter)
575             formatter.text('\n')
576             call = parser.parse_call()
577
578
579 if __name__ == '__main__':
580     main()