blob: 3db739dcf559ec768a956a3ca6d1974f17f55311 [file] [log] [blame]
Jose Fonseca247e1fa2019-04-28 14:14:44 +01001#!/usr/bin/env python3
José Fonseca299a1b32012-01-26 20:32:59 +00002##########################################################################
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
Jose Fonseca247e1fa2019-04-28 14:14:44 +010037import operator
José Fonseca447576d2012-01-27 14:27:13 +000038import optparse
José Fonseca299a1b32012-01-26 20:32:59 +000039import sys
40import time
José Fonseca2b2baf92012-03-17 17:23:39 +000041import re
Piotr Podsiadły0b8b0192019-01-03 20:39:55 +010042import pickle as pickle
José Fonseca2b2baf92012-03-17 17:23:39 +000043
44
José Fonsecac5dd5032014-06-20 11:20:08 +010045# Same as trace_model.hpp's call flags
46CALL_FLAG_FAKE = (1 << 0)
47CALL_FLAG_NON_REPRODUCIBLE = (1 << 1)
48CALL_FLAG_NO_SIDE_EFFECTS = (1 << 2)
49CALL_FLAG_RENDER = (1 << 3)
50CALL_FLAG_SWAP_RENDERTARGET = (1 << 4)
51CALL_FLAG_END_FRAME = (1 << 5)
52CALL_FLAG_INCOMPLETE = (1 << 6)
53CALL_FLAG_VERBOSE = (1 << 7)
54CALL_FLAG_MARKER = (1 << 8)
55CALL_FLAG_MARKER_PUSH = (1 << 9)
56CALL_FLAG_MARKER_POP = (1 << 10)
57
58
Jose Fonseca247e1fa2019-04-28 14:14:44 +010059class Pointer(int):
José Fonseca66b7bcc2014-06-20 14:12:19 +010060
61 def __str__(self):
Piotr Podsiadły0b8b0192019-01-03 20:39:55 +010062 if self == 0:
José Fonseca66b7bcc2014-06-20 14:12:19 +010063 return 'NULL'
64 else:
José Fonsecabef6ae72014-12-29 14:22:23 +000065 return hex(self).rstrip('L')
José Fonseca66b7bcc2014-06-20 14:12:19 +010066
67 __repr__ = __str__
68
69
José Fonseca2b2baf92012-03-17 17:23:39 +000070class Visitor:
71
72 def __init__(self):
73 self.dispatch = {}
74 self.dispatch[type(None)] = self.visitNone
75 self.dispatch[bool] = self.visitBool
76 self.dispatch[int] = self.visitInt
Piotr Podsiadły0b8b0192019-01-03 20:39:55 +010077 self.dispatch[int] = self.visitInt
José Fonseca2b2baf92012-03-17 17:23:39 +000078 self.dispatch[float] = self.visitFloat
79 self.dispatch[str] = self.visitStr
80 self.dispatch[tuple] = self.visitTuple
81 self.dispatch[list] = self.visitList
82 self.dispatch[dict] = self.visitDict
Jose Fonseca247e1fa2019-04-28 14:14:44 +010083 self.dispatch[bytes] = self.visitBytes
José Fonseca66b7bcc2014-06-20 14:12:19 +010084 self.dispatch[Pointer] = self.visitPointer
José Fonseca2b2baf92012-03-17 17:23:39 +000085
José Fonseca47cf67e2012-03-17 21:07:02 +000086 def visit(self, obj):
José Fonseca66b7bcc2014-06-20 14:12:19 +010087 method = self.dispatch.get(obj.__class__, self.visitObj)
José Fonseca47cf67e2012-03-17 21:07:02 +000088 return method(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000089
José Fonseca47cf67e2012-03-17 21:07:02 +000090 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +000091 raise NotImplementedError
92
José Fonseca47cf67e2012-03-17 21:07:02 +000093 def visitAtom(self, obj):
94 return self.visitObj(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000095
José Fonseca47cf67e2012-03-17 21:07:02 +000096 def visitNone(self, obj):
97 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000098
José Fonseca47cf67e2012-03-17 21:07:02 +000099 def visitBool(self, obj):
100 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000101
José Fonseca47cf67e2012-03-17 21:07:02 +0000102 def visitInt(self, obj):
103 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000104
José Fonseca47cf67e2012-03-17 21:07:02 +0000105 def visitFloat(self, obj):
106 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000107
José Fonseca47cf67e2012-03-17 21:07:02 +0000108 def visitStr(self, obj):
109 return self.visitAtom(obj)
110
111 def visitIterable(self, obj):
112 return self.visitObj(obj)
113
114 def visitTuple(self, obj):
115 return self.visitIterable(obj)
116
117 def visitList(self, obj):
118 return self.visitIterable(obj)
119
120 def visitDict(self, obj):
José Fonseca01fc85b2014-06-20 14:11:20 +0100121 return self.visitIterable(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +0000122
Jose Fonseca247e1fa2019-04-28 14:14:44 +0100123 def visitBytes(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000124 raise NotImplementedError
125
José Fonseca66b7bcc2014-06-20 14:12:19 +0100126 def visitPointer(self, obj):
127 return self.visitAtom(obj)
128
José Fonseca2b2baf92012-03-17 17:23:39 +0000129
130class Dumper(Visitor):
131
132 id_re = re.compile('^[_A-Za-z][_A-Za-z0-9]*$')
133
José Fonseca47cf67e2012-03-17 21:07:02 +0000134 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000135 return repr(obj)
136
José Fonseca47cf67e2012-03-17 21:07:02 +0000137 def visitStr(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000138 if self.id_re.match(obj):
139 return obj
140 else:
141 return repr(obj)
142
José Fonseca47cf67e2012-03-17 21:07:02 +0000143 def visitTuple(self, obj):
Piotr Podsiadły0b8b0192019-01-03 20:39:55 +0100144 return '(' + ', '.join(map(self.visit, obj)) + ')'
José Fonseca2b2baf92012-03-17 17:23:39 +0000145
José Fonseca47cf67e2012-03-17 21:07:02 +0000146 def visitList(self, obj):
José Fonsecaa0a20442014-06-20 14:10:36 +0100147 if len(obj) == 1:
148 return '&' + self.visit(obj[0])
Piotr Podsiadły0b8b0192019-01-03 20:39:55 +0100149 return '{' + ', '.join(map(self.visit, obj)) + '}'
José Fonseca2b2baf92012-03-17 17:23:39 +0000150
José Fonseca01fc85b2014-06-20 14:11:20 +0100151 def visitItems(self, items):
152 return ', '.join(['%s = %s' % (name, self.visit(value)) for name, value in items])
153
154 def visitDict(self, obj):
Piotr Podsiadły0b8b0192019-01-03 20:39:55 +0100155 return '{' + self.visitItems(iter(obj.items())) + '}'
José Fonseca01fc85b2014-06-20 14:11:20 +0100156
Jose Fonseca247e1fa2019-04-28 14:14:44 +0100157 def visitBytes(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000158 return 'blob(%u)' % len(obj)
159
160
161class Hasher(Visitor):
162 '''Returns a hashable version of the objtree.'''
163
José Fonseca47cf67e2012-03-17 21:07:02 +0000164 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000165 return obj
166
José Fonseca47cf67e2012-03-17 21:07:02 +0000167 def visitAtom(self, obj):
168 return obj
169
170 def visitIterable(self, obj):
Piotr Podsiadły0b8b0192019-01-03 20:39:55 +0100171 return tuple(map(self.visit, obj))
José Fonseca2b2baf92012-03-17 17:23:39 +0000172
Jose Fonseca247e1fa2019-04-28 14:14:44 +0100173 def visitBytes(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000174 return str(obj)
José Fonseca299a1b32012-01-26 20:32:59 +0000175
176
José Fonseca47cf67e2012-03-17 21:07:02 +0000177class Rebuilder(Visitor):
178 '''Returns a hashable version of the objtree.'''
179
180 def visitAtom(self, obj):
181 return obj
182
183 def visitIterable(self, obj):
184 changed = False
185 newItems = []
186 for oldItem in obj:
187 newItem = self.visit(oldItem)
188 if newItem is not oldItem:
189 changed = True
190 newItems.append(newItem)
191 if changed:
192 klass = type(obj)
José Fonseca0f04d1a2012-04-16 10:59:49 +0100193 return klass(newItems)
José Fonseca47cf67e2012-03-17 21:07:02 +0000194 else:
195 return obj
196
Jose Fonseca247e1fa2019-04-28 14:14:44 +0100197 def visitBytes(self, obj):
José Fonseca47cf67e2012-03-17 21:07:02 +0000198 return obj
199
200
José Fonsecabaee5792012-03-15 00:09:25 +0000201class Call:
202
203 def __init__(self, callTuple):
José Fonsecac5dd5032014-06-20 11:20:08 +0100204 self.no, self.functionName, self.args, self.ret, self.flags = callTuple
José Fonsecabf341272012-03-16 15:40:31 +0000205 self._hash = None
José Fonsecabaee5792012-03-15 00:09:25 +0000206
207 def __str__(self):
208 s = self.functionName
209 if self.no is not None:
210 s = str(self.no) + ' ' + s
José Fonseca2b2baf92012-03-17 17:23:39 +0000211 dumper = Dumper()
José Fonseca87c34802014-06-20 14:13:13 +0100212 s += '(' + dumper.visitItems(self.args) + ')'
José Fonsecabaee5792012-03-15 00:09:25 +0000213 if self.ret is not None:
214 s += ' = '
José Fonseca2b2baf92012-03-17 17:23:39 +0000215 s += dumper.visit(self.ret)
José Fonsecabaee5792012-03-15 00:09:25 +0000216 return s
217
218 def __eq__(self, other):
219 return \
220 self.functionName == other.functionName and \
221 self.args == other.args and \
222 self.ret == other.ret
223
224 def __hash__(self):
José Fonsecabf341272012-03-16 15:40:31 +0000225 if self._hash is None:
José Fonseca2b2baf92012-03-17 17:23:39 +0000226 hasher = Hasher()
227 hashable = hasher.visit(self.functionName), hasher.visit(self.args), hasher.visit(self.ret)
228 self._hash = hash(hashable)
José Fonsecabf341272012-03-16 15:40:31 +0000229 return self._hash
José Fonsecabaee5792012-03-15 00:09:25 +0000230
Jose Fonseca731ccce2016-01-19 12:36:27 +0000231 def arg(self, argName):
232 '''Lookup argument by name.'''
233 for name, value in self.args:
234 if name == argName:
235 return value
236 raise NameError(argName)
237
238 def argValues(self):
239 '''Return the arg values'''
240 return [value for name, value in self.args]
241
José Fonsecabaee5792012-03-15 00:09:25 +0000242
243class Unpickler:
244
245 callFactory = Call
246
247 def __init__(self, stream):
248 self.stream = stream
249
250 def parse(self):
251 while self.parseCall():
252 pass
253
254 def parseCall(self):
255 try:
256 callTuple = pickle.load(self.stream)
257 except EOFError:
258 return False
259 else:
260 call = self.callFactory(callTuple)
José Fonsecad9a2b262014-06-20 11:20:53 +0100261 try:
262 self.handleCall(call)
263 except StopIteration:
264 return False
265 else:
266 return True
José Fonsecabaee5792012-03-15 00:09:25 +0000267
268 def handleCall(self, call):
269 pass
270
271
272class Counter(Unpickler):
273
José Fonsecad32fe432012-08-27 18:07:01 +0100274 def __init__(self, stream, verbose = False):
José Fonsecabaee5792012-03-15 00:09:25 +0000275 Unpickler.__init__(self, stream)
José Fonsecad32fe432012-08-27 18:07:01 +0100276 self.verbose = verbose
277 self.numCalls = 0
278 self.functionFrequencies = {}
279
280 def parse(self):
281 Unpickler.parse(self)
282
Piotr Podsiadły0b8b0192019-01-03 20:39:55 +0100283 functionFrequencies = list(self.functionFrequencies.items())
Jose Fonseca247e1fa2019-04-28 14:14:44 +0100284 functionFrequencies.sort(key=operator.itemgetter(1))
José Fonsecad32fe432012-08-27 18:07:01 +0100285 for name, frequency in functionFrequencies:
286 sys.stdout.write('%8u %s\n' % (frequency, name))
José Fonsecabaee5792012-03-15 00:09:25 +0000287
288 def handleCall(self, call):
José Fonsecad32fe432012-08-27 18:07:01 +0100289 if self.verbose:
José Fonsecabaee5792012-03-15 00:09:25 +0000290 sys.stdout.write(str(call))
291 sys.stdout.write('\n')
José Fonsecad32fe432012-08-27 18:07:01 +0100292 self.numCalls += 1
293 try:
294 self.functionFrequencies[call.functionName] += 1
295 except KeyError:
296 self.functionFrequencies[call.functionName] = 1
José Fonsecabaee5792012-03-15 00:09:25 +0000297
298
José Fonseca299a1b32012-01-26 20:32:59 +0000299def main():
José Fonseca447576d2012-01-27 14:27:13 +0000300 optparser = optparse.OptionParser(
José Fonsecad514d792012-10-05 20:56:05 +0100301 usage="\n\tapitrace pickle <trace> | %prog [options]")
José Fonseca447576d2012-01-27 14:27:13 +0000302 optparser.add_option(
José Fonsecad32fe432012-08-27 18:07:01 +0100303 '-p', '--profile',
304 action="store_true", dest="profile", default=False,
305 help="profile call parsing")
306 optparser.add_option(
307 '-v', '--verbose',
308 action="store_true", dest="verbose", default=False,
309 help="dump calls to stdout")
José Fonseca447576d2012-01-27 14:27:13 +0000310
311 (options, args) = optparser.parse_args(sys.argv[1:])
312
313 if args:
314 optparser.error('unexpected arguments')
315
José Fonsecac6977a72012-01-27 14:28:06 +0000316 # Change stdin to binary mode
317 try:
318 import msvcrt
319 except ImportError:
320 pass
321 else:
322 import os
323 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
324
José Fonseca299a1b32012-01-26 20:32:59 +0000325 startTime = time.time()
Jose Fonseca247e1fa2019-04-28 14:14:44 +0100326 parser = Counter(sys.stdin.buffer, options.verbose)
José Fonsecabaee5792012-03-15 00:09:25 +0000327 parser.parse()
José Fonseca299a1b32012-01-26 20:32:59 +0000328 stopTime = time.time()
329 duration = stopTime - startTime
José Fonsecad32fe432012-08-27 18:07:01 +0100330
331 if options.profile:
332 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 +0000333
334
335if __name__ == '__main__':
336 main()