blob: ab2dc4db0311292dce0a0a8ab23474cdc3d9b300 [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
44class Visitor:
45
46 def __init__(self):
47 self.dispatch = {}
48 self.dispatch[type(None)] = self.visitNone
49 self.dispatch[bool] = self.visitBool
50 self.dispatch[int] = self.visitInt
51 self.dispatch[long] = self.visitInt
52 self.dispatch[float] = self.visitFloat
53 self.dispatch[str] = self.visitStr
54 self.dispatch[tuple] = self.visitTuple
55 self.dispatch[list] = self.visitList
56 self.dispatch[dict] = self.visitDict
57 self.dispatch[bytearray] = self.visitByteArray
58
José Fonseca47cf67e2012-03-17 21:07:02 +000059 def visit(self, obj):
60 method = self.dispatch.get(type(obj), self.visitObj)
61 return method(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000062
José Fonseca47cf67e2012-03-17 21:07:02 +000063 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +000064 raise NotImplementedError
65
José Fonseca47cf67e2012-03-17 21:07:02 +000066 def visitAtom(self, obj):
67 return self.visitObj(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000068
José Fonseca47cf67e2012-03-17 21:07:02 +000069 def visitNone(self, obj):
70 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000071
José Fonseca47cf67e2012-03-17 21:07:02 +000072 def visitBool(self, obj):
73 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000074
José Fonseca47cf67e2012-03-17 21:07:02 +000075 def visitInt(self, obj):
76 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000077
José Fonseca47cf67e2012-03-17 21:07:02 +000078 def visitFloat(self, obj):
79 return self.visitAtom(obj)
José Fonseca2b2baf92012-03-17 17:23:39 +000080
José Fonseca47cf67e2012-03-17 21:07:02 +000081 def visitStr(self, obj):
82 return self.visitAtom(obj)
83
84 def visitIterable(self, obj):
85 return self.visitObj(obj)
86
87 def visitTuple(self, obj):
88 return self.visitIterable(obj)
89
90 def visitList(self, obj):
91 return self.visitIterable(obj)
92
93 def visitDict(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +000094 raise NotImplementedError
95
José Fonseca47cf67e2012-03-17 21:07:02 +000096 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +000097 raise NotImplementedError
98
99
100class Dumper(Visitor):
101
102 id_re = re.compile('^[_A-Za-z][_A-Za-z0-9]*$')
103
José Fonseca47cf67e2012-03-17 21:07:02 +0000104 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000105 return repr(obj)
106
José Fonseca47cf67e2012-03-17 21:07:02 +0000107 def visitStr(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000108 if self.id_re.match(obj):
109 return obj
110 else:
111 return repr(obj)
112
José Fonseca47cf67e2012-03-17 21:07:02 +0000113 def visitTuple(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000114 return '[' + ', '.join(itertools.imap(self.visit, obj)) + ']'
115
José Fonseca47cf67e2012-03-17 21:07:02 +0000116 def visitList(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000117 return '(' + ', '.join(itertools.imap(self.visit, obj)) + ')'
118
José Fonseca47cf67e2012-03-17 21:07:02 +0000119 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000120 return 'blob(%u)' % len(obj)
121
122
123class Hasher(Visitor):
124 '''Returns a hashable version of the objtree.'''
125
José Fonseca47cf67e2012-03-17 21:07:02 +0000126 def visitObj(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000127 return obj
128
José Fonseca47cf67e2012-03-17 21:07:02 +0000129 def visitAtom(self, obj):
130 return obj
131
132 def visitIterable(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000133 return tuple(itertools.imap(self.visit, obj))
134
José Fonseca47cf67e2012-03-17 21:07:02 +0000135 def visitByteArray(self, obj):
José Fonseca2b2baf92012-03-17 17:23:39 +0000136 return str(obj)
José Fonseca299a1b32012-01-26 20:32:59 +0000137
138
José Fonseca47cf67e2012-03-17 21:07:02 +0000139class Rebuilder(Visitor):
140 '''Returns a hashable version of the objtree.'''
141
142 def visitAtom(self, obj):
143 return obj
144
145 def visitIterable(self, obj):
146 changed = False
147 newItems = []
148 for oldItem in obj:
149 newItem = self.visit(oldItem)
150 if newItem is not oldItem:
151 changed = True
152 newItems.append(newItem)
153 if changed:
154 klass = type(obj)
155 return klass(changed)
156 else:
157 return obj
158
159 def visitByteArray(self, obj):
160 return obj
161
162
José Fonsecabaee5792012-03-15 00:09:25 +0000163class Call:
164
165 def __init__(self, callTuple):
166 self.no, self.functionName, self.args, self.ret = callTuple
José Fonsecabf341272012-03-16 15:40:31 +0000167 self._hash = None
José Fonsecabaee5792012-03-15 00:09:25 +0000168
169 def __str__(self):
170 s = self.functionName
171 if self.no is not None:
172 s = str(self.no) + ' ' + s
José Fonseca2b2baf92012-03-17 17:23:39 +0000173 dumper = Dumper()
174 s += '(' + ', '.join(itertools.imap(dumper.visit, self.args)) + ')'
José Fonsecabaee5792012-03-15 00:09:25 +0000175 if self.ret is not None:
176 s += ' = '
José Fonseca2b2baf92012-03-17 17:23:39 +0000177 s += dumper.visit(self.ret)
José Fonsecabaee5792012-03-15 00:09:25 +0000178 return s
179
180 def __eq__(self, other):
181 return \
182 self.functionName == other.functionName and \
183 self.args == other.args and \
184 self.ret == other.ret
185
186 def __hash__(self):
José Fonsecabf341272012-03-16 15:40:31 +0000187 if self._hash is None:
José Fonseca2b2baf92012-03-17 17:23:39 +0000188 hasher = Hasher()
189 hashable = hasher.visit(self.functionName), hasher.visit(self.args), hasher.visit(self.ret)
190 self._hash = hash(hashable)
José Fonsecabf341272012-03-16 15:40:31 +0000191 return self._hash
José Fonsecabaee5792012-03-15 00:09:25 +0000192
193
194class Unpickler:
195
196 callFactory = Call
197
198 def __init__(self, stream):
199 self.stream = stream
200
201 def parse(self):
202 while self.parseCall():
203 pass
204
205 def parseCall(self):
206 try:
207 callTuple = pickle.load(self.stream)
208 except EOFError:
209 return False
210 else:
211 call = self.callFactory(callTuple)
212 self.handleCall(call)
213 return True
214
215 def handleCall(self, call):
216 pass
217
218
219class Counter(Unpickler):
220
221 def __init__(self, stream, quiet):
222 Unpickler.__init__(self, stream)
223 self.quiet = quiet
224 self.calls = 0
225
226 def handleCall(self, call):
227 if not self.quiet:
228 sys.stdout.write(str(call))
229 sys.stdout.write('\n')
230 self.calls += 1
231
232
José Fonseca299a1b32012-01-26 20:32:59 +0000233def main():
José Fonseca447576d2012-01-27 14:27:13 +0000234 optparser = optparse.OptionParser(
235 usage="\n\tapitrace pickle trace. %prog [options]")
236 optparser.add_option(
José Fonseca47cf67e2012-03-17 21:07:02 +0000237 '-q', '--quiet',
José Fonseca447576d2012-01-27 14:27:13 +0000238 action="store_true", dest="quiet", default=False,
239 help="don't dump calls to stdout")
240
241 (options, args) = optparser.parse_args(sys.argv[1:])
242
243 if args:
244 optparser.error('unexpected arguments')
245
José Fonsecac6977a72012-01-27 14:28:06 +0000246 # Change stdin to binary mode
247 try:
248 import msvcrt
249 except ImportError:
250 pass
251 else:
252 import os
253 msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
254
José Fonseca299a1b32012-01-26 20:32:59 +0000255 startTime = time.time()
José Fonsecabaee5792012-03-15 00:09:25 +0000256 parser = Counter(sys.stdin, options.quiet)
257 parser.parse()
José Fonseca299a1b32012-01-26 20:32:59 +0000258 stopTime = time.time()
259 duration = stopTime - startTime
José Fonsecabaee5792012-03-15 00:09:25 +0000260 sys.stderr.write('%u calls, %.03f secs, %u calls/sec\n' % (parser.calls, duration, parser.calls/duration))
José Fonseca299a1b32012-01-26 20:32:59 +0000261
262
263if __name__ == '__main__':
264 main()