blob: b6c76d10e5248e3533ede73b3dc87f8e3647246f [file] [log] [blame]
José Fonseca299a1b32012-01-26 20:32:59 +00001#!/usr/bin/env python
2##########################################################################
3#
4# Copyright 2012 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'''Sample program for apitrace pickle command.
28
29Run as:
30
31 apitrace pickle foo.trace | python unpickle.py
32
33'''
34
35
José Fonseca2b2baf92012-03-17 17:23:39 +000036import itertools
José Fonseca447576d2012-01-27 14:27:13 +000037import optparse
José Fonseca299a1b32012-01-26 20:32:59 +000038import sys
39import time
José Fonseca2b2baf92012-03-17 17:23:39 +000040import re
41import cPickle as pickle
42
43
José Fonsecac5dd5032014-06-20 11:20:08 +010044# Same as trace_model.hpp's call flags
45CALL_FLAG_FAKE = (1 << 0)
46CALL_FLAG_NON_REPRODUCIBLE = (1 << 1)
47CALL_FLAG_NO_SIDE_EFFECTS = (1 << 2)
48CALL_FLAG_RENDER = (1 << 3)
49CALL_FLAG_SWAP_RENDERTARGET = (1 << 4)
50CALL_FLAG_END_FRAME = (1 << 5)
51CALL_FLAG_INCOMPLETE = (1 << 6)
52CALL_FLAG_VERBOSE = (1 << 7)
53CALL_FLAG_MARKER = (1 << 8)
54CALL_FLAG_MARKER_PUSH = (1 << 9)
55CALL_FLAG_MARKER_POP = (1 << 10)
56
57
José Fonseca66b7bcc2014-06-20 14:12:19 +010058class Pointer(int):
59
60 def __str__(self):
61 if self == 0:
62 return 'NULL'
63 else:
64 return hex(self)
65
66 __repr__ = __str__
67
68
José Fonseca2b2baf92012-03-17 17:23:39 +000069class Visitor:
70
71 def __init__(self):
72 self.dispatch = {}
73 self.dispatch[type(None)] = self.visitNone
74 self.dispatch[bool] = self.visitBool
75 self.dispatch[int] = self.visitInt
76 self.dispatch[long] = self.visitInt
77 self.dispatch[float] = self.visitFloat
78 self.dispatch[str] = self.visitStr
79 self.dispatch[tuple] = self.visitTuple
80 self.dispatch[list] = self.visitList
81 self.dispatch[dict] = self.visitDict
82 self.dispatch[bytearray] = self.visitByteArray
José Fonseca66b7bcc2014-06-20 14:12:19 +010083 self.dispatch[Pointer] = self.visitPointer
José Fonseca2b2baf92012-03-17 17:23:39 +000084
José Fonseca47cf67e2012-03-17 21:07:02 +000085 def visit(self, obj):
José Fonseca66b7bcc2014-06-20 14:12:19 +010086 method = self.dispatch.get(obj.__class__, self.visitObj)
José Fonseca47cf67e2012-03-17 21:07:02 +000087 return method(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000088
José Fonseca47cf67e2012-03-17 21:07:02 +000089 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +000090 raise NotImplementedError
91
José Fonseca47cf67e2012-03-17 21:07:02 +000092 def visitAtom(self, obj):
93 return self.visitObj(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000094
José Fonseca47cf67e2012-03-17 21:07:02 +000095 def visitNone(self, obj):
96 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000097
José Fonseca47cf67e2012-03-17 21:07:02 +000098 def visitBool(self, obj):
99 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000100
José Fonseca47cf67e2012-03-17 21:07:02 +0000101 def visitInt(self, obj):
102 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000103
José Fonseca47cf67e2012-03-17 21:07:02 +0000104 def visitFloat(self, obj):
105 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000106
José Fonseca47cf67e2012-03-17 21:07:02 +0000107 def visitStr(self, obj):
108 return self.visitAtom(obj)
109
110 def visitIterable(self, obj):
111 return self.visitObj(obj)
112
113 def visitTuple(self, obj):
114 return self.visitIterable(obj)
115
116 def visitList(self, obj):
117 return self.visitIterable(obj)
118
119 def visitDict(self, obj):
José Fonseca01fc85b2014-06-20 14:11:20 +0100120 return self.visitIterable(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000121
José Fonseca47cf67e2012-03-17 21:07:02 +0000122 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000123 raise NotImplementedError
124
José Fonseca66b7bcc2014-06-20 14:12:19 +0100125 def visitPointer(self, obj):
126 return self.visitAtom(obj)
127
José Fonseca2b2baf92012-03-17 17:23:39 +0000128
129class Dumper(Visitor):
130
131 id_re = re.compile('^[_A-Za-z][_A-Za-z0-9]*$')
132
José Fonseca47cf67e2012-03-17 21:07:02 +0000133 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000134 return repr(obj)
135
José Fonseca47cf67e2012-03-17 21:07:02 +0000136 def visitStr(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000137 if self.id_re.match(obj):
138 return obj
139 else:
140 return repr(obj)
141
José Fonseca47cf67e2012-03-17 21:07:02 +0000142 def visitTuple(self, obj):
José Fonsecaa0a20442014-06-20 14:10:36 +0100143 return '(' + ', '.join(itertools.imap(self.visit, obj)) + ')'
José Fonseca2b2baf92012-03-17 17:23:39 +0000144
José Fonseca47cf67e2012-03-17 21:07:02 +0000145 def visitList(self, obj):
José Fonsecaa0a20442014-06-20 14:10:36 +0100146 if len(obj) == 1:
147 return '&' + self.visit(obj[0])
148 return '{' + ', '.join(itertools.imap(self.visit, obj)) + '}'
José Fonseca2b2baf92012-03-17 17:23:39 +0000149
José Fonseca01fc85b2014-06-20 14:11:20 +0100150 def visitItems(self, items):
151 return ', '.join(['%s = %s' % (name, self.visit(value)) for name, value in items])
152
153 def visitDict(self, obj):
154 return '{' + self.visitItems(obj.iteritems()) + '}'
155
José Fonseca47cf67e2012-03-17 21:07:02 +0000156 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000157 return 'blob(%u)' % len(obj)
158
159
160class Hasher(Visitor):
161 '''Returns a hashable version of the objtree.'''
162
José Fonseca47cf67e2012-03-17 21:07:02 +0000163 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000164 return obj
165
José Fonseca47cf67e2012-03-17 21:07:02 +0000166 def visitAtom(self, obj):
167 return obj
168
169 def visitIterable(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000170 return tuple(itertools.imap(self.visit, obj))
171
José Fonseca47cf67e2012-03-17 21:07:02 +0000172 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000173 return str(obj)
José Fonseca299a1b32012-01-26 20:32:59 +0000174
175
José Fonseca47cf67e2012-03-17 21:07:02 +0000176class Rebuilder(Visitor):
177 '''Returns a hashable version of the objtree.'''
178
179 def visitAtom(self, obj):
180 return obj
181
182 def visitIterable(self, obj):
183 changed = False
184 newItems = []
185 for oldItem in obj:
186 newItem = self.visit(oldItem)
187 if newItem is not oldItem:
188 changed = True
189 newItems.append(newItem)
190 if changed:
191 klass = type(obj)
José Fonseca0f04d1a2012-04-16 10:59:49 +0100192 return klass(newItems)
José Fonseca47cf67e2012-03-17 21:07:02 +0000193 else:
194 return obj
195
196 def visitByteArray(self, obj):
197 return obj
198
199
José Fonsecabaee5792012-03-15 00:09:25 +0000200class Call:
201
202 def __init__(self, callTuple):
José Fonsecac5dd5032014-06-20 11:20:08 +0100203 self.no, self.functionName, self.args, self.ret, self.flags = callTuple
José Fonsecabf341272012-03-16 15:40:31 +0000204 self._hash = None
José Fonsecabaee5792012-03-15 00:09:25 +0000205
206 def __str__(self):
207 s = self.functionName
208 if self.no is not None:
209 s = str(self.no) + ' ' + s
José Fonseca2b2baf92012-03-17 17:23:39 +0000210 dumper = Dumper()
José Fonseca87c34802014-06-20 14:13:13 +0100211 s += '(' + dumper.visitItems(self.args) + ')'
José Fonsecabaee5792012-03-15 00:09:25 +0000212 if self.ret is not None:
213 s += ' = '
José Fonseca2b2baf92012-03-17 17:23:39 +0000214 s += dumper.visit(self.ret)
José Fonsecabaee5792012-03-15 00:09:25 +0000215 return s
216
217 def __eq__(self, other):
218 return \
219 self.functionName == other.functionName and \
220 self.args == other.args and \
221 self.ret == other.ret
222
223 def __hash__(self):
José Fonsecabf341272012-03-16 15:40:31 +0000224 if self._hash is None:
José Fonseca2b2baf92012-03-17 17:23:39 +0000225 hasher = Hasher()
226 hashable = hasher.visit(self.functionName), hasher.visit(self.args), hasher.visit(self.ret)
227 self._hash = hash(hashable)
José Fonsecabf341272012-03-16 15:40:31 +0000228 return self._hash
José Fonsecabaee5792012-03-15 00:09:25 +0000229
230
231class Unpickler:
232
233 callFactory = Call
234
235 def __init__(self, stream):
236 self.stream = stream
237
238 def parse(self):
239 while self.parseCall():
240 pass
241
242 def parseCall(self):
243 try:
244 callTuple = pickle.load(self.stream)
245 except EOFError:
246 return False
247 else:
248 call = self.callFactory(callTuple)
José Fonsecad9a2b262014-06-20 11:20:53 +0100249 try:
250 self.handleCall(call)
251 except StopIteration:
252 return False
253 else:
254 return True
José Fonsecabaee5792012-03-15 00:09:25 +0000255
256 def handleCall(self, call):
257 pass
258
259
260class Counter(Unpickler):
261
José Fonsecad32fe432012-08-27 18:07:01 +0100262 def __init__(self, stream, verbose = False):
José Fonsecabaee5792012-03-15 00:09:25 +0000263 Unpickler.__init__(self, stream)
José Fonsecad32fe432012-08-27 18:07:01 +0100264 self.verbose = verbose
265 self.numCalls = 0
266 self.functionFrequencies = {}
267
268 def parse(self):
269 Unpickler.parse(self)
270
271 functionFrequencies = self.functionFrequencies.items()
272 functionFrequencies.sort(lambda (name1, freq1), (name2, freq2): cmp(freq1, freq2))
273 for name, frequency in functionFrequencies:
274 sys.stdout.write('%8u %s\n' % (frequency, name))
José Fonsecabaee5792012-03-15 00:09:25 +0000275
276 def handleCall(self, call):
José Fonsecad32fe432012-08-27 18:07:01 +0100277 if self.verbose:
José Fonsecabaee5792012-03-15 00:09:25 +0000278 sys.stdout.write(str(call))
279 sys.stdout.write('\n')
José Fonsecad32fe432012-08-27 18:07:01 +0100280 self.numCalls += 1
281 try:
282 self.functionFrequencies[call.functionName] += 1
283 except KeyError:
284 self.functionFrequencies[call.functionName] = 1
José Fonsecabaee5792012-03-15 00:09:25 +0000285
286
José Fonseca299a1b32012-01-26 20:32:59 +0000287def main():
José Fonseca447576d2012-01-27 14:27:13 +0000288 optparser = optparse.OptionParser(
José Fonsecad514d792012-10-05 20:56:05 +0100289 usage="\n\tapitrace pickle <trace> | %prog [options]")
José Fonseca447576d2012-01-27 14:27:13 +0000290 optparser.add_option(
José Fonsecad32fe432012-08-27 18:07:01 +0100291 '-p', '--profile',
292 action="store_true", dest="profile", default=False,
293 help="profile call parsing")
294 optparser.add_option(
295 '-v', '--verbose',
296 action="store_true", dest="verbose", default=False,
297 help="dump calls to stdout")
José Fonseca447576d2012-01-27 14:27:13 +0000298
299 (options, args) = optparser.parse_args(sys.argv[1:])
300
301 if args:
302 optparser.error('unexpected arguments')
303
José Fonsecac6977a72012-01-27 14:28:06 +0000304 # Change stdin to binary mode
305 try:
306 import msvcrt
307 except ImportError:
308 pass
309 else:
310 import os
311 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
312
José Fonseca299a1b32012-01-26 20:32:59 +0000313 startTime = time.time()
José Fonsecad32fe432012-08-27 18:07:01 +0100314 parser = Counter(sys.stdin, options.verbose)
José Fonsecabaee5792012-03-15 00:09:25 +0000315 parser.parse()
José Fonseca299a1b32012-01-26 20:32:59 +0000316 stopTime = time.time()
317 duration = stopTime - startTime
José Fonsecad32fe432012-08-27 18:07:01 +0100318
319 if options.profile:
320 sys.stderr.write('Processed %u calls in %.03f secs, at %u calls/sec\n' % (parser.numCalls, duration, parser.numCalls/duration))
José Fonseca299a1b32012-01-26 20:32:59 +0000321
322
323if __name__ == '__main__':
324 main()