From: José Fonseca Date: Mon, 15 Nov 2010 16:09:14 +0000 (+0000) Subject: More concise data model. X-Git-Url: https://git.cworth.org/git?a=commitdiff_plain;h=51c1ef8138459350ea7fb49798d4e657621f4a77;p=apitrace More concise data model. --- diff --git a/base.py b/base.py index d6ed0a2..f3beda5 100644 --- a/base.py +++ b/base.py @@ -100,14 +100,14 @@ class Concrete(Type): print ' Dump%s(%s);' % (self.id, instance) -class Intrinsic(Concrete): +class Literal(Concrete): - def __init__(self, expr, format): + def __init__(self, expr, format, base=10): Concrete.__init__(self, expr) self.format = format def _dump(self, instance): - print ' Log::TextF("%s", %s);' % (self.format, instance) + print ' Log::Literal%s(%s);' % (self.format, instance) class Const(Type): @@ -143,7 +143,7 @@ class Pointer(Type): print ' Log::EndReference();' print ' }' print ' else' - print ' Log::Text("NULL");' + print ' Log::LiteralNull();' def wrap_instance(self, instance): self.type.wrap_instance("*" + instance) @@ -172,10 +172,10 @@ class Enum(Concrete): print ' switch(%s) {' % instance for value in self.values: print ' case %s:' % value - print ' Log::Text("%s");' % value + print ' Log::LiteralNamedConstant("%s");' % value print ' break;' print ' default:' - print ' Log::TextF("%%i", %s);' % instance + print ' Log::LiteralSInt(%s);' % instance print ' break;' print ' }' @@ -195,21 +195,17 @@ class Flags(Concrete): self.values = values def _dump(self, instance): - print ' bool l_First = true;' print ' %s l_Value = %s;' % (self.type, instance) + print ' Log::BeginBitmask("%s");' % (self.type,) for value in self.values: print ' if((l_Value & %s) == %s) {' % (value, value) - print ' if(!l_First)' - print ' Log::Text(" | ");' - print ' Log::Text("%s");' % value + print ' Log::LiteralNamedConstant("%s");' % value print ' l_Value &= ~%s;' % value - print ' l_First = false;' print ' }' - print ' if(l_Value || l_First) {' - print ' if(!l_First)' - print ' Log::Text(" | ");' + print ' if(l_Value) {' self.type.dump("l_Value"); print ' }' + print ' Log::EndBitmask();' class Array(Type): @@ -221,11 +217,13 @@ class Array(Type): def dump(self, instance): index = '__i' + self.type.id + print ' Log::BeginArray("%s", %s);' % (self.type, self.length) print ' for (int %s = 0; %s < %s; ++%s) {' % (index, index, self.length, index) print ' Log::BeginElement("%s");' % (self.type,) self.type.dump('(%s)[%s]' % (instance, index)) print ' Log::EndElement();' print ' }' + print ' Log::EndArray();' def wrap_instance(self, instance): self.type.wrap_instance("*" + instance) @@ -244,13 +242,16 @@ class Struct(Concrete): def __init__(self, name, members): Concrete.__init__(self, name) + self.name = name self.members = members def _dump(self, instance): + print ' Log::BeginStruct("%s");' % (self.name,) for type, name in self.members: - print ' Log::BeginElement("%s", "%s");' % (type, name) + print ' Log::BeginMember("%s", "%s");' % (type, name) type.dump('(%s).%s' % (instance, name)) - print ' Log::EndElement();' + print ' Log::EndMember();' + print ' Log::EndStruct();' class Alias(Type): @@ -478,7 +479,7 @@ class Interface(Type): class Method(Function): def __init__(self, type, name, args): - Function.__init__(self, type, name, args) + Function.__init__(self, type, name, args, call = '__stdcall') towrap = [] @@ -505,32 +506,36 @@ class _String(Type): Type.__init__(self, "char *") def dump(self, instance): - print ' Log::DumpString((const char *)%s);' % instance + print ' Log::LiteralString((const char *)%s);' % instance String = _String() -class _WString(Type): + +class _Opaque(Type): def __init__(self): - Type.__init__(self, "wchar_t *") + Type.__init__(self, "void *") def dump(self, instance): - print ' Log::DumpWString(%s);' % instance - -WString = _WString() - - -SChar = Intrinsic("signed char", "%i") -UChar = Intrinsic("unsigned char", "%u") -Short = Intrinsic("short", "%i") -Int = Intrinsic("int", "%i") -Long = Intrinsic("long", "%li") -UShort = Intrinsic("unsigned short", "%u") -UInt = Intrinsic("unsigned int", "%u") -ULong = Intrinsic("unsigned long", "%lu") -Float = Intrinsic("float", "%f") -Double = Intrinsic("double", "%f") -SizeT = Intrinsic("size_t", "%lu") + print ' Log::LiteralOpaque((const void *)%s);' % instance + +Opaque = _Opaque() + + +Bool = Literal("bool", "Bool") +SChar = Literal("signed char", "SInt") +UChar = Literal("unsigned char", "UInt") +Short = Literal("short", "SInt") +Int = Literal("int", "SInt") +Long = Literal("long", "SInt") +LongLong = Literal("long long", "SInt") +UShort = Literal("unsigned short", "UInt") +UInt = Literal("unsigned int", "UInt") +ULong = Literal("unsigned long", "UInt") +Float = Literal("float", "Float") +Double = Literal("double", "Float") +SizeT = Literal("size_t", "UInt") +WString = Literal("wchar_t *", "WString") def wrap(): diff --git a/format.py b/format.py new file mode 100644 index 0000000..9842f23 --- /dev/null +++ b/format.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python +########################################################################## +# +# 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) + diff --git a/glx.py b/glx.py index 79a4f46..697e3f9 100644 --- a/glx.py +++ b/glx.py @@ -467,7 +467,7 @@ class GlxGetProcAddressFunction(DllFunction): print ' }' -PROC = Intrinsic("__GLXextFuncPtr", "%p") +PROC = Alias("__GLXextFuncPtr", Opaque) glXgetprocaddress = GlxGetProcAddressFunction(PROC, "glXGetProcAddress", [(Pointer(Const(GLubyte)), "procName")]) libgl.functions.append(glXgetprocaddress) diff --git a/log.cpp b/log.cpp index 2493ec1..2dc3775 100644 --- a/log.cpp +++ b/log.cpp @@ -109,7 +109,7 @@ WriteF(const char *format, ...) } static inline void -Escape(char c) +Escape(wchar_t c) { switch(c) { case '&': @@ -127,8 +127,21 @@ Escape(char c) case '\'': Write("'"); break; + case '\t': + Write(" "); + break; + case '\r': + Write(" "); + break; + case '\n': + Write(" "); + break; default: - Write(c); + if (c >= 0x20 && c <= 0x7e) { + Write((char)c); + } else { + Write('.'); + } } } @@ -141,6 +154,15 @@ Escape(const char *s) } } +static inline void +Escape(const wchar_t *s) +{ + unsigned char c; + while((c = *s++) != 0) { + Escape(c); + } +} + static inline void EscapeF(const char *format, ...) { @@ -255,19 +277,6 @@ void Close(void) { _Close(); } -void Text(const char *text) { - Escape(text); -} - -void TextF(const char *format, ...) { - char szBuffer[4096]; - va_list ap; - va_start(ap, format); - vsnprintf(szBuffer, sizeof(szBuffer), format, ap); - va_end(ap); - Escape(szBuffer); -} - void BeginCall(const char *function) { OS::AcquireMutex(); Indent(1); @@ -303,98 +312,140 @@ void EndReturn(void) { NewLine(); } -void BeginElement(const char *type, const char *name) { - BeginTag("elem", "type", type, "name", name); +void BeginArray(const char *type, size_t length) +{ + BeginTag("array", "type", type); +} + +void EndArray(void) +{ + EndTag("array"); } -void BeginElement(const char *type) { +void BeginElement(const char *type) +{ BeginTag("elem", "type", type); } -void EndElement(void) { +void EndElement(void) +{ EndTag("elem"); } -void BeginReference(const char *type, const void *addr) { +void BeginStruct(const char *type) +{ + BeginTag("struct", "type", type); +} + +void EndStruct(void) +{ + EndTag("struct"); +} + +void BeginMember(const char *type, const char *name) +{ + BeginTag("member", "type", type, "name", name); +} + +void EndMember(void) +{ + EndTag("member"); +} + +void BeginBitmask(const char *type) +{ + BeginTag("bitmask"); +} + +void EndBitmask(void) +{ + EndTag("bitmask"); +} + +void BeginReference(const char *type, const void *addr) +{ char saddr[256]; snprintf(saddr, sizeof(saddr), "%p", addr); BeginTag("ref", "type", type, "addr", saddr); } -void EndReference(void) { +void EndReference(void) +{ EndTag("ref"); } -void DumpString(const char *str) { - const unsigned char *p = (const unsigned char *)str; +void LiteralBool(bool value) +{ + BeginTag("bool"); + WriteF("%u", value ? 0 : 1); + EndTag("bool"); +} + +void LiteralSInt(signed long long value) +{ + BeginTag("int"); + WriteF("%lli", value); + EndTag("int"); +} + +void LiteralUInt(unsigned long long value) +{ + BeginTag("uint"); + WriteF("%llu", value); + EndTag("uint"); +} + +void LiteralFloat(double value) +{ + BeginTag("float"); + WriteF("%f", value); + EndTag("float"); +} + +void LiteralString(const char *str) +{ if (!str) { - Write("NULL"); + LiteralNull(); return; } - Write("\""); - unsigned char c; - while((c = *p++) != 0) { - if(c == '\"') - Write("\\\""); - else if(c == '\\') - Write("\\\\"); - else if(c >= 0x20 && c <= 0x7e) - Write(c); - else if(c == '\t') - Write(" "); - else if(c == '\r') - Write(" "); - else if(c == '\n') - Write(" "); - else { - unsigned char octal0 = c & 0x7; - unsigned char octal1 = (c >> 3) & 0x7; - unsigned char octal2 = (c >> 3) & 0x7; - if(octal2) - WriteF("\\%u%u%u", octal2, octal1, octal0); - else if(octal1) - WriteF("\\%u%u", octal1, octal0); - else - WriteF("\\%u", octal0); - } - } - Write("\""); + BeginTag("string"); + Escape(str); + EndTag("string"); } -void DumpWString(const wchar_t *str) { - const wchar_t *p = str; +void LiteralWString(const wchar_t *str) +{ if (!str) { - Write("NULL"); + LiteralNull(); return; } - Write("L\""); - wchar_t c; - while((c = *p++) != 0) { - if(c == '\"') - Write("\\\""); - else if(c == '\\') - Write("\\\\"); - else if(c >= 0x20 && c <= 0x7e) - Write((char)c); - else if(c == '\t') - Write(" "); - else if(c == '\r') - Write(" "); - else if(c == '\n') - Write(" "); - else { - unsigned octal0 = c & 0x7; - unsigned octal1 = (c >> 3) & 0x7; - unsigned octal2 = (c >> 3) & 0x7; - if(octal2) - WriteF("\\%u%u%u", octal2, octal1, octal0); - else if(octal1) - WriteF("\\%u%u", octal1, octal0); - else - WriteF("\\%u", octal0); - } + BeginTag("wstring"); + Escape(str); + EndTag("wstring"); +} + +void LiteralNamedConstant(const char *str) +{ + BeginTag("const"); + Escape(str); + EndTag("const"); +} + +void LiteralOpaque(const void *addr) +{ + char saddr[256]; + if (!addr) { + LiteralNull(); + return; } - Write("\""); + snprintf(saddr, sizeof(saddr), "%p", addr); + BeginTag("opaque", "addr", saddr); + EndTag("opaque"); +} + +void LiteralNull(void) +{ + Tag("null"); } } /* namespace Log */ diff --git a/log.hpp b/log.hpp index 7ed928b..4dc5337 100644 --- a/log.hpp +++ b/log.hpp @@ -32,9 +32,6 @@ namespace Log { void ReOpen(void); void Close(void); - void Text(const char *text); - void TextF(const char *format, ...); - void BeginCall(const char *function); void EndCall(void); @@ -44,16 +41,33 @@ namespace Log { void BeginReturn(const char *type); void EndReturn(void); + void BeginArray(const char *type, size_t length); + void EndArray(void); + void BeginElement(const char *type); - void BeginElement(const char *type, const char *name); void EndElement(void); + void BeginStruct(const char *type); + void EndStruct(void); + + void BeginMember(const char *type, const char *name); + void EndMember(void); + + void BeginBitmask(const char *type); + void EndBitmask(void); + void BeginReference(const char *type, const void *addr); void EndReference(void); - void DumpString(const char *str); - void DumpWString(const wchar_t *str); - + void LiteralBool(bool value); + void LiteralSInt(signed long long value); + void LiteralUInt(unsigned long long value); + void LiteralFloat(double value); + void LiteralString(const char *str); + void LiteralWString(const wchar_t *str); + void LiteralNamedConstant(const char *str); + void LiteralOpaque(const void *addr); + void LiteralNull(void); } #endif /* _LOG_HPP_ */ diff --git a/model.py b/model.py new file mode 100644 index 0000000..8c461f7 --- /dev/null +++ b/model.py @@ -0,0 +1,238 @@ +#!/usr/bin/env python +########################################################################## +# +# 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. +# +##########################################################################/ + + +'''Trace data model.''' + + +import sys +import string +import format + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + + +class Node: + + def visit(self, visitor): + raise NotImplementedError + + def __str__(self): + stream = StringIO() + formatter = format.DefaultFormatter(stream) + pretty_printer = PrettyPrinter(formatter) + self.visit(pretty_printer) + return stream.getvalue() + + +class Literal(Node): + + def __init__(self, value): + self.value = value + + def visit(self, visitor): + visitor.visit_literal(self) + + +class NamedConstant(Node): + + def __init__(self, name): + self.name = name + + def visit(self, visitor): + visitor.visit_named_constant(self) + + +class Bitmask(Node): + + def __init__(self, elements): + self.elements = elements + + def visit(self, visitor): + visitor.visit_bitmask(self) + + +class Array(Node): + + def __init__(self, elements): + self.elements = elements + + def visit(self, visitor): + visitor.visit_array(self) + + +class Struct(Node): + + def __init__(self, name, members): + self.name = name + self.members = members + + def visit(self, visitor): + visitor.visit_struct(self) + + +class Pointer(Node): + + def __init__(self, address, value): + self.address = address + self.address = value + + def visit(self, visitor): + visitor.visit_pointer(self) + + +class Call(Node): + + def __init__(self, no, name, args, ret, attrs = None): + self.no = no + self.name = name + self.args = args + self.ret = ret + if attrs is None: + self.attrs = {} + else: + self.attrs = attrs + + def visit(self, visitor): + visitor.visit_call(self) + + +class Trace: + + 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_named_constant(self, node): + raise NotImplementedError + + def visit_bitmask(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(str(node.value)) + + def visit_named_constant(self, node): + self.formatter.literal(node.name) + + def visit_bitmask(self, node): + if len(node.elements) == 0: + self.formatter.literal('0') + return + if len(node.elements) > 1: + self.formatter.text('(') + sep = '' + for value in node.elements: + self.formatter.text(sep) + value.visit(self) + sep = ' | ' + if len(node.elements) > 1: + self.formatter.text(')') + + 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 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.address) + + def visit_call(self, node): + self.formatter.text('%s ' % node.no) + self.formatter.function(node.name) + self.formatter.text('(') + sep = '' + for name, value in 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() + diff --git a/opengl32.py b/opengl32.py index 7d3b4cd..43a9e85 100644 --- a/opengl32.py +++ b/opengl32.py @@ -439,7 +439,7 @@ opengl32.functions += [ ] HGLRC = Alias("HGLRC", HANDLE) -PROC = Intrinsic("PROC", "%p") +PROC = Alias("PROC", Opaque) PFD = Flags(DWORD, [ "PFD_DOUBLEBUFFER", diff --git a/windows.py b/windows.py index 9aebf1b..5aca759 100644 --- a/windows.py +++ b/windows.py @@ -27,22 +27,23 @@ from base import * -SHORT = Intrinsic("SHORT", "%i") -USHORT = Intrinsic("USHORT", "%u") -INT = Intrinsic("INT", "%i") -UINT = Intrinsic("UINT", "%u") -LONG = Intrinsic("LONG", "%li") -ULONG = Intrinsic("ULONG", "%lu") -FLOAT = Intrinsic("FLOAT", "%f") +SHORT = Alias("SHORT", Short) +USHORT = Alias("USHORT", UShort) +INT = Alias("INT", Int) +UINT = Alias("UINT", UInt) +LONG = Alias("LONG", Long) +ULONG = Alias("ULONG", ULong) +LONGLONG = Alias("LONGLONG", LongLong) +FLOAT = Alias("FLOAT", Float) -INT32 = Intrinsic("INT32", "%i") -UINT32 = Intrinsic("UINT32", "%i") +INT32 = Literal("INT32", "UInt") +UINT32 = Literal("UINT32", "UInt") -BYTE = Intrinsic("BYTE", "0x%02lx") -WORD = Intrinsic("WORD", "0x%04lx") -DWORD = Intrinsic("DWORD", "0x%08lx") +BYTE = Literal("BYTE", "UInt", base=16) +WORD = Literal("WORD", "UInt", base=16) +DWORD = Literal("DWORD", "UInt", base=16) -BOOL = Intrinsic("BOOL", "%i") +BOOL = Alias("BOOL", Bool) LPLONG = Pointer(LONG) LPWORD = Pointer(WORD) @@ -55,17 +56,20 @@ LPCSTR = Const(String) LPWSTR = WString LPCWSTR = Const(WString) -LARGE_INTEGER = Intrinsic("LARGE_INTEGER", "0x%llx") +LARGE_INTEGER = Struct("LARGE_INTEGER", [ + (LONGLONG, 'QuadPart'), +]) + SIZE_T = Alias("SIZE_T", SizeT) HRESULT = Alias("HRESULT", Int) -PVOID = Intrinsic("PVOID", "%p") +PVOID = Alias("PVOID", Opaque) LPVOID = PVOID -HANDLE = Intrinsic("HANDLE", "%p") -HWND = Intrinsic("HWND", "%p") -HDC = Intrinsic("HDC", "%p") -HMONITOR = Intrinsic("HMONITOR", "%p") +HANDLE = Alias("HANDLE", Opaque) +HWND = Alias("HWND", Opaque) +HDC = Alias("HDC", Opaque) +HMONITOR = Alias("HMONITOR", Opaque) GUID = Struct("GUID", [ (DWORD, "Data1"), diff --git a/xml2txt.py b/xml2txt.py index 80e74ee..e0c5efd 100755 --- a/xml2txt.py +++ b/xml2txt.py @@ -30,6 +30,8 @@ import optparse import xml.parsers.expat import gzip +from model import * + ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4) @@ -194,68 +196,11 @@ class GzipFile(gzip.GzipFile): pass -class Formatter: - - def function(self, name): - return name - - def variable(self, name): - return name - - def literal(self, value): - return str(value) - - def address(self, addr): - return 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, text): - return self._csi + code + text + self._csi + self._normal - - def function(self, name): - text = Formatter.function(self, name) - return self._escape(self._bold, text) - - def variable(self, name): - text = Formatter.variable(self, name) - return self._escape(self._italic, text) - - def literal(self, value): - text = Formatter.literal(self, value) - return self._escape(self._blue, text) - - def address(self, value): - text = Formatter.address(self, value) - return self._escape(self._green, text) - - -def DefaultFormatter(): - if sys.platform in ('linux2', 'cygwin'): - return AnsiFormatter() - else: - return Formatter() - - class TraceParser(XmlParser): - def __init__(self, stream, formatter): + def __init__(self, stream): XmlParser.__init__(self, stream) - self.formatter = formatter + self.call_no = 0 def parse(self): self.element_start('trace') @@ -269,15 +214,16 @@ class TraceParser(XmlParser): name = attrs['name'] args = [] ret = None - duration = None + properties = {} while self.token.type == ELEMENT_START: if self.token.name_or_data == 'arg': arg = self.parse_arg() args.append(arg) elif self.token.name_or_data == 'ret': ret = self.parse_ret() - elif self.token.name_or_data == 'duration': - duration = self.parse_duration() + elif self.token.name_or_data in ('duration', 'starttsc', 'endtsc'): + property = self.token.name_or_data + properties[property] = self.parse_hex(self.token.name_or_data) elif self.token.name_or_data == 'call': # ignore nested function calls self.parse_call() @@ -285,7 +231,11 @@ class TraceParser(XmlParser): raise TokenMismatch(" or ", self.token) self.element_end('call') - self.handle_call(name, args, ret, duration) + self.call_no += 1 + + call = Call(self.call_no, name, args, ret, properties) + + self.handle_call(call) def parse_arg(self): attrs = self.element_start('arg') @@ -302,18 +252,28 @@ class TraceParser(XmlParser): return value - def parse_duration(self): - attrs = self.element_start('duration') - value = int(self.character_data()) - self.element_end('duration') + def parse_hex(self, token_name): + attrs = self.element_start(token_name) + value = int(self.character_data(), 16) + self.element_end(token_name) return value def parse_value(self): - if self.token.type == CHARACTER_DATA: - return self.formatter.literal(self.character_data()) if self.token.type == ELEMENT_START: - if self.token.name_or_data == 'elem': - return self.parse_elems() + if self.token.name_or_data == 'int': + return self.parse_int() + if self.token.name_or_data == 'uint': + return self.parse_uint() + if self.token.name_or_data == 'float': + return self.parse_float() + if self.token.name_or_data == 'string': + return self.parse_string() + if self.token.name_or_data == 'wstring': + return self.parse_wstring() + if self.token.name_or_data == 'const': + return self.parse_const() + if self.token.name_or_data == 'bitmask': + return self.parse_bitmask() if self.token.name_or_data == 'ref': return self.parse_ref() raise TokenMismatch(", , or text", self.token) @@ -322,7 +282,7 @@ class TraceParser(XmlParser): elems = [self.parse_elem()] while self.token.type != ELEMENT_END: elems.append(self.parse_elem()) - return '{' + ', '.join(elems) + '}' + return Struct("", elems) def parse_elem(self): attrs = self.element_start('elem') @@ -332,39 +292,79 @@ class TraceParser(XmlParser): try: name = attrs['name'] except KeyError: - pass - else: - value = name + ' = ' + value + name = "" - return value + return name, value def parse_ref(self): attrs = self.element_start('ref') if self.token.type != ELEMENT_END: - value = '&' + self.parse_value() + value = self.parse_value() else: - value = self.formatter.address(attrs['addr']) + value = None self.element_end('ref') - return value + return Pointer(attrs['addr'], value) - def handle_call(self, name, args, ret, duration): - s = '' + def parse_bitmask(self): + self.element_start('bitmask') + elems = [] + while self.token.type != ELEMENT_END: + elems.append(self.parse_value()) + self.element_end('bitmask') + return Bitmask(elems) + + def parse_int(self): + self.element_start('int') + value = self.character_data() + self.element_end('int') + return Literal(int(value)) + + def parse_uint(self): + self.element_start('uint') + value = self.character_data() + self.element_end('uint') + return Literal(int(value)) + + def parse_float(self): + self.element_start('float') + value = self.character_data() + self.element_end('float') + return Literal(float(value)) + + def parse_string(self): + self.element_start('string') + value = self.character_data() + self.element_end('string') + return Literal(value) + + def parse_wstring(self): + self.element_start('wstring') + value = self.character_data() + self.element_end('wstring') + return Literal(value) + + def parse_const(self): + self.element_start('const') + value = self.character_data() + self.element_end('const') + return NamedConstant(value) + + def handle_call(self, call): + pass + + +class DumpTraceParser(TraceParser): - #if duration is not None: - # s += '%8u ' % (duration) + def __init__(self, stream, formatter): + XmlParser.__init__(self, stream) + self.formatter = formatter + self.pretty_printer = PrettyPrinter(self.formatter) + self.call_no = 0 - s += self.formatter.function(name) - s += '(' + ', '.join([self.formatter.variable(name) + ' = ' + value for name, value in args]) + ')' - if ret is not None: - s += ' = ' + ret - s += '\n' - - try: - sys.stdout.write(s) - except IOError: - # catch broken pipe - sys.exit(0) + def handle_call(self, call): + call.visit(self.pretty_printer) + self.formatter.newline() class StatsTraceParser(TraceParser): @@ -393,46 +393,59 @@ class StatsTraceParser(TraceParser): self.stats[name] = nr_calls, total_duration -def main(): - parser = optparse.OptionParser( - usage="\n\t%prog [options] [file] ...") - parser.add_option( - '-s', '--stats', - action="store_true", - dest="stats", default=False, - help="generate statistics instead") - parser.add_option( - '--color', '--colour', - type="choice", choices=('never', 'always', 'auto'), metavar='WHEN', - dest="color", default="always", - help="coloring: never, always, or auto [default: %default]") - (options, args) = parser.parse_args(sys.argv[1:]) - - if options.color == 'always' or options.color == 'auto' and sys.stdout.isatty(): - formatter = DefaultFormatter() - else: - formatter = Formatter() +class Main: + + def __init__(self): + pass + + def main(self): + optparser = self.get_optparser() + (options, args) = optparser.parse_args(sys.argv[1:]) - if options.stats: - factory = StatsTraceParser - else: - factory = TraceParser - - if args: - for arg in args: - if arg.endswith('.gz'): - stream = GzipFile(arg, 'rb') - elif arg.endswith('.bz2'): - from bz2 import BZ2File - stream = BZ2File(arg, 'rt') - else: - stream = open(arg, 'rt') - parser = factory(stream, formatter) - parser.parse() - else: - parser = factory(sys.stdin, formatter) - parser.parse() + if args: + for arg in args: + if arg.endswith('.gz'): + from gzip import GzipFile + stream = GzipFile(arg, 'rb') + elif arg.endswith('.bz2'): + from bz2 import BZ2File + stream = BZ2File(arg, 'rU') + else: + stream = open(arg, 'rt') + self.process_arg(stream, options) + else: + self.process_arg(stream, options) + + def get_optparser(self): + optparser = optparse.OptionParser( + usage="\n\t%prog [options] [traces] ...") + optparser.add_option( + '-s', '--stats', + action="store_true", + dest="stats", default=False, + help="generate statistics instead") + optparser.add_option( + '--color', '--colour', + type="choice", choices=('never', 'always', 'auto'), metavar='WHEN', + dest="color", default="always", + help="coloring: never, always, or auto [default: %default]") + return optparser + + def process_arg(self, stream, options): + if options.color == 'always' or options.color == 'auto' and sys.stdout.isatty(): + formatter = format.DefaultFormatter(sys.stdout) + else: + formatter = format.Formatter(sys.stdout) + + if options.stats: + factory = StatsTraceParser + else: + factory = DumpTraceParser + + parser = DumpTraceParser(stream, formatter) + parser.parse() if __name__ == '__main__': - main() + Main().main() +