2 ##########################################################################
4 # Copyright 2011 Jose Fonseca
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:
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
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
25 ##########################################################################/
36 call_re = re.compile('^([0-9]+) (\w+)\(')
38 ansi_re = re.compile('\x1b\[[0-9]{1,2}(;[0-9]{1,2}){0,2}m')
42 # http://www.theeggeadventure.com/wikimedia/index.php/Linux_Tips#Use_sed_to_remove_ANSI_colors
43 return ansi_re.sub('', s)
46 ignored_function_names = set([
49 'glXGetCurrentDisplay',
51 'glXGetProcAddressARB',
57 p = subprocess.Popen([options.tracedump, trace], stdout=subprocess.PIPE)
59 for line in p.stdout.readlines():
60 line = ansi_strip(line)
61 mo = call_re.match(line)
63 function_name = mo.group(2)
64 if function_name in ignored_function_names:
66 lines.append(line[mo.start(2):])
75 def __init__(self, a, b):
80 matcher = difflib.SequenceMatcher(None, self.a, self.b)
81 for tag, alo, ahi, blo, bhi in matcher.get_opcodes():
83 g = self.replace(alo, ahi, blo, bhi)
85 g = self.delete(alo, ahi)
87 g = self.insert(blo, bhi)
89 g = self.equal(alo, ahi)
91 raise ValueError, 'unknown tag %s' % (tag,)
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)
102 first = self.delete(alo, ahi)
103 second = self.insert(blo, bhi)
105 for g in first, second:
111 def delete(self, alo, ahi):
112 return self.dump('- ' + self.escape + '9m', self.a, alo, ahi, self.escape + '0m')
114 def insert(self, blo, bhi):
115 return self.dump('+ ', self.b, blo, bhi)
117 def equal(self, alo, ahi):
118 return self.dump(' ' + self.escape + '2m', self.a, alo, ahi, self.escape + '0m')
120 def dump(self, prefix, x, lo, hi, suffix=""):
121 for i in xrange(lo, hi):
122 yield prefix + str(x[i]) + suffix
128 optparser = optparse.OptionParser(
129 usage='\n\t%prog <trace> <trace>',
131 optparser.add_option(
132 '-d', '--tracedump', metavar='PROGRAM',
133 type='string', dest='tracedump', default='tracedump',
134 help='tracedump command [default: %default]')
136 (options, args) = optparser.parse_args(sys.argv[1:])
138 optparser.error("incorrect number of arguments")
140 ref_lines = readtrace(args[0])
141 src_lines = readtrace(args[1])
143 diff = SDiffer(ref_lines, src_lines).diff()
144 sys.stdout.writelines(diff)
147 if __name__ == '__main__':