blob: edd5af7f506613b059b55851b749505fe90a63f4 [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é Fonseca2b2baf92012-03-17 17:23:39 +000058class Visitor:
59
60 def __init__(self):
61 self.dispatch = {}
62 self.dispatch[type(None)] = self.visitNone
63 self.dispatch[bool] = self.visitBool
64 self.dispatch[int] = self.visitInt
65 self.dispatch[long] = self.visitInt
66 self.dispatch[float] = self.visitFloat
67 self.dispatch[str] = self.visitStr
68 self.dispatch[tuple] = self.visitTuple
69 self.dispatch[list] = self.visitList
70 self.dispatch[dict] = self.visitDict
71 self.dispatch[bytearray] = self.visitByteArray
72
José Fonseca47cf67e2012-03-17 21:07:02 +000073 def visit(self, obj):
74 method = self.dispatch.get(type(obj), self.visitObj)
75 return method(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000076
José Fonseca47cf67e2012-03-17 21:07:02 +000077 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +000078 raise NotImplementedError
79
José Fonseca47cf67e2012-03-17 21:07:02 +000080 def visitAtom(self, obj):
81 return self.visitObj(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000082
José Fonseca47cf67e2012-03-17 21:07:02 +000083 def visitNone(self, obj):
84 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000085
José Fonseca47cf67e2012-03-17 21:07:02 +000086 def visitBool(self, obj):
87 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000088
José Fonseca47cf67e2012-03-17 21:07:02 +000089 def visitInt(self, obj):
90 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000091
José Fonseca47cf67e2012-03-17 21:07:02 +000092 def visitFloat(self, obj):
93 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000094
José Fonseca47cf67e2012-03-17 21:07:02 +000095 def visitStr(self, obj):
96 return self.visitAtom(obj)
97
98 def visitIterable(self, obj):
99 return self.visitObj(obj)
100
101 def visitTuple(self, obj):
102 return self.visitIterable(obj)
103
104 def visitList(self, obj):
105 return self.visitIterable(obj)
106
107 def visitDict(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000108 raise NotImplementedError
109
José Fonseca47cf67e2012-03-17 21:07:02 +0000110 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000111 raise NotImplementedError
112
113
114class Dumper(Visitor):
115
116 id_re = re.compile('^[_A-Za-z][_A-Za-z0-9]*$')
117
José Fonseca47cf67e2012-03-17 21:07:02 +0000118 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000119 return repr(obj)
120
José Fonseca47cf67e2012-03-17 21:07:02 +0000121 def visitStr(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000122 if self.id_re.match(obj):
123 return obj
124 else:
125 return repr(obj)
126
José Fonseca47cf67e2012-03-17 21:07:02 +0000127 def visitTuple(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000128 return '[' + ', '.join(itertools.imap(self.visit, obj)) + ']'
129
José Fonseca47cf67e2012-03-17 21:07:02 +0000130 def visitList(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000131 return '(' + ', '.join(itertools.imap(self.visit, obj)) + ')'
132
José Fonseca47cf67e2012-03-17 21:07:02 +0000133 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000134 return 'blob(%u)' % len(obj)
135
136
137class Hasher(Visitor):
138 '''Returns a hashable version of the objtree.'''
139
José Fonseca47cf67e2012-03-17 21:07:02 +0000140 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000141 return obj
142
José Fonseca47cf67e2012-03-17 21:07:02 +0000143 def visitAtom(self, obj):
144 return obj
145
146 def visitIterable(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000147 return tuple(itertools.imap(self.visit, obj))
148
José Fonseca47cf67e2012-03-17 21:07:02 +0000149 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000150 return str(obj)
José Fonseca299a1b32012-01-26 20:32:59 +0000151
152
José Fonseca47cf67e2012-03-17 21:07:02 +0000153class Rebuilder(Visitor):
154 '''Returns a hashable version of the objtree.'''
155
156 def visitAtom(self, obj):
157 return obj
158
159 def visitIterable(self, obj):
160 changed = False
161 newItems = []
162 for oldItem in obj:
163 newItem = self.visit(oldItem)
164 if newItem is not oldItem:
165 changed = True
166 newItems.append(newItem)
167 if changed:
168 klass = type(obj)
José Fonseca0f04d1a2012-04-16 10:59:49 +0100169 return klass(newItems)
José Fonseca47cf67e2012-03-17 21:07:02 +0000170 else:
171 return obj
172
173 def visitByteArray(self, obj):
174 return obj
175
176
José Fonsecabaee5792012-03-15 00:09:25 +0000177class Call:
178
179 def __init__(self, callTuple):
José Fonsecac5dd5032014-06-20 11:20:08 +0100180 self.no, self.functionName, self.args, self.ret, self.flags = callTuple
José Fonsecabf341272012-03-16 15:40:31 +0000181 self._hash = None
José Fonsecabaee5792012-03-15 00:09:25 +0000182
183 def __str__(self):
184 s = self.functionName
185 if self.no is not None:
186 s = str(self.no) + ' ' + s
José Fonseca2b2baf92012-03-17 17:23:39 +0000187 dumper = Dumper()
188 s += '(' + ', '.join(itertools.imap(dumper.visit, self.args)) + ')'
José Fonsecabaee5792012-03-15 00:09:25 +0000189 if self.ret is not None:
190 s += ' = '
José Fonseca2b2baf92012-03-17 17:23:39 +0000191 s += dumper.visit(self.ret)
José Fonsecabaee5792012-03-15 00:09:25 +0000192 return s
193
194 def __eq__(self, other):
195 return \
196 self.functionName == other.functionName and \
197 self.args == other.args and \
198 self.ret == other.ret
199
200 def __hash__(self):
José Fonsecabf341272012-03-16 15:40:31 +0000201 if self._hash is None:
José Fonseca2b2baf92012-03-17 17:23:39 +0000202 hasher = Hasher()
203 hashable = hasher.visit(self.functionName), hasher.visit(self.args), hasher.visit(self.ret)
204 self._hash = hash(hashable)
José Fonsecabf341272012-03-16 15:40:31 +0000205 return self._hash
José Fonsecabaee5792012-03-15 00:09:25 +0000206
207
208class Unpickler:
209
210 callFactory = Call
211
212 def __init__(self, stream):
213 self.stream = stream
214
215 def parse(self):
216 while self.parseCall():
217 pass
218
219 def parseCall(self):
220 try:
221 callTuple = pickle.load(self.stream)
222 except EOFError:
223 return False
224 else:
225 call = self.callFactory(callTuple)
226 self.handleCall(call)
227 return True
228
229 def handleCall(self, call):
230 pass
231
232
233class Counter(Unpickler):
234
José Fonsecad32fe432012-08-27 18:07:01 +0100235 def __init__(self, stream, verbose = False):
José Fonsecabaee5792012-03-15 00:09:25 +0000236 Unpickler.__init__(self, stream)
José Fonsecad32fe432012-08-27 18:07:01 +0100237 self.verbose = verbose
238 self.numCalls = 0
239 self.functionFrequencies = {}
240
241 def parse(self):
242 Unpickler.parse(self)
243
244 functionFrequencies = self.functionFrequencies.items()
245 functionFrequencies.sort(lambda (name1, freq1), (name2, freq2): cmp(freq1, freq2))
246 for name, frequency in functionFrequencies:
247 sys.stdout.write('%8u %s\n' % (frequency, name))
José Fonsecabaee5792012-03-15 00:09:25 +0000248
249 def handleCall(self, call):
José Fonsecad32fe432012-08-27 18:07:01 +0100250 if self.verbose:
José Fonsecabaee5792012-03-15 00:09:25 +0000251 sys.stdout.write(str(call))
252 sys.stdout.write('\n')
José Fonsecad32fe432012-08-27 18:07:01 +0100253 self.numCalls += 1
254 try:
255 self.functionFrequencies[call.functionName] += 1
256 except KeyError:
257 self.functionFrequencies[call.functionName] = 1
José Fonsecabaee5792012-03-15 00:09:25 +0000258
259
José Fonseca299a1b32012-01-26 20:32:59 +0000260def main():
José Fonseca447576d2012-01-27 14:27:13 +0000261 optparser = optparse.OptionParser(
José Fonsecad514d792012-10-05 20:56:05 +0100262 usage="\n\tapitrace pickle <trace> | %prog [options]")
José Fonseca447576d2012-01-27 14:27:13 +0000263 optparser.add_option(
José Fonsecad32fe432012-08-27 18:07:01 +0100264 '-p', '--profile',
265 action="store_true", dest="profile", default=False,
266 help="profile call parsing")
267 optparser.add_option(
268 '-v', '--verbose',
269 action="store_true", dest="verbose", default=False,
270 help="dump calls to stdout")
José Fonseca447576d2012-01-27 14:27:13 +0000271
272 (options, args) = optparser.parse_args(sys.argv[1:])
273
274 if args:
275 optparser.error('unexpected arguments')
276
José Fonsecac6977a72012-01-27 14:28:06 +0000277 # Change stdin to binary mode
278 try:
279 import msvcrt
280 except ImportError:
281 pass
282 else:
283 import os
284 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
285
José Fonseca299a1b32012-01-26 20:32:59 +0000286 startTime = time.time()
José Fonsecad32fe432012-08-27 18:07:01 +0100287 parser = Counter(sys.stdin, options.verbose)
José Fonsecabaee5792012-03-15 00:09:25 +0000288 parser.parse()
José Fonseca299a1b32012-01-26 20:32:59 +0000289 stopTime = time.time()
290 duration = stopTime - startTime
José Fonsecad32fe432012-08-27 18:07:01 +0100291
292 if options.profile:
293 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 +0000294
295
296if __name__ == '__main__':
297 main()