]> git.cworth.org Git - apitrace/blob - scripts/tracediff.py
More control over the diffing output.
[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 re
32 import subprocess
33 import sys
34
35
36 call_re = re.compile('^([0-9]+) (\w+)\(')
37
38 ansi_re = re.compile('\x1b\[[0-9]{1,2}(;[0-9]{1,2}){0,2}m')
39
40
41 def ansi_strip(s):
42     # http://www.theeggeadventure.com/wikimedia/index.php/Linux_Tips#Use_sed_to_remove_ANSI_colors
43     return ansi_re.sub('', s)
44
45
46 ignored_function_names = set([
47     'glGetString',
48     'glXGetClientString',
49     'glXGetCurrentDisplay',
50     'glXGetProcAddress',
51     'glXGetProcAddressARB',
52     'wglGetProcAddress',
53 ])
54
55
56 def readtrace(trace):
57     p = subprocess.Popen([options.tracedump, trace], stdout=subprocess.PIPE)
58     lines = []
59     for line in p.stdout.readlines():
60         line = ansi_strip(line)
61         mo = call_re.match(line)
62         if mo:
63             function_name = mo.group(2)
64             if function_name in ignored_function_names:
65                 continue
66             lines.append(line[mo.start(2):])
67         else:
68             lines[-1] += line
69     p.wait()
70     return lines
71
72
73 class SDiffer:
74
75     def __init__(self, a, b):
76         self.a = a
77         self.b = b
78
79     def diff(self):
80         matcher = difflib.SequenceMatcher(None, self.a, self.b)
81         for tag, alo, ahi, blo, bhi in matcher.get_opcodes():
82             if tag == 'replace':
83                 g = self.replace(alo, ahi, blo, bhi)
84             elif tag == 'delete':
85                 g = self.delete(alo, ahi)
86             elif tag == 'insert':
87                 g = self.insert(blo, bhi)
88             elif tag == 'equal':
89                 g = self.equal(alo, ahi)
90             else:
91                 raise ValueError, 'unknown tag %s' % (tag,)
92
93             for line in g:
94                 yield line
95
96     def replace(self, alo, ahi, blo, bhi):
97         assert alo < ahi and blo < bhi
98         if bhi - blo < ahi - alo:
99             first  = self.insert(blo, bhi)
100             second = self.delete(alo, ahi)
101         else:
102             first  = self.delete(alo, ahi)
103             second = self.insert(blo, bhi)
104
105         for g in first, second:
106             for line in g:
107                 yield line
108
109     escape = "\33["
110
111     def delete(self, alo, ahi):
112         return self.dump('- ' + self.escape + '9m', self.a, alo, ahi, self.escape + '0m')
113
114     def insert(self, blo, bhi):
115         return self.dump('+ ', self.b, blo, bhi)
116
117     def equal(self, alo, ahi):
118         return self.dump('  ' + self.escape + '2m', self.a, alo, ahi, self.escape + '0m')
119
120     def dump(self, prefix, x, lo, hi, suffix=""):
121         for i in xrange(lo, hi):
122             yield prefix + str(x[i]) + suffix
123
124
125 def main():
126     global options
127
128     optparser = optparse.OptionParser(
129         usage='\n\t%prog <trace> <trace>',
130         version='%%prog')
131     optparser.add_option(
132         '-d', '--tracedump', metavar='PROGRAM',
133         type='string', dest='tracedump', default='tracedump',
134         help='tracedump command [default: %default]')
135
136     (options, args) = optparser.parse_args(sys.argv[1:])
137     if len(args) != 2:
138         optparser.error("incorrect number of arguments")
139
140     ref_lines = readtrace(args[0])
141     src_lines = readtrace(args[1])
142
143     diff = SDiffer(ref_lines, src_lines).diff()
144     sys.stdout.writelines(diff)
145
146
147 if __name__ == '__main__':
148     main()