blob: 0249e4b7fce1e4cd4087d9bf212f81517711d86b [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é Fonsecaa0a20442014-06-20 14:10:36 +0100128 return '(' + ', '.join(itertools.imap(self.visit, obj)) + ')'
José Fonseca2b2baf92012-03-17 17:23:39 +0000129
José Fonseca47cf67e2012-03-17 21:07:02 +0000130 def visitList(self, obj):
José Fonsecaa0a20442014-06-20 14:10:36 +0100131 if len(obj) == 1:
132 return '&' + self.visit(obj[0])
133 return '{' + ', '.join(itertools.imap(self.visit, obj)) + '}'
José Fonseca2b2baf92012-03-17 17:23:39 +0000134
José Fonseca47cf67e2012-03-17 21:07:02 +0000135 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000136 return 'blob(%u)' % len(obj)
137
138
139class Hasher(Visitor):
140 '''Returns a hashable version of the objtree.'''
141
José Fonseca47cf67e2012-03-17 21:07:02 +0000142 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000143 return obj
144
José Fonseca47cf67e2012-03-17 21:07:02 +0000145 def visitAtom(self, obj):
146 return obj
147
148 def visitIterable(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000149 return tuple(itertools.imap(self.visit, obj))
150
José Fonseca47cf67e2012-03-17 21:07:02 +0000151 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000152 return str(obj)
José Fonseca299a1b32012-01-26 20:32:59 +0000153
154
José Fonseca47cf67e2012-03-17 21:07:02 +0000155class Rebuilder(Visitor):
156 '''Returns a hashable version of the objtree.'''
157
158 def visitAtom(self, obj):
159 return obj
160
161 def visitIterable(self, obj):
162 changed = False
163 newItems = []
164 for oldItem in obj:
165 newItem = self.visit(oldItem)
166 if newItem is not oldItem:
167 changed = True
168 newItems.append(newItem)
169 if changed:
170 klass = type(obj)
José Fonseca0f04d1a2012-04-16 10:59:49 +0100171 return klass(newItems)
José Fonseca47cf67e2012-03-17 21:07:02 +0000172 else:
173 return obj
174
175 def visitByteArray(self, obj):
176 return obj
177
178
José Fonsecabaee5792012-03-15 00:09:25 +0000179class Call:
180
181 def __init__(self, callTuple):
José Fonsecac5dd5032014-06-20 11:20:08 +0100182 self.no, self.functionName, self.args, self.ret, self.flags = callTuple
José Fonsecabf341272012-03-16 15:40:31 +0000183 self._hash = None
José Fonsecabaee5792012-03-15 00:09:25 +0000184
185 def __str__(self):
186 s = self.functionName
187 if self.no is not None:
188 s = str(self.no) + ' ' + s
José Fonseca2b2baf92012-03-17 17:23:39 +0000189 dumper = Dumper()
190 s += '(' + ', '.join(itertools.imap(dumper.visit, self.args)) + ')'
José Fonsecabaee5792012-03-15 00:09:25 +0000191 if self.ret is not None:
192 s += ' = '
José Fonseca2b2baf92012-03-17 17:23:39 +0000193 s += dumper.visit(self.ret)
José Fonsecabaee5792012-03-15 00:09:25 +0000194 return s
195
196 def __eq__(self, other):
197 return \
198 self.functionName == other.functionName and \
199 self.args == other.args and \
200 self.ret == other.ret
201
202 def __hash__(self):
José Fonsecabf341272012-03-16 15:40:31 +0000203 if self._hash is None:
José Fonseca2b2baf92012-03-17 17:23:39 +0000204 hasher = Hasher()
205 hashable = hasher.visit(self.functionName), hasher.visit(self.args), hasher.visit(self.ret)
206 self._hash = hash(hashable)
José Fonsecabf341272012-03-16 15:40:31 +0000207 return self._hash
José Fonsecabaee5792012-03-15 00:09:25 +0000208
209
210class Unpickler:
211
212 callFactory = Call
213
214 def __init__(self, stream):
215 self.stream = stream
216
217 def parse(self):
218 while self.parseCall():
219 pass
220
221 def parseCall(self):
222 try:
223 callTuple = pickle.load(self.stream)
224 except EOFError:
225 return False
226 else:
227 call = self.callFactory(callTuple)
José Fonsecad9a2b262014-06-20 11:20:53 +0100228 try:
229 self.handleCall(call)
230 except StopIteration:
231 return False
232 else:
233 return True
José Fonsecabaee5792012-03-15 00:09:25 +0000234
235 def handleCall(self, call):
236 pass
237
238
239class Counter(Unpickler):
240
José Fonsecad32fe432012-08-27 18:07:01 +0100241 def __init__(self, stream, verbose = False):
José Fonsecabaee5792012-03-15 00:09:25 +0000242 Unpickler.__init__(self, stream)
José Fonsecad32fe432012-08-27 18:07:01 +0100243 self.verbose = verbose
244 self.numCalls = 0
245 self.functionFrequencies = {}
246
247 def parse(self):
248 Unpickler.parse(self)
249
250 functionFrequencies = self.functionFrequencies.items()
251 functionFrequencies.sort(lambda (name1, freq1), (name2, freq2): cmp(freq1, freq2))
252 for name, frequency in functionFrequencies:
253 sys.stdout.write('%8u %s\n' % (frequency, name))
José Fonsecabaee5792012-03-15 00:09:25 +0000254
255 def handleCall(self, call):
José Fonsecad32fe432012-08-27 18:07:01 +0100256 if self.verbose:
José Fonsecabaee5792012-03-15 00:09:25 +0000257 sys.stdout.write(str(call))
258 sys.stdout.write('\n')
José Fonsecad32fe432012-08-27 18:07:01 +0100259 self.numCalls += 1
260 try:
261 self.functionFrequencies[call.functionName] += 1
262 except KeyError:
263 self.functionFrequencies[call.functionName] = 1
José Fonsecabaee5792012-03-15 00:09:25 +0000264
265
José Fonseca299a1b32012-01-26 20:32:59 +0000266def main():
José Fonseca447576d2012-01-27 14:27:13 +0000267 optparser = optparse.OptionParser(
José Fonsecad514d792012-10-05 20:56:05 +0100268 usage="\n\tapitrace pickle <trace> | %prog [options]")
José Fonseca447576d2012-01-27 14:27:13 +0000269 optparser.add_option(
José Fonsecad32fe432012-08-27 18:07:01 +0100270 '-p', '--profile',
271 action="store_true", dest="profile", default=False,
272 help="profile call parsing")
273 optparser.add_option(
274 '-v', '--verbose',
275 action="store_true", dest="verbose", default=False,
276 help="dump calls to stdout")
José Fonseca447576d2012-01-27 14:27:13 +0000277
278 (options, args) = optparser.parse_args(sys.argv[1:])
279
280 if args:
281 optparser.error('unexpected arguments')
282
José Fonsecac6977a72012-01-27 14:28:06 +0000283 # Change stdin to binary mode
284 try:
285 import msvcrt
286 except ImportError:
287 pass
288 else:
289 import os
290 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
291
José Fonseca299a1b32012-01-26 20:32:59 +0000292 startTime = time.time()
José Fonsecad32fe432012-08-27 18:07:01 +0100293 parser = Counter(sys.stdin, options.verbose)
José Fonsecabaee5792012-03-15 00:09:25 +0000294 parser.parse()
José Fonseca299a1b32012-01-26 20:32:59 +0000295 stopTime = time.time()
296 duration = stopTime - startTime
José Fonsecad32fe432012-08-27 18:07:01 +0100297
298 if options.profile:
299 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 +0000300
301
302if __name__ == '__main__':
303 main()