X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=scripts%2Ftracediff2.py;h=255191e8c5bf93bd9a744ef558b4c28e7f386e2d;hb=0ee878906f76cfe44ea6206112179600501efe52;hp=001f3226aaed5f117ad400e8a2f90cc2e8cc2ffb;hpb=587ca79001b473729f496829656c93e1fe4c21a0;p=apitrace diff --git a/scripts/tracediff2.py b/scripts/tracediff2.py index 001f322..255191e 100755 --- a/scripts/tracediff2.py +++ b/scripts/tracediff2.py @@ -1,7 +1,7 @@ #!/usr/bin/env python ########################################################################## # -# Copyright 2011 Jose Fonseca +# Copyright 2011-2012 Jose Fonseca # All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -26,50 +26,80 @@ import difflib +import itertools import optparse import os.path import subprocess import sys -from unpickle import Unpickler -from highlight import Highlighter +from unpickle import Unpickler, Dumper, Rebuilder +from highlight import ColorHighlighter, LessHighlighter ignoredFunctionNames = set([ 'glGetString', 'glXGetClientString', 'glXGetCurrentDisplay', + 'glXGetCurrentContext', 'glXGetProcAddress', 'glXGetProcAddressARB', 'wglGetProcAddress', ]) +class Blob: + '''Data-less proxy for bytearrays, to save memory.''' + + def __init__(self, size, hash): + self.size = size + self.hash = hash + + def __repr__(self): + return 'blob(%u)' % self.size + + def __eq__(self, other): + return isinstance(other, Blob) and self.size == other.size and self.hash == other.hash + + def __hash__(self): + return self.hash + + +class BlobReplacer(Rebuilder): + '''Replace blobs with proxys.''' + + def visitByteArray(self, obj): + return Blob(len(obj), hash(str(obj))) + + def visitCall(self, call): + call.args = map(self.visit, call.args) + call.ret = self.visit(call.ret) + + class Loader(Unpickler): def __init__(self, stream): Unpickler.__init__(self, stream) self.calls = [] + self.rebuilder = BlobReplacer() def handleCall(self, call): - call.no = None - hash(call) if call.functionName not in ignoredFunctionNames: + self.rebuilder.visitCall(call) self.calls.append(call) -def readtrace(trace): +def readtrace(trace, calls): p = subprocess.Popen( args = [ options.apitrace, 'pickle', - '--calls=' + options.calls, + '--symbolic', + '--calls=' + calls, trace ], stdout = subprocess.PIPE, ) - calls = [] parser = Loader(p.stdout) parser.parse() return parser.calls @@ -77,27 +107,34 @@ 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 + self.aSpace = 0 + self.bSpace = 0 + self.dumper = Dumper() 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 == 'delete': - self.delete(alo, ahi) + self.delete(alo, ahi, blo, bhi) elif tag == 'insert': - self.insert(blo, bhi) + self.insert(alo, ahi, 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 @@ -113,9 +150,9 @@ class SDiffer: if tag == 'replace': self.replace_dissimilar(_alo, _ahi, _blo, _bhi) elif tag == 'delete': - self.delete(_alo, _ahi) + self.delete(_alo, _ahi, _blo, _bhi) elif tag == 'insert': - self.insert(_blo, _bhi) + self.insert(_alo, _ahi, _blo, _bhi) elif tag == 'equal': self.replace_similar(_alo, _ahi, _blo, _bhi) else: @@ -125,18 +162,28 @@ class SDiffer: assert alo < ahi and blo < bhi assert ahi - alo == bhi - blo for i in xrange(0, bhi - blo): + self.highlighter.write('| ') a_call = self.a[alo + i] b_call = self.b[blo + i] assert a_call.functionName == b_call.functionName - assert len(a_call.args) == len(b_call.args) - self.equal_prefix() + self.dumpCallNos(a_call.no, b_call.no) self.highlighter.bold(True) self.highlighter.write(b_call.functionName) self.highlighter.bold(False) + self.highlighter.write('(') sep = '' - for j in xrange(len(b_call.args)): + numArgs = max(len(a_call.args), len(b_call.args)) + for j in xrange(numArgs): self.highlighter.write(sep) - self.replace_value(a_call.args[j], b_call.args[j]) + try: + a_arg = a_call.args[j] + except IndexError: + pass + try: + b_arg = b_call.args[j] + except IndexError: + pass + self.replace_value(a_arg, b_arg) sep = ', ' self.highlighter.write(')') if a_call.ret is not None or b_call.ret is not None: @@ -147,65 +194,93 @@ class SDiffer: 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) + self.insert(alo, alo, blo, bhi) + self.delete(alo, ahi, bhi, bhi) else: - first = self.delete(alo, ahi) - second = self.insert(blo, bhi) - - for g in first, second: - for line in g: - yield line + self.delete(alo, ahi, blo, blo) + self.insert(ahi, ahi, blo, bhi) def replace_value(self, a, b): if b == a: - self.highlighter.write(str(b)) + self.highlighter.write(self.dumper.visit(b)) else: + self.highlighter.strike() self.highlighter.color(self.delete_color) - self.highlighter.write(str(b)) + self.highlighter.write(self.dumper.visit(a)) self.highlighter.normal() - self.highlighter.write(" -> ") + self.highlighter.write(" ") self.highlighter.color(self.insert_color) - self.highlighter.write(str(b)) + self.highlighter.write(self.dumper.visit(b)) self.highlighter.normal() escape = "\33[" - def delete(self, alo, ahi): - self.dump(self.delete_prefix, self.a, alo, ahi, self.normal_suffix) - - 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 dump(self, prefix, x, lo, hi, suffix): - for i in xrange(lo, hi): - call = x[i] - prefix() - self.highlighter.bold(True) - self.highlighter.write(call.functionName) - self.highlighter.bold(False) - self.highlighter.write('(' + ', '.join(map(repr, call.args)) + ')') - if call.ret is not None: - self.highlighter.write(' = ' + repr(call.ret)) - suffix() - self.highlighter.write('\n') + def delete(self, alo, ahi, blo, bhi): + assert alo < ahi + assert blo == bhi + for i in xrange(alo, ahi): + call = self.a[i] + self.highlighter.write('- ') + self.dumpCallNos(call.no, None) + self.highlighter.strike() + self.highlighter.color(self.delete_color) + self.dumpCall(call) + + def insert(self, alo, ahi, blo, bhi): + assert alo == ahi + assert blo < bhi + for i in xrange(blo, bhi): + call = self.b[i] + self.highlighter.write('+ ') + self.dumpCallNos(None, call.no) + self.highlighter.color(self.insert_color) + self.dumpCall(call) - def delete_prefix(self): - self.highlighter.write('- ') - self.highlighter.color(self.delete_color) - - def insert_prefix(self): - self.highlighter.write('+ ') - self.highlighter.color(self.insert_color) + def equal(self, alo, ahi, blo, bhi): + assert alo < ahi and blo < bhi + assert ahi - alo == bhi - blo + for i in xrange(0, bhi - blo): + self.highlighter.write(' ') + a_call = self.a[alo + i] + b_call = self.b[blo + i] + assert a_call.functionName == b_call.functionName + assert len(a_call.args) == len(b_call.args) + self.dumpCallNos(a_call.no, b_call.no) + self.dumpCall(b_call) - def equal_prefix(self): - self.highlighter.write(' ') + def dumpCallNos(self, aNo, bNo): + if not self.callNos: + return - def normal_suffix(self): + if aNo is None: + self.highlighter.write(' '*self.aSpace) + else: + aNoStr = str(aNo) + self.highlighter.strike() + self.highlighter.color(self.delete_color) + self.highlighter.write(aNoStr) + self.highlighter.normal() + self.aSpace = len(aNoStr) + self.highlighter.write(' ') + if bNo is None: + self.highlighter.write(' '*self.bSpace) + else: + bNoStr = str(bNo) + self.highlighter.color(self.insert_color) + self.highlighter.write(bNoStr) + self.highlighter.normal() + self.bSpace = len(bNoStr) + self.highlighter.write(' ') + + def dumpCall(self, call): + self.highlighter.bold(True) + self.highlighter.write(call.functionName) + self.highlighter.bold(False) + self.highlighter.write('(' + ', '.join(itertools.imap(self.dumper.visit, call.args)) + ')') + if call.ret is not None: + self.highlighter.write(' = ' + self.dumper.visit(call.ret)) self.highlighter.normal() + self.highlighter.write('\n') def main(): @@ -222,31 +297,44 @@ def main(): help='apitrace command [default: %default]') optparser.add_option( '-c', '--calls', metavar='CALLSET', - type="string", dest="calls", default='1-10000', + type="string", dest="calls", default='*', 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) if sys.stdout.isatty(): - less = subprocess.Popen( - args = ['less', '-FRXn'], - stdin = subprocess.PIPE - ) - highlighter = Highlighter(less.stdin, True) + highlighter = LessHighlighter() else: - highlighter = Highlighter(sys.stdout) - - differ = SDiffer(ref_calls, src_calls, highlighter) - differ.diff() - less.stdin.close() + highlighter = ColorHighlighter() - less.wait() + differ = SDiffer(ref_calls, src_calls, highlighter, options.call_nos) + try: + differ.diff() + except IOError: + pass if __name__ == '__main__':