]> git.cworth.org Git - apitrace/commitdiff
Several improvements to trace diffing.
authorJosé Fonseca <jose.r.fonseca@gmail.com>
Fri, 16 Mar 2012 15:46:26 +0000 (15:46 +0000)
committerJosé Fonseca <jose.r.fonseca@gmail.com>
Fri, 16 Mar 2012 15:46:26 +0000 (15:46 +0000)
scripts/tracediff.py
scripts/tracediff2.py

index c023e0e395bd35b3df0a51983bbfa3b65abc92d0..b3933a9d59ee1b11a5e6b0b9b81327345d9aa4ca 100755 (executable)
 
 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__':
index bba4ebead1e3f287ff00f54b7b3d04ab142a84f0..37298024feeb476720c3813158257667bf04bedf 100755 (executable)
@@ -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()