]> git.cworth.org Git - apitrace/blob - scripts/unpickle.py
Improve tracediff2's dumping.
[apitrace] / scripts / unpickle.py
1 #!/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
29 Run as:
30
31    apitrace pickle foo.trace | python unpickle.py
32
33 '''
34
35
36 import itertools
37 import optparse
38 import sys
39 import time
40 import re
41 import cPickle as pickle
42
43
44 class 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
59     def visit(self, obj, *args, **kwargs):
60         return self.dispatch[type(obj)](obj, *args, **kwargs)
61
62     def visitAtom(self, obj, *args, **kwargs):
63         raise NotImplementedError
64
65     def visitNone(self, obj, *args, **kwargs):
66         return self.visitAtom(obj, *args, **kwargs)
67
68     def visitBool(self, obj, *args, **kwargs):
69         return self.visitAtom(obj, *args, **kwargs)
70
71     def visitInt(self, obj, *args, **kwargs):
72         return self.visitAtom(obj, *args, **kwargs)
73
74     def visitFloat(self, obj, *args, **kwargs):
75         return self.visitAtom(obj, *args, **kwargs)
76
77     def visitStr(self, obj, *args, **kwargs):
78         return self.visitAtom(obj, *args, **kwargs)
79
80     def visitIterable(self, obj, *args, **kwargs):
81         raise NotImplementedError
82
83     def visitTuple(self, obj, *args, **kwargs):
84         return self.visitIterable(obj, *args, **kwargs)
85
86     def visitList(self, obj, *args, **kwargs):
87         return self.visitIterable(obj, *args, **kwargs)
88
89     def visitDict(self, obj, *args, **kwargs):
90         raise NotImplementedError
91
92     def visitByteArray(self, obj, *args, **kwargs):
93         raise NotImplementedError
94
95
96 class Dumper(Visitor):
97
98     id_re = re.compile('^[_A-Za-z][_A-Za-z0-9]*$')
99
100     def visitAtom(self, obj, *args, **kwargs):
101         return repr(obj)
102
103     def visitStr(self, obj, *args, **kwargs):
104         if self.id_re.match(obj):
105             return obj
106         else:
107             return repr(obj)
108
109     def visitTuple(self, obj, *args, **kwargs):
110         return '[' + ', '.join(itertools.imap(self.visit, obj)) + ']'
111
112     def visitList(self, obj, *args, **kwargs):
113         return '(' + ', '.join(itertools.imap(self.visit, obj)) + ')'
114
115     def visitByteArray(self, obj, *args, **kwargs):
116         return 'blob(%u)' % len(obj)
117
118
119 class Hasher(Visitor):
120     '''Returns a hashable version of the objtree.'''
121
122     def visitAtom(self, obj, *args, **kwargs):
123         return obj
124
125     def visitIterable(self, obj, *args, **kwargs):
126         return tuple(itertools.imap(self.visit, obj))
127
128     def visitByteArray(self, obj, *args, **kwargs):
129         return str(obj)
130
131
132 class Call:
133
134     def __init__(self, callTuple):
135         self.no, self.functionName, self.args, self.ret = callTuple
136         self._hash = None
137
138     def __str__(self):
139         s = self.functionName
140         if self.no is not None:
141             s = str(self.no) + ' ' + s
142         dumper = Dumper()
143         s += '(' + ', '.join(itertools.imap(dumper.visit, self.args)) + ')'
144         if self.ret is not None:
145             s += ' = '
146             s += dumper.visit(self.ret)
147         return s
148
149     def __eq__(self, other):
150         return \
151             self.functionName == other.functionName and \
152             self.args == other.args and \
153             self.ret == other.ret
154
155     def __hash__(self):
156         if self._hash is None:
157             hasher = Hasher()
158             hashable = hasher.visit(self.functionName), hasher.visit(self.args), hasher.visit(self.ret)
159             self._hash = hash(hashable)
160         return self._hash
161
162
163 class Unpickler:
164
165     callFactory = Call
166
167     def __init__(self, stream):
168         self.stream = stream
169
170     def parse(self):
171         while self.parseCall():
172             pass
173
174     def parseCall(self):
175         try:
176             callTuple = pickle.load(self.stream)
177         except EOFError:
178             return False
179         else:
180             call = self.callFactory(callTuple)
181             self.handleCall(call)
182             return True
183
184     def handleCall(self, call):
185         pass
186
187
188 class Counter(Unpickler):
189
190     def __init__(self, stream, quiet):
191         Unpickler.__init__(self, stream)
192         self.quiet = quiet
193         self.calls = 0
194
195     def handleCall(self, call):
196         if not self.quiet:
197             sys.stdout.write(str(call))
198             sys.stdout.write('\n')
199         self.calls += 1
200
201
202 def main():
203     optparser = optparse.OptionParser(
204         usage="\n\tapitrace pickle trace. %prog [options]")
205     optparser.add_option(
206         '--quiet',
207         action="store_true", dest="quiet", default=False,
208         help="don't dump calls to stdout")
209
210     (options, args) = optparser.parse_args(sys.argv[1:])
211
212     if args:
213         optparser.error('unexpected arguments')
214
215     # Change stdin to binary mode
216     try:
217         import msvcrt
218     except ImportError:
219         pass
220     else:
221         import os
222         msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
223
224     startTime = time.time()
225     parser = Counter(sys.stdin, options.quiet)
226     parser.parse()
227     stopTime = time.time()
228     duration = stopTime - startTime
229     sys.stderr.write('%u calls, %.03f secs, %u calls/sec\n' % (parser.calls, duration, parser.calls/duration))
230
231
232 if __name__ == '__main__':
233     main()