blob: 5fe40f59939c2c564eadffb2ec56e71df1057f68 [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é Fonseca01fc85b2014-06-20 14:11:20 +0100108 return self.visitIterable(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000109
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é Fonseca01fc85b2014-06-20 14:11:20 +0100135 def visitItems(self, items):
136 return ', '.join(['%s = %s' % (name, self.visit(value)) for name, value in items])
137
138 def visitDict(self, obj):
139 return '{' + self.visitItems(obj.iteritems()) + '}'
140
José Fonseca47cf67e2012-03-17 21:07:02 +0000141 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000142 return 'blob(%u)' % len(obj)
143
144
145class Hasher(Visitor):
146 '''Returns a hashable version of the objtree.'''
147
José Fonseca47cf67e2012-03-17 21:07:02 +0000148 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000149 return obj
150
José Fonseca47cf67e2012-03-17 21:07:02 +0000151 def visitAtom(self, obj):
152 return obj
153
154 def visitIterable(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000155 return tuple(itertools.imap(self.visit, obj))
156
José Fonseca47cf67e2012-03-17 21:07:02 +0000157 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000158 return str(obj)
José Fonseca299a1b32012-01-26 20:32:59 +0000159
160
José Fonseca47cf67e2012-03-17 21:07:02 +0000161class Rebuilder(Visitor):
162 '''Returns a hashable version of the objtree.'''
163
164 def visitAtom(self, obj):
165 return obj
166
167 def visitIterable(self, obj):
168 changed = False
169 newItems = []
170 for oldItem in obj:
171 newItem = self.visit(oldItem)
172 if newItem is not oldItem:
173 changed = True
174 newItems.append(newItem)
175 if changed:
176 klass = type(obj)
José Fonseca0f04d1a2012-04-16 10:59:49 +0100177 return klass(newItems)
José Fonseca47cf67e2012-03-17 21:07:02 +0000178 else:
179 return obj
180
181 def visitByteArray(self, obj):
182 return obj
183
184
José Fonsecabaee5792012-03-15 00:09:25 +0000185class Call:
186
187 def __init__(self, callTuple):
José Fonsecac5dd5032014-06-20 11:20:08 +0100188 self.no, self.functionName, self.args, self.ret, self.flags = callTuple
José Fonsecabf341272012-03-16 15:40:31 +0000189 self._hash = None
José Fonsecabaee5792012-03-15 00:09:25 +0000190
191 def __str__(self):
192 s = self.functionName
193 if self.no is not None:
194 s = str(self.no) + ' ' + s
José Fonseca2b2baf92012-03-17 17:23:39 +0000195 dumper = Dumper()
196 s += '(' + ', '.join(itertools.imap(dumper.visit, self.args)) + ')'
José Fonsecabaee5792012-03-15 00:09:25 +0000197 if self.ret is not None:
198 s += ' = '
José Fonseca2b2baf92012-03-17 17:23:39 +0000199 s += dumper.visit(self.ret)
José Fonsecabaee5792012-03-15 00:09:25 +0000200 return s
201
202 def __eq__(self, other):
203 return \
204 self.functionName == other.functionName and \
205 self.args == other.args and \
206 self.ret == other.ret
207
208 def __hash__(self):
José Fonsecabf341272012-03-16 15:40:31 +0000209 if self._hash is None:
José Fonseca2b2baf92012-03-17 17:23:39 +0000210 hasher = Hasher()
211 hashable = hasher.visit(self.functionName), hasher.visit(self.args), hasher.visit(self.ret)
212 self._hash = hash(hashable)
José Fonsecabf341272012-03-16 15:40:31 +0000213 return self._hash
José Fonsecabaee5792012-03-15 00:09:25 +0000214
215
216class Unpickler:
217
218 callFactory = Call
219
220 def __init__(self, stream):
221 self.stream = stream
222
223 def parse(self):
224 while self.parseCall():
225 pass
226
227 def parseCall(self):
228 try:
229 callTuple = pickle.load(self.stream)
230 except EOFError:
231 return False
232 else:
233 call = self.callFactory(callTuple)
José Fonsecad9a2b262014-06-20 11:20:53 +0100234 try:
235 self.handleCall(call)
236 except StopIteration:
237 return False
238 else:
239 return True
José Fonsecabaee5792012-03-15 00:09:25 +0000240
241 def handleCall(self, call):
242 pass
243
244
245class Counter(Unpickler):
246
José Fonsecad32fe432012-08-27 18:07:01 +0100247 def __init__(self, stream, verbose = False):
José Fonsecabaee5792012-03-15 00:09:25 +0000248 Unpickler.__init__(self, stream)
José Fonsecad32fe432012-08-27 18:07:01 +0100249 self.verbose = verbose
250 self.numCalls = 0
251 self.functionFrequencies = {}
252
253 def parse(self):
254 Unpickler.parse(self)
255
256 functionFrequencies = self.functionFrequencies.items()
257 functionFrequencies.sort(lambda (name1, freq1), (name2, freq2): cmp(freq1, freq2))
258 for name, frequency in functionFrequencies:
259 sys.stdout.write('%8u %s\n' % (frequency, name))
José Fonsecabaee5792012-03-15 00:09:25 +0000260
261 def handleCall(self, call):
José Fonsecad32fe432012-08-27 18:07:01 +0100262 if self.verbose:
José Fonsecabaee5792012-03-15 00:09:25 +0000263 sys.stdout.write(str(call))
264 sys.stdout.write('\n')
José Fonsecad32fe432012-08-27 18:07:01 +0100265 self.numCalls += 1
266 try:
267 self.functionFrequencies[call.functionName] += 1
268 except KeyError:
269 self.functionFrequencies[call.functionName] = 1
José Fonsecabaee5792012-03-15 00:09:25 +0000270
271
José Fonseca299a1b32012-01-26 20:32:59 +0000272def main():
José Fonseca447576d2012-01-27 14:27:13 +0000273 optparser = optparse.OptionParser(
José Fonsecad514d792012-10-05 20:56:05 +0100274 usage="\n\tapitrace pickle <trace> | %prog [options]")
José Fonseca447576d2012-01-27 14:27:13 +0000275 optparser.add_option(
José Fonsecad32fe432012-08-27 18:07:01 +0100276 '-p', '--profile',
277 action="store_true", dest="profile", default=False,
278 help="profile call parsing")
279 optparser.add_option(
280 '-v', '--verbose',
281 action="store_true", dest="verbose", default=False,
282 help="dump calls to stdout")
José Fonseca447576d2012-01-27 14:27:13 +0000283
284 (options, args) = optparser.parse_args(sys.argv[1:])
285
286 if args:
287 optparser.error('unexpected arguments')
288
José Fonsecac6977a72012-01-27 14:28:06 +0000289 # Change stdin to binary mode
290 try:
291 import msvcrt
292 except ImportError:
293 pass
294 else:
295 import os
296 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
297
José Fonseca299a1b32012-01-26 20:32:59 +0000298 startTime = time.time()
José Fonsecad32fe432012-08-27 18:07:01 +0100299 parser = Counter(sys.stdin, options.verbose)
José Fonsecabaee5792012-03-15 00:09:25 +0000300 parser.parse()
José Fonseca299a1b32012-01-26 20:32:59 +0000301 stopTime = time.time()
302 duration = stopTime - startTime
José Fonsecad32fe432012-08-27 18:07:01 +0100303
304 if options.profile:
305 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 +0000306
307
308if __name__ == '__main__':
309 main()