X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=scripts%2Ftracediff.py;h=827293d0b40ace25a9fd55287d3509588b9ce5a3;hb=a65795f10ab268c2b2c4e079c715eca6f47c2239;hp=2506eb752111faa793b127ddbd249f94bc730146;hpb=03263ab4bdd31537bc4f48a825225a47b4b74a86;p=apitrace diff --git a/scripts/tracediff.py b/scripts/tracediff.py index 2506eb7..827293d 100755 --- a/scripts/tracediff.py +++ b/scripts/tracediff.py @@ -5,7 +5,7 @@ # All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the 'Software'), to deal +# of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is @@ -14,7 +14,7 @@ # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # -# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER @@ -25,160 +25,109 @@ ##########################################################################/ -import difflib +import platform import optparse -import os.path +import os +import shutil +import subprocess import sys - -from trace import Parser - -try: - import debug -except ImportError: - pass - - -ignored_function_names = set([ - 'glGetString', - 'glXGetClientString', - 'glXGetCurrentDisplay', - 'glXGetProcAddress', - 'glXGetProcAddressARB', - 'wglGetProcAddress', -]) - - -def readtrace(trace): - calls = [] - parser = Parser() - parser.open(trace) - call = parser.parse_call() - while call and len(calls) < 1000: - hash(call) - if call.sig.name not in ignored_function_names: - calls.append(call) - call = parser.parse_call() - return calls - - -class SDiffer: - - def __init__(self, a, b): - self.a = a - self.b = b - - def diff(self): - matcher = difflib.SequenceMatcher(None, self.a, self.b) - for tag, alo, ahi, blo, bhi in matcher.get_opcodes(): - if tag == 'replace': - self.replace(alo, ahi, blo, bhi) - elif tag == 'delete': - self.delete(alo, ahi) - elif tag == 'insert': - self.insert(blo, bhi) - elif tag == 'equal': - self.equal(alo, ahi) - else: - raise ValueError, 'unknown tag %s' % (tag,) - - def replace(self, alo, ahi, blo, bhi): - assert alo < ahi and blo < bhi - - a_names = [call.name for call in self.a[alo:ahi]] - b_names = [call.name for call in self.b[blo:bhi]] - - matcher = difflib.SequenceMatcher(None, a_names, b_names) - for tag, _alo, _ahi, _blo, _bhi in matcher.get_opcodes(): - _alo += alo - _ahi += alo - _blo += blo - _bhi += blo - if tag == 'replace': - self.replace_dissimilar(_alo, _ahi, _blo, _bhi) - elif tag == 'delete': - self.delete(_alo, _ahi) - elif tag == 'insert': - self.insert(_blo, _bhi) - elif tag == 'equal': - self.replace_similar(_alo, _ahi, _blo, _bhi) - else: - raise ValueError, 'unknown tag %s' % (tag,) - - def replace_similar(self, alo, ahi, blo, bhi): - assert alo < ahi and blo < bhi - assert ahi - alo == bhi - blo - for i in xrange(0, bhi - blo): - a_call = self.a[alo + i] - b_call = self.b[blo + i] - assert a_call.name == b_call.name - assert len(a_call.args) == len(b_call.args) - sys.stdout.write(b_call.name + '(') - sep = '' - for j in xrange(len(b_call.args)): - sys.stdout.write(sep) - self.replace_value(a_call.args[j], b_call.args[j]) - sep = ', ' - sys.stdout.write(')') - if a_call.ret is not None or b_call.ret is not None: - sys.stdout.write(' = ') - self.replace_value(a_call.ret, b_call.ret) - sys.stdout.write('\n') - - def replace_dissimilar(self, alo, ahi, blo, bhi): - assert alo < ahi and blo < bhi - if bhi - blo < ahi - alo: - first = self.insert(blo, bhi) - second = self.delete(alo, ahi) - else: - first = self.delete(alo, ahi) - second = self.insert(blo, bhi) - - for g in first, second: - for line in g: - yield line - - def replace_value(self, a, b): - if b == a: - sys.stdout.write(str(b)) - else: - sys.stdout.write('%s -> %s' % (a, b)) - - escape = "\33[" - - def delete(self, alo, ahi): - self.dump('- ' + self.escape + '9m', self.a, alo, ahi, self.escape + '0m') - - def insert(self, blo, bhi): - self.dump('+ ', self.b, blo, bhi) - - def equal(self, alo, ahi): - self.dump(' ' + self.escape + '2m', self.a, alo, ahi, self.escape + '0m') - - def dump(self, prefix, x, lo, hi, suffix=""): - for i in xrange(lo, hi): - sys.stdout.write(prefix + str(x[i]) + suffix + '\n') +import tempfile + + +def stripdump(trace, fifo): + dump = subprocess.Popen( + args = [ + options.apitrace, + 'dump', + '--color=never', + '--arg-names=no', + '--calls=' + options.calls, + trace + ], + stdout = subprocess.PIPE, + universal_newlines = True, + ) + + sed = subprocess.Popen( + args = [ + 'sed', + '-e', r's/\r$//g', + '-e', r's/^[0-9]\+ //', + '-e', r's/hdc = \w\+/hdc/g', + ], + stdin = dump.stdout, + stdout = open(fifo, 'wt') + universal_newlines = True, + ) + + +def diff(traces): + fifodir = tempfile.mkdtemp() + try: + fifos = [] + for i in range(len(traces)): + trace = traces[i] + fifo = os.path.join(fifodir, str(i)) + stripdump(trace, fifo) + fifos.append(fifo) + + # TODO use difflib instead + sdiff = subprocess.Popen( + args = [ + 'sdiff', + '--width=%u' % options.width, + '--speed-large-files', + ] + fifos, + stdout = subprocess.PIPE + universal_newlines = True, + ) + + less = subprocess.Popen( + args = ['less', '-FRXn'], + stdin = sdiff.stdout + ) + + less.wait() + + finally: + shutil.rmtree(fifodir) + + +def columns(): + import curses + curses.setupterm() + return curses.tigetnum('cols') def main(): - global options + '''Main program. + ''' + default_width = columns() + + # Parse command line options optparser = optparse.OptionParser( - usage='\n\t%prog ', + usage='\n\t%prog [options] -- TRACE_FILE TRACE_FILE', version='%%prog') optparser.add_option( - '-d', '--tracedump', metavar='PROGRAM', - type='string', dest='tracedump', default='tracedump', - help='tracedump command [default: %default]') + '-a', '--apitrace', metavar='PROGRAM', + type='string', dest='apitrace', default='apitrace', + help='apitrace command [default: %default]') + optparser.add_option( + '-c', '--calls', metavar='CALLSET', + type="string", dest="calls", default='1-10000', + help="calls to compare [default: %default]") + optparser.add_option( + '-w', '--width', metavar='NUM', + type="string", dest="width", default=default_width, + help="columns [default: %default]") + global options (options, args) = optparser.parse_args(sys.argv[1:]) if len(args) != 2: optparser.error("incorrect number of arguments") - ref_calls = readtrace(args[0]) - src_calls = readtrace(args[1]) - - differ = SDiffer(ref_calls, src_calls) - differ.diff() + diff(args) if __name__ == '__main__':