]> git.cworth.org Git - apitrace/blob - scripts/tracediff.py
2506eb752111faa793b127ddbd249f94bc730146
[apitrace] / scripts / tracediff.py
1 #!/usr/bin/env python
2 ##########################################################################
3 #
4 # Copyright 2011 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
28 import difflib
29 import optparse
30 import os.path
31 import sys
32
33 from trace import Parser
34
35 try:
36     import debug
37 except ImportError:
38     pass
39
40
41 ignored_function_names = set([
42     'glGetString',
43     'glXGetClientString',
44     'glXGetCurrentDisplay',
45     'glXGetProcAddress',
46     'glXGetProcAddressARB',
47     'wglGetProcAddress',
48 ])
49
50
51 def readtrace(trace):
52     calls = []
53     parser = Parser()
54     parser.open(trace)
55     call = parser.parse_call()
56     while call and len(calls) < 1000:
57         hash(call)
58         if call.sig.name not in ignored_function_names:
59             calls.append(call)
60         call = parser.parse_call()
61     return calls
62
63
64 class SDiffer:
65
66     def __init__(self, a, b):
67         self.a = a
68         self.b = b
69
70     def diff(self):
71         matcher = difflib.SequenceMatcher(None, self.a, self.b)
72         for tag, alo, ahi, blo, bhi in matcher.get_opcodes():
73             if tag == 'replace':
74                 self.replace(alo, ahi, blo, bhi)
75             elif tag == 'delete':
76                 self.delete(alo, ahi)
77             elif tag == 'insert':
78                 self.insert(blo, bhi)
79             elif tag == 'equal':
80                 self.equal(alo, ahi)
81             else:
82                 raise ValueError, 'unknown tag %s' % (tag,)
83
84     def replace(self, alo, ahi, blo, bhi):
85         assert alo < ahi and blo < bhi
86         
87         a_names = [call.name for call in self.a[alo:ahi]]
88         b_names = [call.name for call in self.b[blo:bhi]]
89
90         matcher = difflib.SequenceMatcher(None, a_names, b_names)
91         for tag, _alo, _ahi, _blo, _bhi in matcher.get_opcodes():
92             _alo += alo
93             _ahi += alo
94             _blo += blo
95             _bhi += blo
96             if tag == 'replace':
97                 self.replace_dissimilar(_alo, _ahi, _blo, _bhi)
98             elif tag == 'delete':
99                 self.delete(_alo, _ahi)
100             elif tag == 'insert':
101                 self.insert(_blo, _bhi)
102             elif tag == 'equal':
103                 self.replace_similar(_alo, _ahi, _blo, _bhi)
104             else:
105                 raise ValueError, 'unknown tag %s' % (tag,)
106
107     def replace_similar(self, alo, ahi, blo, bhi):
108         assert alo < ahi and blo < bhi
109         assert ahi - alo == bhi - blo
110         for i in xrange(0, bhi - blo):
111             a_call = self.a[alo + i]
112             b_call = self.b[blo + i]
113             assert a_call.name == b_call.name
114             assert len(a_call.args) == len(b_call.args)
115             sys.stdout.write(b_call.name + '(')
116             sep = ''
117             for j in xrange(len(b_call.args)):
118                 sys.stdout.write(sep)
119                 self.replace_value(a_call.args[j], b_call.args[j])
120                 sep = ', '
121             sys.stdout.write(')')
122             if a_call.ret is not None or b_call.ret is not None:
123                 sys.stdout.write(' = ')
124                 self.replace_value(a_call.ret, b_call.ret)
125             sys.stdout.write('\n')
126
127     def replace_dissimilar(self, alo, ahi, blo, bhi):
128         assert alo < ahi and blo < bhi
129         if bhi - blo < ahi - alo:
130             first  = self.insert(blo, bhi)
131             second = self.delete(alo, ahi)
132         else:
133             first  = self.delete(alo, ahi)
134             second = self.insert(blo, bhi)
135
136         for g in first, second:
137             for line in g:
138                 yield line
139
140     def replace_value(self, a, b):
141         if b == a:
142             sys.stdout.write(str(b))
143         else:
144             sys.stdout.write('%s -> %s' % (a, b))
145
146     escape = "\33["
147
148     def delete(self, alo, ahi):
149         self.dump('- ' + self.escape + '9m', self.a, alo, ahi, self.escape + '0m')
150
151     def insert(self, blo, bhi):
152         self.dump('+ ', self.b, blo, bhi)
153
154     def equal(self, alo, ahi):
155         self.dump('  ' + self.escape + '2m', self.a, alo, ahi, self.escape + '0m')
156
157     def dump(self, prefix, x, lo, hi, suffix=""):
158         for i in xrange(lo, hi):
159             sys.stdout.write(prefix + str(x[i]) + suffix + '\n')
160
161
162 def main():
163     global options
164
165     optparser = optparse.OptionParser(
166         usage='\n\t%prog <trace> <trace>',
167         version='%%prog')
168     optparser.add_option(
169         '-d', '--tracedump', metavar='PROGRAM',
170         type='string', dest='tracedump', default='tracedump',
171         help='tracedump command [default: %default]')
172
173     (options, args) = optparser.parse_args(sys.argv[1:])
174     if len(args) != 2:
175         optparser.error("incorrect number of arguments")
176
177     ref_calls = readtrace(args[0])
178     src_calls = readtrace(args[1])
179
180     differ = SDiffer(ref_calls, src_calls)
181     differ.diff()
182
183
184 if __name__ == '__main__':
185     main()