X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=scripts%2Ftracediff2.py;fp=scripts%2Ftracediff2.py;h=0000000000000000000000000000000000000000;hb=8f9116e676de8147558e451c38761a3dabba1df3;hp=255191e8c5bf93bd9a744ef558b4c28e7f386e2d;hpb=262e041bdbbdb9304936893bf4373800827fbbd8;p=apitrace diff --git a/scripts/tracediff2.py b/scripts/tracediff2.py deleted file mode 100755 index 255191e..0000000 --- a/scripts/tracediff2.py +++ /dev/null @@ -1,341 +0,0 @@ -#!/usr/bin/env python -########################################################################## -# -# Copyright 2011-2012 Jose Fonseca -# 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 -# 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 -# furnished to do so, subject to the following conditions: -# -# 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 -# 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 -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. -# -##########################################################################/ - - -import difflib -import itertools -import optparse -import os.path -import subprocess -import sys - -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): - if call.functionName not in ignoredFunctionNames: - self.rebuilder.visitCall(call) - self.calls.append(call) - - -def readtrace(trace, calls): - p = subprocess.Popen( - args = [ - options.apitrace, - 'pickle', - '--symbolic', - '--calls=' + calls, - trace - ], - stdout = subprocess.PIPE, - ) - - parser = Loader(p.stdout) - parser.parse() - return parser.calls - - -class SDiffer: - - 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(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, blo, bhi) - elif tag == 'insert': - self.insert(alo, ahi, blo, bhi) - elif tag == 'equal': - 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 - - a_names = [call.functionName for call in self.a[alo:ahi]] - b_names = [call.functionName 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, _blo, _bhi) - elif tag == 'insert': - self.insert(_alo, _ahi, _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): - self.highlighter.write('| ') - a_call = self.a[alo + i] - b_call = self.b[blo + i] - assert a_call.functionName == b_call.functionName - 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 = '' - numArgs = max(len(a_call.args), len(b_call.args)) - for j in xrange(numArgs): - self.highlighter.write(sep) - 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: - self.highlighter.write(' = ') - self.replace_value(a_call.ret, b_call.ret) - self.highlighter.write('\n') - - def replace_dissimilar(self, alo, ahi, blo, bhi): - assert alo < ahi and blo < bhi - if bhi - blo < ahi - alo: - self.insert(alo, alo, blo, bhi) - self.delete(alo, ahi, bhi, bhi) - else: - self.delete(alo, ahi, blo, blo) - self.insert(ahi, ahi, blo, bhi) - - def replace_value(self, a, b): - if b == a: - self.highlighter.write(self.dumper.visit(b)) - else: - self.highlighter.strike() - self.highlighter.color(self.delete_color) - self.highlighter.write(self.dumper.visit(a)) - self.highlighter.normal() - self.highlighter.write(" ") - self.highlighter.color(self.insert_color) - self.highlighter.write(self.dumper.visit(b)) - self.highlighter.normal() - - escape = "\33[" - - 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 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 dumpCallNos(self, aNo, bNo): - if not self.callNos: - return - - 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(): - '''Main program. - ''' - - # Parse command line options - optparser = optparse.OptionParser( - usage='\n\t%prog ', - version='%%prog') - optparser.add_option( - '-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='*', - 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") - - 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(): - highlighter = LessHighlighter() - else: - highlighter = ColorHighlighter() - - differ = SDiffer(ref_calls, src_calls, highlighter, options.call_nos) - try: - differ.diff() - except IOError: - pass - - -if __name__ == '__main__': - main()