blob: a561bf695ceaa2dcbe353e968093dbf6dd4ac5f4 [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)
José Fonsecad9a2b262014-06-20 11:20:53 +0100226 try:
227 self.handleCall(call)
228 except StopIteration:
229 return False
230 else:
231 return True
José Fonsecabaee5792012-03-15 00:09:25 +0000232
233 def handleCall(self, call):
234 pass
235
236
237class Counter(Unpickler):
238
José Fonsecad32fe432012-08-27 18:07:01 +0100239 def __init__(self, stream, verbose = False):
José Fonsecabaee5792012-03-15 00:09:25 +0000240 Unpickler.__init__(self, stream)
José Fonsecad32fe432012-08-27 18:07:01 +0100241 self.verbose = verbose
242 self.numCalls = 0
243 self.functionFrequencies = {}
244
245 def parse(self):
246 Unpickler.parse(self)
247
248 functionFrequencies = self.functionFrequencies.items()
249 functionFrequencies.sort(lambda (name1, freq1), (name2, freq2): cmp(freq1, freq2))
250 for name, frequency in functionFrequencies:
251 sys.stdout.write('%8u %s\n' % (frequency, name))
José Fonsecabaee5792012-03-15 00:09:25 +0000252
253 def handleCall(self, call):
José Fonsecad32fe432012-08-27 18:07:01 +0100254 if self.verbose:
José Fonsecabaee5792012-03-15 00:09:25 +0000255 sys.stdout.write(str(call))
256 sys.stdout.write('\n')
José Fonsecad32fe432012-08-27 18:07:01 +0100257 self.numCalls += 1
258 try:
259 self.functionFrequencies[call.functionName] += 1
260 except KeyError:
261 self.functionFrequencies[call.functionName] = 1
José Fonsecabaee5792012-03-15 00:09:25 +0000262
263
José Fonseca299a1b32012-01-26 20:32:59 +0000264def main():
José Fonseca447576d2012-01-27 14:27:13 +0000265 optparser = optparse.OptionParser(
José Fonsecad514d792012-10-05 20:56:05 +0100266 usage="\n\tapitrace pickle <trace> | %prog [options]")
José Fonseca447576d2012-01-27 14:27:13 +0000267 optparser.add_option(
José Fonsecad32fe432012-08-27 18:07:01 +0100268 '-p', '--profile',
269 action="store_true", dest="profile", default=False,
270 help="profile call parsing")
271 optparser.add_option(
272 '-v', '--verbose',
273 action="store_true", dest="verbose", default=False,
274 help="dump calls to stdout")
José Fonseca447576d2012-01-27 14:27:13 +0000275
276 (options, args) = optparser.parse_args(sys.argv[1:])
277
278 if args:
279 optparser.error('unexpected arguments')
280
José Fonsecac6977a72012-01-27 14:28:06 +0000281 # Change stdin to binary mode
282 try:
283 import msvcrt
284 except ImportError:
285 pass
286 else:
287 import os
288 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
289
José Fonseca299a1b32012-01-26 20:32:59 +0000290 startTime = time.time()
José Fonsecad32fe432012-08-27 18:07:01 +0100291 parser = Counter(sys.stdin, options.verbose)
José Fonsecabaee5792012-03-15 00:09:25 +0000292 parser.parse()
José Fonseca299a1b32012-01-26 20:32:59 +0000293 stopTime = time.time()
294 duration = stopTime - startTime
José Fonsecad32fe432012-08-27 18:07:01 +0100295
296 if options.profile:
297 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 +0000298
299
300if __name__ == '__main__':
301 main()