From 3cf1e9a7d501fd8e662b3e28575429e72f433398 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jos=C3=A9=20Fonseca?= Date: Fri, 16 Mar 2012 15:46:26 +0000 Subject: [PATCH] Several improvements to trace diffing. --- scripts/tracediff.py | 175 +++++++++++++++++++++++------------------- scripts/tracediff2.py | 66 ++++++++++++---- 2 files changed, 144 insertions(+), 97 deletions(-) diff --git a/scripts/tracediff.py b/scripts/tracediff.py index c023e0e..b3933a9 100755 --- a/scripts/tracediff.py +++ b/scripts/tracediff.py @@ -27,42 +27,33 @@ import platform import optparse -import os +import os.path import shutil import subprocess import sys import tempfile -import time -def stripdump(trace, fifo): - dump = subprocess.Popen( - args = [ +class Dumper: + + def __init__(self, trace, calls): + self.output = tempfile.NamedTemporaryFile() + + dump_args = [ options.apitrace, 'dump', '--color=never', + '--call-nos=no', '--arg-names=no', - '--calls=' + options.calls, + '--calls=' + 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, - ) + ] - # XXX: Avoid a weird race condition - time.sleep(0.01) + self.dump = subprocess.Popen( + args = dump_args, + stdout = self.output, + universal_newlines = True, + ) if platform.system() == 'Windows': @@ -77,59 +68,63 @@ else: end_insert = '\33[0m' -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 - if options.diff == 'diff': - diff_args = [ - 'diff', - '--speed-large-files', - '--old-line-format=' + start_delete + '%l' + end_delete + '\n', - '--new-line-format=' + start_insert + '%l' + end_insert + '\n', - ] - elif options.diff == 'sdiff': - diff_args = [ - 'sdiff', - '--width=%u' % options.width, - '--speed-large-files', - ] - elif options.diff == 'wdiff': - diff_args = [ - 'wdiff', - #'--terminal', - '--avoid-wraps', - '--start-delete=' + start_delete, - '--end-delete=' + end_delete, - '--start-insert=' + start_insert, - '--end-insert=' + end_insert, - ] - else: - assert False - diff_args += fifos - - diff = subprocess.Popen( - args = diff_args, - stdout = subprocess.PIPE, - universal_newlines = True, - ) +def diff(ref_trace, src_trace): + + ref_dumper = Dumper(ref_trace, options.ref_calls) + src_dumper = Dumper(src_trace, options.src_calls) + + # TODO use difflib instead + if options.diff == 'diff': + diff_args = [ + 'diff', + '--speed-large-files', + '--old-line-format=' + start_delete + '%l' + end_delete + '\n', + '--new-line-format=' + start_insert + '%l' + end_insert + '\n', + ] + elif options.diff == 'sdiff': + diff_args = [ + 'sdiff', + '--width=%u' % options.width, + '--speed-large-files', + ] + elif options.diff == 'wdiff': + diff_args = [ + 'wdiff', + #'--terminal', + '--avoid-wraps', + '--start-delete=' + start_delete, + '--end-delete=' + end_delete, + '--start-insert=' + start_insert, + '--end-insert=' + end_insert, + ] + else: + assert False + diff_args += [ref_dumper.output.name, src_dumper.output.name] + ref_dumper.dump.wait() + src_dumper.dump.wait() + + less = None + if sys.stdout.isatty(): less = subprocess.Popen( args = ['less', '-FRXn'], - stdin = diff.stdout + stdin = subprocess.PIPE ) - less.wait() + diff_stdout = less.stdin + else: + diff_stdout = None - finally: - shutil.rmtree(fifodir) + diff = subprocess.Popen( + args = diff_args, + stdout = diff_stdout, + universal_newlines = True, + ) + + diff.wait() + + if less is not None: + less.wait() def which(executable): @@ -158,13 +153,7 @@ def main(): '''Main program. ''' - if which('wdiff'): - default_diff = 'wdiff' - elif which('sdiff'): - default_diff = 'sdiff' - else: - default_diff = 'diff' - + # Determine default options default_width = columns() # Parse command line options @@ -178,12 +167,20 @@ def main(): optparser.add_option( '-d', '--diff', type="choice", choices=('diff', 'sdiff', 'wdiff'), - dest="diff", default=default_diff, - help="diff program: diff, sdiff, or wdiff [default: %default]") + dest="diff", default=None, + help="diff program: wdiff, sdiff, or diff [default: auto]") optparser.add_option( '-c', '--calls', metavar='CALLSET', type="string", dest="calls", default='1-10000', help="calls to compare [default: %default]") + optparser.add_option( + '--ref-calls', metavar='CALLSET', + type="string", dest="ref_calls", default=None, + help="calls to compare from reference trace") + optparser.add_option( + '--src-calls', metavar='CALLSET', + type="string", dest="src_calls", default=None, + help="calls to compare from source trace") optparser.add_option( '-w', '--width', metavar='NUM', type="string", dest="width", default=default_width, @@ -194,7 +191,23 @@ def main(): if len(args) != 2: optparser.error("incorrect number of arguments") - diff(args) + if options.diff is None: + if which('wdiff'): + options.diff = 'wdiff' + else: + sys.stderr.write('warning: wdiff not found\n') + if which('sdiff'): + options.diff = 'sdiff' + else: + sys.stderr.write('warning: sdiff not found\n') + options.diff = 'diff' + + if options.ref_calls is None: + options.ref_calls = options.calls + if options.src_calls is None: + options.src_calls = options.calls + + diff(*args) if __name__ == '__main__': diff --git a/scripts/tracediff2.py b/scripts/tracediff2.py index bba4ebe..3729802 100755 --- a/scripts/tracediff2.py +++ b/scripts/tracediff2.py @@ -32,7 +32,7 @@ import subprocess import sys from unpickle import Unpickler -from highlight import LessHighlighter +from highlight import ColorHighlighter, LessHighlighter ignoredFunctionNames = set([ @@ -52,19 +52,18 @@ class Loader(Unpickler): self.calls = [] def handleCall(self, call): - call.no = None - hash(call) if call.functionName not in ignoredFunctionNames: self.calls.append(call) + hash(call) -def readtrace(trace): +def readtrace(trace, calls): p = subprocess.Popen( args = [ options.apitrace, 'pickle', '--symbolic', - '--calls=' + options.calls, + '--calls=' + calls, trace ], stdout = subprocess.PIPE, @@ -78,15 +77,16 @@ def readtrace(trace): class SDiffer: - def __init__(self, a, b, highlighter): + def __init__(self, a, b, highlighter, callNos = False): self.a = a self.b = b self.highlighter = highlighter self.delete_color = highlighter.red self.insert_color = highlighter.green + self.callNos = callNos def diff(self): - matcher = difflib.SequenceMatcher(None, self.a, self.b) + matcher = difflib.SequenceMatcher(self.isjunk, self.a, self.b) for tag, alo, ahi, blo, bhi in matcher.get_opcodes(): if tag == 'replace': self.replace(alo, ahi, blo, bhi) @@ -95,10 +95,13 @@ class SDiffer: elif tag == 'insert': self.insert(blo, bhi) elif tag == 'equal': - self.equal(alo, ahi) + self.equal(alo, ahi, blo, bhi) else: raise ValueError, 'unknown tag %s' % (tag,) + def isjunk(self, call): + return call.functionName == 'glGetError' and call.ret in ('GL_NO_ERROR', 0) + def replace(self, alo, ahi, blo, bhi): assert alo < ahi and blo < bhi @@ -131,6 +134,9 @@ class SDiffer: assert a_call.functionName == b_call.functionName assert len(a_call.args) == len(b_call.args) self.replace_prefix() + if self.callNos: + self.replace_value(a_call.no, b_call.no) + self.highlighter.write(' ') self.highlighter.bold(True) self.highlighter.write(b_call.functionName) self.highlighter.bold(False) @@ -163,10 +169,12 @@ class SDiffer: if b == a: self.highlighter.write(str(b)) else: + self.highlighter.strike() self.highlighter.color(self.delete_color) self.highlighter.write(str(a)) self.highlighter.normal() - self.highlighter.write(" -> ") + #self.highlighter.write(" -> ") + self.highlighter.write(" ") self.highlighter.color(self.insert_color) self.highlighter.write(str(b)) self.highlighter.normal() @@ -179,13 +187,18 @@ class SDiffer: def insert(self, blo, bhi): self.dump(self.insert_prefix, self.b, blo, bhi, self.normal_suffix) - def equal(self, alo, ahi): - self.dump(self.equal_prefix, self.a, alo, ahi, self.normal_suffix) + def equal(self, alo, ahi, blo, bhi): + if self.callNos: + self.replace_similar(alo, ahi, blo, bhi) + else: + self.dump(self.equal_prefix, self.b, blo, bhi, self.normal_suffix) def dump(self, prefix, x, lo, hi, suffix): for i in xrange(lo, hi): call = x[i] prefix() + if self.callNos: + self.highlighter.write(str(call.no) + ' ') self.highlighter.bold(True) self.highlighter.write(call.functionName) self.highlighter.bold(False) @@ -197,6 +210,7 @@ class SDiffer: def delete_prefix(self): self.highlighter.write('- ') + self.highlighter.strike() self.highlighter.color(self.delete_color) def insert_prefix(self): @@ -229,18 +243,38 @@ def main(): '-c', '--calls', metavar='CALLSET', type="string", dest="calls", default='1-10000', help="calls to compare [default: %default]") - + optparser.add_option( + '--ref-calls', metavar='CALLSET', + type="string", dest="ref_calls", default=None, + help="calls to compare from reference trace") + optparser.add_option( + '--src-calls', metavar='CALLSET', + type="string", dest="src_calls", default=None, + help="calls to compare from source trace") + optparser.add_option( + '--call-nos', + action="store_true", + dest="call_nos", default=False, + help="dump call numbers") 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]) + if options.ref_calls is None: + options.ref_calls = options.calls + if options.src_calls is None: + options.src_calls = options.calls + + ref_calls = readtrace(args[0], options.ref_calls) + src_calls = readtrace(args[1], options.src_calls) - highlighter = LessHighlighter() + if sys.stdout.isatty(): + highlighter = LessHighlighter() + else: + highlighter = ColorHighlighter() - differ = SDiffer(ref_calls, src_calls, highlighter) + differ = SDiffer(ref_calls, src_calls, highlighter, options.call_nos) differ.diff() -- 2.43.0