About **apitrace**:
+* [Official mailing list](http://lists.freedesktop.org/mailman/listinfo/apitrace)
+
* [Zack Rusin's blog introducing the GUI](http://zrusin.blogspot.com/2011/04/apitrace.html)
* [Jose's Fonseca blog introducing the tool](http://jrfonseca.blogspot.com/2008/07/tracing-d3d-applications.html)
#endif
-inline Formatter *defaultFormatter(void) {
+inline Formatter *defaultFormatter(bool color = true) {
+ if (color) {
#ifdef _WIN32
- return new WindowsFormatter;
+ return new WindowsFormatter;
#else
- return new AnsiFormatter;
+ return new AnsiFormatter;
#endif
+ } else {
+ return new Formatter;
+ }
}
static bool
isVariantEditable(const QVariant &var)
{
+ if (var.canConvert<ApiArray>()) {
+ ApiArray array = var.value<ApiArray>();
+ QList<QVariant> vals = array.values();
+ if (vals.isEmpty())
+ return false;
+ else
+ return isVariantEditable(vals.first());
+ }
switch (var.userType()) {
case QVariant::Bool:
case QVariant::Int:
QString argName = argNames[i];
QVariant argValue = originalValues[i];
QVariant editorValue = valueForName(argName, argValue, &valChanged);
+
newValues.append(editorValue);
#if 0
qDebug()<<"Arg = "<<argName;
return arrayFromEditor(array, changed);
}
+ if (!isVariantEditable(originalValue)) {
+ return originalValue;
+ }
+
for (int topRow = 0; topRow < m_model->rowCount(); ++topRow) {
QModelIndex nameIdx = m_model->index(topRow, 0, QModelIndex());
QString argName = nameIdx.data().toString();
+++ /dev/null
-##########################################################################
-#
-# Copyright 2008-2009 VMware, Inc.
-# 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 sys
-
-
-class Formatter:
- '''Plain formatter'''
-
- def __init__(self, stream):
- self.stream = stream
-
- def text(self, text):
- self.stream.write(text)
-
- def newline(self):
- self.text('\n')
-
- def function(self, name):
- self.text(name)
-
- def variable(self, name):
- self.text(name)
-
- def literal(self, value):
- self.text(str(value))
-
- def address(self, addr):
- self.text(str(addr))
-
-
-class AnsiFormatter(Formatter):
- '''Formatter for plain-text files which outputs ANSI escape codes. See
- http://en.wikipedia.org/wiki/ANSI_escape_code for more information
- concerning ANSI escape codes.
- '''
-
- _csi = '\33['
-
- _normal = '0m'
- _bold = '1m'
- _italic = '3m'
- _red = '31m'
- _green = '32m'
- _blue = '34m'
-
- def _escape(self, code):
- self.text(self._csi + code)
-
- def function(self, name):
- self._escape(self._bold)
- Formatter.function(self, name)
- self._escape(self._normal)
-
- def variable(self, name):
- self._escape(self._italic)
- Formatter.variable(self, name)
- self._escape(self._normal)
-
- def literal(self, value):
- self._escape(self._blue)
- Formatter.literal(self, value)
- self._escape(self._normal)
-
- def address(self, value):
- self._escape(self._green)
- Formatter.address(self, value)
- self._escape(self._normal)
-
-
-class WindowsConsoleFormatter(Formatter):
- '''Formatter for the Windows Console. See
- http://code.activestate.com/recipes/496901/ for more information.
- '''
-
- STD_INPUT_HANDLE = -10
- STD_OUTPUT_HANDLE = -11
- STD_ERROR_HANDLE = -12
-
- FOREGROUND_BLUE = 0x01
- FOREGROUND_GREEN = 0x02
- FOREGROUND_RED = 0x04
- FOREGROUND_INTENSITY = 0x08
- BACKGROUND_BLUE = 0x10
- BACKGROUND_GREEN = 0x20
- BACKGROUND_RED = 0x40
- BACKGROUND_INTENSITY = 0x80
-
- _normal = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
- _bold = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY
- _italic = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED
- _red = FOREGROUND_RED | FOREGROUND_INTENSITY
- _green = FOREGROUND_GREEN | FOREGROUND_INTENSITY
- _blue = FOREGROUND_BLUE | FOREGROUND_INTENSITY
-
- def __init__(self, stream):
- Formatter.__init__(self, stream)
-
- if stream is sys.stdin:
- nStdHandle = self.STD_INPUT_HANDLE
- elif stream is sys.stdout:
- nStdHandle = self.STD_OUTPUT_HANDLE
- elif stream is sys.stderr:
- nStdHandle = self.STD_ERROR_HANDLE
- else:
- nStdHandle = None
-
- if nStdHandle:
- import ctypes
- self.handle = ctypes.windll.kernel32.GetStdHandle(nStdHandle)
- else:
- self.handle = None
-
- def _attribute(self, attr):
- if self.handle:
- import ctypes
- ctypes.windll.kernel32.SetConsoleTextAttribute(self.handle, attr)
-
- def function(self, name):
- self._attribute(self._bold)
- Formatter.function(self, name)
- self._attribute(self._normal)
-
- def variable(self, name):
- self._attribute(self._italic)
- Formatter.variable(self, name)
- self._attribute(self._normal)
-
- def literal(self, value):
- self._attribute(self._blue)
- Formatter.literal(self, value)
- self._attribute(self._normal)
-
- def address(self, value):
- self._attribute(self._green)
- Formatter.address(self, value)
- self._attribute(self._normal)
-
-
-def DefaultFormatter(stream):
- if sys.platform in ('linux2', 'cygwin'):
- return AnsiFormatter(stream)
- elif sys.platform in ('win32',):
- return WindowsConsoleFormatter(stream)
- else:
- return Formatter(stream)
-
+++ /dev/null
-#!/usr/bin/env python
-##########################################################################
-#
-# Copyright 2011 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 gzip
-import string
-import struct
-import sys
-
-try:
- from cStringIO import StringIO
-except ImportError:
- from StringIO import StringIO
-
-import format
-
-
-class GzipFile(gzip.GzipFile):
-
- def _read_eof(self):
- # Ignore incomplete files
- try:
- gzip.GzipFile._read_eof(self)
- except IOError:
- pass
-
-
-TRACE_VERSION = 1
-
-EVENT_ENTER, EVENT_LEAVE = range(2)
-
-CALL_END, CALL_ARG, CALL_RET, CALL_THREAD = range(4)
-
-TYPE_NULL, TYPE_FALSE, TYPE_TRUE, TYPE_SINT, TYPE_UINT, TYPE_FLOAT, TYPE_DOUBLE, TYPE_STRING, TYPE_BLOB, TYPE_ENUM, TYPE_BITMASK, TYPE_ARRAY, TYPE_STRUCT, TYPE_OPAQUE = range(14)
-
-
-class Signature:
- '''Dummy class for signatures.'''
-
- pass
-
-
-class Node:
-
- def visit(self, visitor):
- raise NotImplementedError
-
- def pretty_print(self, formatter):
- pretty_printer = PrettyPrinter(formatter)
- self.visit(pretty_printer)
-
- def __str__(self):
- stream = StringIO()
- formatter = format.Formatter(stream)
- self.pretty_print(formatter)
- return stream.getvalue()
-
- def __eq__(self, other):
- raise NotImplementedError
-
- def __hash__(self):
- raise NotImplementedError
-
-
-class Literal(Node):
-
- def __init__(self, value):
- self.value = value
-
- def visit(self, visitor):
- visitor.visit_literal(self)
-
- def __eq__(self, other):
- return \
- self.__class__ == other.__class__ and \
- self.value == other.value
-
- def __hash__(self):
- return hash(self.value)
-
-
-class Enum(Node):
-
- def __init__(self, name, value):
- self.name = name
- self.value = value
-
- def visit(self, visitor):
- visitor.visit_enum(self)
-
- def __eq__(self, other):
- return \
- self.__class__ == other.__class__ and \
- self.name == other.name and \
- self.value == other.value
-
- def __hash__(self):
- return hash(self.value)
-
-
-class Array(Node):
-
- def __init__(self, elements):
- self.elements = tuple(elements)
-
- def visit(self, visitor):
- visitor.visit_array(self)
-
- def __eq__(self, other):
- return \
- self.__class__ == other.__class__ and \
- self.elements == other.elements
-
- def __hash__(self):
- return hash(self.elements)
-
-
-class Pointer(Node):
-
- def __init__(self, value):
- self.value = value
-
- def visit(self, visitor):
- visitor.visit_pointer(self)
-
- def __eq__(self, other):
- return \
- self.__class__ == other.__class__ and \
- self.value == other.value
-
- def __hash__(self):
- return hash(self.value)
-
-
-def Null():
- return Enum("NULL", 0)
-
-def Bitmask(sig, value):
- return Literal(value)
-
-def Blob(buf):
- return Literal('blob(%u)' % len(buf))
-
-
-class Struct(Node):
-
- def __init__(self, sig, members):
- self.sig = sig
- self.members = tuple(members)
-
- def visit(self, visitor):
- visitor.visit_struct(self)
-
- def __eq__(self, other):
- return \
- self.__class__ == other.__class__ and \
- self.sig.member_names == other.sig.member_names and \
- self.members == other.members
-
- def __hash__(self):
- return hash(self.sig.member_names) ^ hash(self.members)
-
-
-class Call(Node):
-
- def __init__(self, sig):
- self.sig = sig
- self.args = [None] * len(sig.arg_names)
- self.ret = None
-
- def get_name(self):
- return self.sig.name
-
- name = property(get_name)
-
- def visit(self, visitor):
- visitor.visit_call(self)
-
- def __eq__(self, other):
- return \
- self.__class__ == other.__class__ and \
- self.sig.name == other.sig.name and \
- self.args == other.args and \
- self.ret == other.ret
-
- def __hash__(self):
- return hash(self.sig.name) ^ hash(tuple(self.args)) ^ hash(self.ret)
-
-
-class Trace(Node):
-
- def __init__(self, calls):
- self.calls = calls
-
- def visit(self, visitor):
- visitor.visit_trace(self)
-
-
-class Visitor:
-
- def visit_literal(self, node):
- raise NotImplementedError
-
- def visit_enum(self, node):
- raise NotImplementedError
-
- def visit_array(self, node):
- raise NotImplementedError
-
- def visit_struct(self, node):
- raise NotImplementedError
-
- def visit_pointer(self, node):
- raise NotImplementedError
-
- def visit_call(self, node):
- raise NotImplementedError
-
- def visit_trace(self, node):
- raise NotImplementedError
-
-
-class PrettyPrinter:
-
- def __init__(self, formatter):
- self.formatter = formatter
-
- def visit_literal(self, node):
- if isinstance(node.value, basestring):
- if len(node.value) >= 4096 or node.value.strip(string.printable):
- self.formatter.text('...')
- return
-
- self.formatter.literal('"' + node.value + '"')
- return
-
- self.formatter.literal(repr(node.value))
-
- def visit_enum(self, node):
- self.formatter.literal(node.name)
-
- def visit_array(self, node):
- self.formatter.text('{')
- sep = ''
- for value in node.elements:
- self.formatter.text(sep)
- value.visit(self)
- sep = ', '
- self.formatter.text('}')
-
- def visit_struct(self, node):
- self.formatter.text('{')
- sep = ''
- for name, value in zip(node.sig.member_names, node.members):
- self.formatter.text(sep)
- self.formatter.variable(name)
- self.formatter.text(' = ')
- value.visit(self)
- sep = ', '
- self.formatter.text('}')
-
- def visit_pointer(self, node):
- self.formatter.address(node.value)
-
- def visit_call(self, node):
- #self.formatter.text('%s ' % node.no)
- self.formatter.function(node.name)
- self.formatter.text('(')
- sep = ''
- for name, value in zip(node.sig.arg_names, node.args):
- self.formatter.text(sep)
- self.formatter.variable(name)
- self.formatter.text(' = ')
- value.visit(self)
- sep = ', '
- self.formatter.text(')')
- if node.ret is not None:
- self.formatter.text(' = ')
- node.ret.visit(self)
-
- def visit_trace(self, node):
- for call in node.calls:
- call.visit(self)
- self.formatter.newline()
-
-
-class Parser:
-
- debug = False
-
- def __init__(self):
- self.file = None
- self.next_call_no = 0
- self.version = 0
-
- self.functions = {}
- self.enums = {}
- self.bitmasks = {}
- self.structs = {}
-
- self.calls = []
-
- def open(self, filename):
- self.file = GzipFile(filename, "rb")
- if not self.file:
- return False
-
- version = self.read_uint()
- if version > TRACE_VERSION:
- sys.stderr.write("error: unsupported trace format version %u\n" % version)
- return False
-
- return True
-
- def parse_call(self):
- if self.debug:
- sys.stdout.flush()
- while True:
- c = self.read_byte()
- if c == EVENT_ENTER:
- self.parse_enter()
- elif c == EVENT_LEAVE:
- return self.parse_leave()
- elif c == -1:
- return None
- else:
- sys.stderr.write("error: unknown event %i\n" % c)
- sys.exit(1)
-
- def parse_enter(self):
- id = self.read_uint()
-
- try:
- sig = self.functions[id]
- except KeyError:
- sig = Signature()
- sig.name = self.read_string()
- num_args = self.read_uint()
- sig.arg_names = tuple([self.read_string() for i in range(num_args)])
- self.functions[id] = sig
-
- call = Call(sig)
- call.no = self.next_call_no
- self.next_call_no += 1
-
- if self.parse_call_details(call):
- self.calls.append(call)
- else:
- del call
-
- def parse_leave(self):
- call_no = self.read_uint()
- call = None
- for i in range(len(self.calls)):
- if self.calls[i].no == call_no:
- call = self.calls.pop(i)
- break
- if call is None:
- return None
-
- if self.parse_call_details(call):
- return call
- else:
- del call
- return None
-
- def parse_call_details(self, call):
- while True:
- c = self.read_byte()
- if c == CALL_END:
- return True
- elif c == CALL_ARG:
- self.parse_arg(call)
- elif c == CALL_RET:
- call.ret = self.parse_value()
- else:
- sys.stderr.write("error: unknown call detail %i\n" % c)
- sys.exit(1)
-
- def parse_arg(self, call):
- index = self.read_uint()
- value = self.parse_value()
- if index >= len(call.args):
- call.args.resize(index + 1)
- call.args[index] = value
- if self.debug:
- sys.stderr.write("\tARG %i %s\n" % (index, value))
-
- def parse_value(self):
- c = self.read_byte()
- if c == TYPE_NULL:
- value = Null()
- elif c == TYPE_FALSE:
- value = Literal(False)
- elif c == TYPE_TRUE:
- value = Literal(True)
- elif c == TYPE_SINT:
- value = self.parse_sint()
- elif c == TYPE_UINT:
- value = self.parse_uint()
- elif c == TYPE_FLOAT:
- value = self.parse_float()
- elif c == TYPE_DOUBLE:
- value = self.parse_double()
- elif c == TYPE_STRING:
- value = self.parse_string()
- elif c == TYPE_ENUM:
- value = self.parse_enum()
- elif c == TYPE_BITMASK:
- value = self.parse_bitmask()
- elif c == TYPE_ARRAY:
- value = self.parse_array()
- elif c == TYPE_STRUCT:
- value = self.parse_struct()
- elif c == TYPE_BLOB:
- value = self.parse_blob()
- elif c == TYPE_OPAQUE:
- value = self.parse_opaque()
- else:
- sys.stderr.write("error: unknown type %i\n" % c)
- sys.exit(1)
- if self.debug:
- sys.stderr.write("\tVALUE %s\n" % value)
- return value
-
- def parse_sint(self):
- return Literal(-self.read_uint())
-
- def parse_uint(self):
- return Literal(self.read_uint())
-
- def parse_float(self):
- value = self.file.read(4)
- value, = struct.unpack('f', value)
- return Literal(value)
-
- def parse_double(self):
- value = self.file.read(8)
- value, = struct.unpack('d', value)
- return Literal(value)
-
- def parse_string(self):
- return Literal(self.read_string())
-
- def parse_enum(self):
- id = self.read_uint()
- try:
- enum = self.enums[id]
- except KeyError:
- name = self.read_string()
- value = self.parse_value()
- enum = Enum(name, value)
- self.enums[id] = enum
- return enum
-
- def parse_bitmask(self):
- id = self.read_uint()
- try:
- sig = self.bitmasks[id]
- except KeyError:
- sig = Signature()
- num_flags = self.read_uint()
- sig.flags = []
- for i in range(num_flags):
- name = self.read_string()
- value = self.read_uint()
- if value == 0 and i:
- sys.stderr.write("warning: bitmask %s is zero but is not first flag\n" % name)
- flag = name, value
- sig.flags.append(flag)
- self.bitmasks[id] = sig
- assert sig
-
- value = self.read_uint()
-
- return Bitmask(sig, value)
-
- def parse_array(self):
- size = self.read_uint()
- elements = [self.parse_value() for i in range(size)]
- return Array(elements)
-
- def parse_blob(self):
- size = self.read_uint()
- if size:
- buf = self.file.read(size)
- else:
- buf = ""
- return Blob(buf)
-
- def parse_struct(self):
- id = self.read_uint()
-
- try:
- sig = self.structs[id]
- except KeyError:
- sig = Signature()
- sig.name = self.read_string()
- num_members = self.read_uint()
- sig.member_names = tuple([self.read_string() for i in range(num_members)])
- self.structs[id] = sig
-
- members = [self.parse_value() for i in range(len(sig.member_names))]
- value = Struct(sig, members)
-
- return value
-
- def parse_opaque(self):
- addr = self.read_uint()
- return Pointer(addr)
-
- def read_string(self):
- size = self.read_uint()
- if size:
- value = self.file.read(size)
- else:
- value = ''
- if self.debug:
- sys.stderr.write("\tSTRING \"%s\"\n" % value)
- return value
-
- def read_uint(self):
- value = 0
- shift = 0
- while True:
- c = self.file.read(1)
- if c == "":
- return 0
- c = ord(c)
- value |= (c & 0x7f) << shift
- shift += 7
- if c & 0x80 == 0:
- break
- if self.debug:
- sys.stderr.write("\tUINT %u\n" % value)
- return value
-
- def read_byte(self):
- c = self.file.read(1)
- if c == "":
- if self.debug:
- sys.stderr.write("\tEOF\n")
- return -1
- else:
- c = ord(c)
- if self.debug:
- sys.stderr.write("\tBYTE 0x%x\n" % c)
- return c
-
-
-def main():
- formatter = format.DefaultFormatter(sys.stdout)
- for arg in sys.argv[1:]:
- parser = Parser()
- parser.open(arg)
- call = parser.parse_call()
- while call:
- formatter.text('%u ' % call.no)
- call.pretty_print(formatter)
- formatter.text('\n')
- call = parser.parse_call()
-
-
-if __name__ == '__main__':
- main()
+++ /dev/null
-#!/usr/bin/env python
-##########################################################################
-#
-# Copyright 2011 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 optparse
-import os.path
-import sys
-
-from trace import Parser
-
-try:
- import debug
-except ImportError:
- pass
-
-
-ignored_function_names = set([
- 'glGetString',
- 'glXGetClientString',
- 'glXGetCurrentDisplay',
- 'glXGetProcAddress',
- 'glXGetProcAddressARB',
- 'wglGetProcAddress',
-])
-
-
-def readtrace(trace, limit=sys.maxint):
- calls = []
- parser = Parser()
- parser.open(trace)
- call = parser.parse_call()
- while call and len(calls) < limit:
- hash(call)
- if call.sig.name not in ignored_function_names:
- calls.append(call)
- call = parser.parse_call()
- return calls
-
-
-class SDiffer:
-
- def __init__(self, a, b):
- self.a = a
- self.b = b
-
- def diff(self):
- matcher = difflib.SequenceMatcher(None, 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)
- elif tag == 'insert':
- self.insert(blo, bhi)
- elif tag == 'equal':
- self.equal(alo, ahi)
- else:
- raise ValueError, 'unknown tag %s' % (tag,)
-
- def replace(self, alo, ahi, blo, bhi):
- assert alo < ahi and blo < bhi
-
- a_names = [call.name for call in self.a[alo:ahi]]
- b_names = [call.name 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)
- elif tag == 'insert':
- self.insert(_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):
- a_call = self.a[alo + i]
- b_call = self.b[blo + i]
- assert a_call.name == b_call.name
- assert len(a_call.args) == len(b_call.args)
- sys.stdout.write(b_call.name + '(')
- sep = ''
- for j in xrange(len(b_call.args)):
- sys.stdout.write(sep)
- self.replace_value(a_call.args[j], b_call.args[j])
- sep = ', '
- sys.stdout.write(')')
- if a_call.ret is not None or b_call.ret is not None:
- sys.stdout.write(' = ')
- self.replace_value(a_call.ret, b_call.ret)
- sys.stdout.write('\n')
-
- 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)
- else:
- first = self.delete(alo, ahi)
- second = self.insert(blo, bhi)
-
- for g in first, second:
- for line in g:
- yield line
-
- def replace_value(self, a, b):
- if b == a:
- sys.stdout.write(str(b))
- else:
- sys.stdout.write('%s -> %s' % (a, b))
-
- escape = "\33["
-
- def delete(self, alo, ahi):
- self.dump('- ' + self.escape + '9m', self.a, alo, ahi, self.escape + '0m')
-
- def insert(self, blo, bhi):
- self.dump('+ ', self.b, blo, bhi)
-
- def equal(self, alo, ahi):
- self.dump(' ' + self.escape + '2m', self.a, alo, ahi, self.escape + '0m')
-
- def dump(self, prefix, x, lo, hi, suffix=""):
- for i in xrange(lo, hi):
- sys.stdout.write(prefix + str(x[i]) + suffix + '\n')
-
-
-def main():
- global options
-
- optparser = optparse.OptionParser(
- usage='\n\t%prog <trace> <trace>',
- version='%%prog')
- optparser.add_option(
- '-d', '--tracedump', metavar='PROGRAM',
- type='string', dest='tracedump', default='tracedump',
- help='tracedump command [default: %default]')
- optparser.add_option(
- '-l', '--limit', metavar='CALLS',
- type='int', dest='limit', default=1000,
- help='limit the number of calls [default: %default]')
-
- (options, args) = optparser.parse_args(sys.argv[1:])
- if len(args) != 2:
- optparser.error("incorrect number of arguments")
-
- ref_calls = readtrace(args[0], options.limit)
- src_calls = readtrace(args[1], options.limit)
-
- differ = SDiffer(ref_calls, src_calls)
- differ.diff()
-
-
-if __name__ == '__main__':
- main()
$TRACEDUMP
stripdump () {
- # http://www.theeggeadventure.com/wikimedia/index.php/Linux_Tips#Use_sed_to_remove_ANSI_colors
- $TRACEDUMP "$1" \
+ $TRACEDUMP --no-color "$1" \
| sed \
- -e 's/\x1b\[[0-9]\{1,2\}\(;[0-9]\{1,2\}\)\{0,2\}m//g' \
-e 's/\r$//g' \
-e 's/^[0-9]\+ //' \
-e 's/hdc = \w\+/hdc/g' \
stripdump "$1" "$FIFO1" &
stripdump "$2" "$FIFO2" &
-sdiff --width=`tput cols` --speed-large-files "$FIFO1" "$FIFO2" | less -R
+sdiff \
+ --width=`tput cols` \
+ --speed-large-files \
+ "$FIFO1" "$FIFO2" \
+| less -R
rm -rf "$FIFODIR"
self.footer(api)
def header(self, api):
- print 'Trace::Writer __writer;'
+ print 'Trace::LocalWriter __writer;'
print
def footer(self, api):
Formatter::Attribute *literal;
public:
- Dumper(std::ostream &_os) : os(_os) {
- formatter = Formatter::defaultFormatter();
+ Dumper(std::ostream &_os, bool color) : os(_os) {
+ formatter = Formatter::defaultFormatter(color);
normal = formatter->normal();
bold = formatter->bold();
italic = formatter->italic();
};
-std::ostream & operator <<(std::ostream &os, Value *value) {
- Dumper d(os);
- if (value) {
- value->visit(d);
- }
- return os;
+void Value::dump(std::ostream &os, bool color) {
+ Dumper d(os, color);
+ visit(d);
}
return null;
}
-std::ostream & operator <<(std::ostream &os, Call &call) {
- Dumper d(os);
- os << call.no << " ";
- d.visit(&call);
- return os;
+void Call::dump(std::ostream &os, bool color) {
+ Dumper d(os, color);
+ os << no << " ";
+ d.visit(this);
}
virtual const char *toString(void) const;
const Value & operator[](size_t index) const;
+
+ void dump(std::ostream &os, bool color=true);
};
};
-std::ostream & operator <<(std::ostream &os, Value *value);
+inline std::ostream & operator <<(std::ostream &os, Value *value) {
+ if (value) {
+ value->dump(os);
+ }
+ return os;
+}
class Call
assert(index < args.size());
return *(args[index]);
}
+
+ void dump(std::ostream &os, bool color=true);
};
-std::ostream & operator <<(std::ostream &os, Call &call);
+inline std::ostream & operator <<(std::ostream &os, Call &call) {
+ call.dump(os);
+ return os;
+}
} /* namespace Trace */
return true;
}
-void
-Writer::open(void) {
-
- static unsigned dwCounter = 0;
-
- const char *szExtension = "trace";
- char szFileName[PATH_MAX];
- const char *lpFileName;
-
- lpFileName = getenv("TRACE_FILE");
- if (lpFileName) {
- strncpy(szFileName, lpFileName, PATH_MAX);
- }
- else {
- char szProcessName[PATH_MAX];
- char szCurrentDir[PATH_MAX];
- OS::GetProcessName(szProcessName, PATH_MAX);
- OS::GetCurrentDir(szCurrentDir, PATH_MAX);
-
- for (;;) {
- FILE *file;
-
- if (dwCounter)
- snprintf(szFileName, PATH_MAX, "%s%c%s.%u.%s", szCurrentDir, PATH_SEP, szProcessName, dwCounter, szExtension);
- else
- snprintf(szFileName, PATH_MAX, "%s%c%s.%s", szCurrentDir, PATH_SEP, szProcessName, szExtension);
-
- file = fopen(szFileName, "rb");
- if (file == NULL)
- break;
-
- fclose(file);
-
- ++dwCounter;
- }
- }
-
- OS::DebugMessage("apitrace: tracing to %s\n", szFileName);
-
- open(szFileName);
-}
-
void inline
Writer::_write(const void *sBuffer, size_t dwBytesToWrite) {
m_file->write(sBuffer, dwBytesToWrite);
}
unsigned Writer::beginEnter(const FunctionSig *sig) {
- OS::AcquireMutex();
-
- if (!m_file->isOpened()) {
- open();
- }
-
_writeByte(Trace::EVENT_ENTER);
_writeUInt(sig->id);
if (!lookup(functions, sig->id)) {
void Writer::endEnter(void) {
_writeByte(Trace::CALL_END);
- OS::ReleaseMutex();
}
void Writer::beginLeave(unsigned call) {
- OS::AcquireMutex();
_writeByte(Trace::EVENT_LEAVE);
_writeUInt(call);
}
void Writer::endLeave(void) {
_writeByte(Trace::CALL_END);
- m_file->flush();
- OS::ReleaseMutex();
}
void Writer::beginArg(unsigned index) {
_writeUInt((size_t)addr);
}
+
+void
+LocalWriter::open(void) {
+
+ static unsigned dwCounter = 0;
+
+ const char *szExtension = "trace";
+ char szFileName[PATH_MAX];
+ const char *lpFileName;
+
+ lpFileName = getenv("TRACE_FILE");
+ if (lpFileName) {
+ strncpy(szFileName, lpFileName, PATH_MAX);
+ }
+ else {
+ char szProcessName[PATH_MAX];
+ char szCurrentDir[PATH_MAX];
+ OS::GetProcessName(szProcessName, PATH_MAX);
+ OS::GetCurrentDir(szCurrentDir, PATH_MAX);
+
+ for (;;) {
+ FILE *file;
+
+ if (dwCounter)
+ snprintf(szFileName, PATH_MAX, "%s%c%s.%u.%s", szCurrentDir, PATH_SEP, szProcessName, dwCounter, szExtension);
+ else
+ snprintf(szFileName, PATH_MAX, "%s%c%s.%s", szCurrentDir, PATH_SEP, szProcessName, szExtension);
+
+ file = fopen(szFileName, "rb");
+ if (file == NULL)
+ break;
+
+ fclose(file);
+
+ ++dwCounter;
+ }
+ }
+
+ OS::DebugMessage("apitrace: tracing to %s\n", szFileName);
+
+ Writer::open(szFileName);
+}
+
+unsigned LocalWriter::beginEnter(const FunctionSig *sig) {
+ OS::AcquireMutex();
+
+ if (!m_file->isOpened()) {
+ open();
+ }
+
+ return Writer::beginEnter(sig);
+}
+
+void LocalWriter::endEnter(void) {
+ Writer::endEnter();
+ OS::ReleaseMutex();
+}
+
+void LocalWriter::beginLeave(unsigned call) {
+ OS::AcquireMutex();
+ Writer::beginLeave(call);
+}
+
+void LocalWriter::endLeave(void) {
+ Writer::endLeave();
+ m_file->flush();
+ OS::ReleaseMutex();
+}
+
+
} /* namespace Trace */
Writer();
~Writer();
- void open(void);
bool open(const char *filename);
void close(void);
void inline _writeString(const char *str);
};
+
+ /**
+ * A specialized Writer class, mean to trace the current process.
+ *
+ * In particular:
+ * - it creates a trace file based on the current process name
+ * - uses mutexes to allow tracing from multiple threades
+ * - flushes the output to ensure the last call is traced in event of
+ * abnormal termination
+ */
+ class LocalWriter : public Writer {
+ protected:
+ public:
+ void open(void);
+
+ unsigned beginEnter(const FunctionSig *sig);
+ void endEnter(void);
+
+ void beginLeave(unsigned call);
+ void endLeave(void);
+ };
}
#endif /* _TRACE_WRITER_HPP_ */
*/
+#include <string.h>
+
#include "trace_parser.hpp"
+static bool color = true;
+
+
+static void usage(void) {
+ std::cout <<
+ "Usage: tracedump [OPTION] [TRACE...]\n"
+ "Dump TRACE to standard output.\n"
+ "\n"
+ " --no-color no colored syntax highlightint\n"
+ " --no-colour alias for --no-color\n"
+ ;
+}
+
+
int main(int argc, char **argv)
{
- for (int i = 1; i < argc; ++i) {
+ int i;
+
+ for (i = 1; i < argc; ++i) {
+ const char *arg = argv[i];
+
+ if (arg[0] != '-') {
+ break;
+ }
+
+ if (!strcmp(arg, "--")) {
+ break;
+ } else if (!strcmp(arg, "--no-color") ||
+ !strcmp(arg, "--no-colour")) {
+ color = false;
+ } else {
+ std::cerr << "error: unknown option " << arg << "\n";
+ usage();
+ return 1;
+ }
+ }
+
+ for (; i < argc; ++i) {
Trace::Parser p;
if (!p.open(argv[i])) {
Trace::Call *call;
while ((call = p.parse_call())) {
- std::cout << *call;
+ call->dump(std::cout, color);
delete call;
}
}
+
return 0;
}