blob: d2b38228420ab81385a7f8a9938bc08966ba7709 [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é Fonsecabef6ae72014-12-29 14:22:23 +000058class Pointer(long):
José Fonseca66b7bcc2014-06-20 14:12:19 +010059
60 def __str__(self):
José Fonsecabef6ae72014-12-29 14:22:23 +000061 if self == 0L:
José Fonseca66b7bcc2014-06-20 14:12:19 +010062 return 'NULL'
63 else:
José Fonsecabef6ae72014-12-29 14:22:23 +000064 return hex(self).rstrip('L')
José Fonseca66b7bcc2014-06-20 14:12:19 +010065
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
Jose Fonseca731ccce2016-01-19 12:36:27 +0000230 def arg(self, argName):
231 '''Lookup argument by name.'''
232 for name, value in self.args:
233 if name == argName:
234 return value
235 raise NameError(argName)
236
237 def argValues(self):
238 '''Return the arg values'''
239 return [value for name, value in self.args]
240
José Fonsecabaee5792012-03-15 00:09:25 +0000241
242class Unpickler:
243
244 callFactory = Call
245
246 def __init__(self, stream):
247 self.stream = stream
248
249 def parse(self):
250 while self.parseCall():
251 pass
252
253 def parseCall(self):
254 try:
255 callTuple = pickle.load(self.stream)
256 except EOFError:
257 return False
258 else:
259 call = self.callFactory(callTuple)
José Fonsecad9a2b262014-06-20 11:20:53 +0100260 try:
261 self.handleCall(call)
262 except StopIteration:
263 return False
264 else:
265 return True
José Fonsecabaee5792012-03-15 00:09:25 +0000266
267 def handleCall(self, call):
268 pass
269
270
271class Counter(Unpickler):
272
José Fonsecad32fe432012-08-27 18:07:01 +0100273 def __init__(self, stream, verbose = False):
José Fonsecabaee5792012-03-15 00:09:25 +0000274 Unpickler.__init__(self, stream)
José Fonsecad32fe432012-08-27 18:07:01 +0100275 self.verbose = verbose
276 self.numCalls = 0
277 self.functionFrequencies = {}
278
279 def parse(self):
280 Unpickler.parse(self)
281
282 functionFrequencies = self.functionFrequencies.items()
283 functionFrequencies.sort(lambda (name1, freq1), (name2, freq2): cmp(freq1, freq2))
284 for name, frequency in functionFrequencies:
285 sys.stdout.write('%8u %s\n' % (frequency, name))
José Fonsecabaee5792012-03-15 00:09:25 +0000286
287 def handleCall(self, call):
José Fonsecad32fe432012-08-27 18:07:01 +0100288 if self.verbose:
José Fonsecabaee5792012-03-15 00:09:25 +0000289 sys.stdout.write(str(call))
290 sys.stdout.write('\n')
José Fonsecad32fe432012-08-27 18:07:01 +0100291 self.numCalls += 1
292 try:
293 self.functionFrequencies[call.functionName] += 1
294 except KeyError:
295 self.functionFrequencies[call.functionName] = 1
José Fonsecabaee5792012-03-15 00:09:25 +0000296
297
José Fonseca299a1b32012-01-26 20:32:59 +0000298def main():
José Fonseca447576d2012-01-27 14:27:13 +0000299 optparser = optparse.OptionParser(
José Fonsecad514d792012-10-05 20:56:05 +0100300 usage="\n\tapitrace pickle <trace> | %prog [options]")
José Fonseca447576d2012-01-27 14:27:13 +0000301 optparser.add_option(
José Fonsecad32fe432012-08-27 18:07:01 +0100302 '-p', '--profile',
303 action="store_true", dest="profile", default=False,
304 help="profile call parsing")
305 optparser.add_option(
306 '-v', '--verbose',
307 action="store_true", dest="verbose", default=False,
308 help="dump calls to stdout")
José Fonseca447576d2012-01-27 14:27:13 +0000309
310 (options, args) = optparser.parse_args(sys.argv[1:])
311
312 if args:
313 optparser.error('unexpected arguments')
314
José Fonsecac6977a72012-01-27 14:28:06 +0000315 # Change stdin to binary mode
316 try:
317 import msvcrt
318 except ImportError:
319 pass
320 else:
321 import os
322 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
323
José Fonseca299a1b32012-01-26 20:32:59 +0000324 startTime = time.time()
José Fonsecad32fe432012-08-27 18:07:01 +0100325 parser = Counter(sys.stdin, options.verbose)
José Fonsecabaee5792012-03-15 00:09:25 +0000326 parser.parse()
José Fonseca299a1b32012-01-26 20:32:59 +0000327 stopTime = time.time()
328 duration = stopTime - startTime
José Fonsecad32fe432012-08-27 18:07:01 +0100329
330 if options.profile:
331 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 +0000332
333
334if __name__ == '__main__':
335 main()