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':
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):
'''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
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,
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__':
import sys
from unpickle import Unpickler
-from highlight import LessHighlighter
+from highlight import ColorHighlighter, LessHighlighter
ignoredFunctionNames = set([
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,
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)
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
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)
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()
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)
def delete_prefix(self):
self.highlighter.write('- ')
+ self.highlighter.strike()
self.highlighter.color(self.delete_color)
def insert_prefix(self):
'-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()