diff options
Diffstat (limited to 'src/gallium')
| -rw-r--r-- | src/gallium/state_trackers/python/retrace/README | 18 | ||||
| -rwxr-xr-x | src/gallium/state_trackers/python/retrace/interpreter.py | 418 | ||||
| -rwxr-xr-x | src/gallium/state_trackers/python/retrace/model.py | 151 | ||||
| -rwxr-xr-x | src/gallium/state_trackers/python/retrace/parser.py | 332 | 
4 files changed, 919 insertions, 0 deletions
| diff --git a/src/gallium/state_trackers/python/retrace/README b/src/gallium/state_trackers/python/retrace/README new file mode 100644 index 0000000000..c0ad8ae6c1 --- /dev/null +++ b/src/gallium/state_trackers/python/retrace/README @@ -0,0 +1,18 @@ +This is  + + +To use it follow the instructions in src/gallium/drivers/trace/README and +src/gallium/state_trackers/python/README, and then do + +  python src/gallium/state_trackers/python/samples/retrace/interpreter.py filename.trace + + + + +This is still work in progress: +- not everything is captured/replayed +  - surface/textures contents +- any tiny error will result in a crash + +-- +Jose Fonseca <jrfonseca@tungstengraphics.com> diff --git a/src/gallium/state_trackers/python/retrace/interpreter.py b/src/gallium/state_trackers/python/retrace/interpreter.py new file mode 100755 index 0000000000..ae62bc04a8 --- /dev/null +++ b/src/gallium/state_trackers/python/retrace/interpreter.py @@ -0,0 +1,418 @@ +#!/usr/bin/env python +############################################################################# +# +# Copyright 2008 Tungsten Graphics, Inc. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + + +import sys +import gallium +import model +from parser import TraceParser + + +def make_image(surface): +    pixels = gallium.FloatArray(surface.height*surface.width*4) +    surface.get_tile_rgba(0, 0, surface.width, surface.height, pixels) + +    import Image +    outimage = Image.new( +        mode='RGB', +        size=(surface.width, surface.height), +        color=(0,0,0)) +    outpixels = outimage.load() +    for y in range(0, surface.height): +        for x in range(0, surface.width): +            offset = (y*surface.width + x)*4 +            r, g, b, a = [int(pixels[offset + ch]*255) for ch in range(4)] +            outpixels[x, y] = r, g, b +    return outimage + +def save_image(filename, surface): +    outimage = make_image(surface) +    outimage.save(filename, "PNG") + +def show_image(surface): +    outimage = make_image(surface) +     +    import Tkinter as tk +    from PIL import Image, ImageTk +    root = tk.Tk() +     +    root.title('background image') +     +    image1 = ImageTk.PhotoImage(outimage) +    w = image1.width() +    h = image1.height() +    x = 100 +    y = 100 +    root.geometry("%dx%d+%d+%d" % (w, h, x, y)) +    panel1 = tk.Label(root, image=image1) +    panel1.pack(side='top', fill='both', expand='yes') +    panel1.image = image1 +    root.mainloop() + + + + +class Struct: +    """C-like struct""" + +    # A basic Python class can pass as a C-like structure +    pass + + +struct_factories = { +    "pipe_blend_color": gallium.BlendColor, +    "pipe_blend_state": gallium.Blend, +    "pipe_clip_state": gallium.Clip, +    #"pipe_constant_buffer": gallium.ConstantBuffer, +    "pipe_depth_state": gallium.Depth, +    "pipe_stencil_state": gallium.Stencil, +    "pipe_alpha_state": gallium.Alpha, +    "pipe_depth_stencil_alpha_state": gallium.DepthStencilAlpha, +    "pipe_format_block": gallium.FormatBlock, +    #"pipe_framebuffer_state": gallium.Framebuffer, +    "pipe_poly_stipple": gallium.PolyStipple, +    "pipe_rasterizer_state": gallium.Rasterizer, +    "pipe_sampler_state": gallium.Sampler, +    "pipe_scissor_state": gallium.Scissor, +    #"pipe_shader_state": gallium.Shader, +    #"pipe_vertex_buffer": gallium.VertexBuffer, +    "pipe_vertex_element": gallium.VertexElement, +    "pipe_viewport_state": gallium.Viewport, +    #"pipe_texture": gallium.Texture, +} + + +member_array_factories = { +    "pipe_rasterizer_state": {"sprite_coord_mode": gallium.ByteArray},                           +    "pipe_poly_stipple": {"stipple": gallium.UnsignedArray},                           +    "pipe_viewport_state": {"scale": gallium.FloatArray, "translate": gallium.FloatArray},                           +    "pipe_clip_state": {"ucp": gallium.FloatArray}, +    "pipe_depth_stencil_alpha_state": {"stencil": gallium.StencilArray}, +    "pipe_blend_color": {"color": gallium.FloatArray},                          +} + + +class Translator(model.Visitor): +    """Translate model arguments into regular Python objects""" + +    def __init__(self, interpreter): +        self.interpreter = interpreter +        self.result = None + +    def visit(self, node): +        self.result = None +        node.visit(self) +        return self.result +         +    def visit_literal(self, node): +        self.result = node.value +     +    def visit_named_constant(self, node): +        # lookup the named constant in the gallium module +        self.result = getattr(gallium, node.name) +     +    def visit_array(self, node): +        array = [] +        for element in node.elements: +            array.append(self.visit(element)) +        self.result = array +     +    def visit_struct(self, node): +        struct_factory = struct_factories.get(node.name, Struct) +        struct = struct_factory() +        for member_name, member_node in node.members: +            member_value = self.visit(member_node) +            try: +                array_factory = member_array_factories[node.name][member_name] +            except KeyError: +                pass +            else: +                assert isinstance(member_value, list) +                array = array_factory(len(member_value)) +                for i in range(len(member_value)): +                    array[i] = member_value[i] +                member_value = array +            #print node.name, member_name, member_value +            assert isinstance(struct, Struct) or hasattr(struct, member_name) +            setattr(struct, member_name, member_value) +        self.result = struct +     +    def visit_pointer(self, node): +        self.result = self.interpreter.lookup_object(node.address) + + +class Object: +     +    def __init__(self, interpreter, real): +        self.interpreter = interpreter +        self.real = real +         + +class Global(Object): + +    def __init__(self, interpreter, real): +        self.interpreter = interpreter +        self.real = real +         +    def pipe_winsys_create(self): +        return Winsys(self.interpreter, gallium.Device()) + +    def pipe_screen_create(self, winsys): +        return Screen(self.interpreter, winsys.real) +     +    def pipe_context_create(self, screen): +        context = screen.real.context_create() +        return Context(self.interpreter, context) + +     +class Winsys(Object): +     +    def __init__(self, interpreter, real): +        self.interpreter = interpreter +        self.real = real + +    def get_name(self): +        pass +     +    def buffer_create(self, alignment, usage, size): +        return self.real.buffer_create(size, alignment, usage) +     +    def buffer_destroy(self, buffer): +        pass +     +    def buffer_write(self, buffer, data, size): +        buffer.write(data, size) +        buffer.write(data, size) + + +class Screen(Object): +     +    def get_name(self): +        pass +     +    def get_vendor(self): +        pass +     +    def get_param(self, param): +        pass +     +    def get_paramf(self, param): +        pass +     +    def is_format_supported(self, format, target, tex_usage, geom_flags): +        return self.real.is_format_supported(format, target, tex_usage, geom_flags) +     +    def texture_create(self, template): +        return self.real.texture_create( +            format = template.format, +            width = template.width[0], +            height = template.height[0], +            depth = template.depth[0], +            last_level = template.last_level, +            target = template.target, +            tex_usage = template.tex_usage, +        ) + +    def texture_release(self, texture): +        self.interpreter.unregister_object(texture) + +    def get_tex_surface(self, texture, face, level, zslice, usage): +        return texture.get_surface(face, level, zslice, usage) +     +    def tex_surface_release(self, surface): +        self.interpreter.unregister_object(surface) + +    def surface_map(self, surface, flags): +        return None +     +    def surface_unmap(self, surface): +        pass + + +class Context(Object): +     +    def __init__(self, interpreter, real): +        Object.__init__(self, interpreter, real) +        self.cbufs = [] +        self.zsbuf = None + +    def create_blend_state(self, state): +        return state + +    def bind_blend_state(self, state): +        self.real.set_blend(state) +         +    def create_sampler_state(self, state): +        return state + +    def bind_sampler_states(self, n, states): +        for i in range(n): +            self.real.set_sampler(i, states[i]) +         +    def create_rasterizer_state(self, state): +        return state + +    def bind_rasterizer_state(self, state): +        self.real.set_rasterizer(state) +         +    def create_depth_stencil_alpha_state(self, state): +        return state + +    def bind_depth_stencil_alpha_state(self, state): +        self.real.set_depth_stencil_alpha(state) +     +    def create_fs_state(self, state): +        tokens = str(state.tokens) +        shader = gallium.Shader(tokens) +        return shader + +    create_vs_state = create_fs_state +     +    def bind_fs_state(self, state): +        self.real.set_fragment_shader(state) +         +    def bind_vs_state(self, state): +        self.real.set_vertex_shader(state) + +    def set_blend_color(self, state): +        self.real.set_blend_color(state) + +    def set_clip_state(self, state): +        self.real.set_clip(state) + +    def set_constant_buffer(self, shader, index, state): +        self.real.set_constant_buffer(shader, index, state.buffer) + +    def set_framebuffer_state(self, state): +        _state = gallium.Framebuffer() +        _state.width = state.width +        _state.height = state.height +        _state.num_cbufs = state.num_cbufs +        for i in range(len(state.cbufs)): +            _state.set_cbuf(i, state.cbufs[i]) +        _state.set_zsbuf(state.zsbuf)     +        self.real.set_framebuffer(_state) +         +        self.cbufs = state.cbufs +        self.zsbuf = state.zsbuf + +    def set_polygon_stipple(self, state): +        self.real.set_polygon_stipple(state) + +    def set_scissor_state(self, state): +        self.real.set_scissor(state) + +    def set_viewport_state(self, state): +        self.real.set_viewport(state) + +    def set_sampler_textures(self, n, textures): +        for i in range(n): +            self.real.set_sampler_textures(textures[i]) + +    def set_vertex_buffers(self, n, vbufs): +        for i in range(n): +            vbuf = vbufs[i] +            self.real.set_vertex_buffer( +                i, +                pitch = vbuf.pitch, +                max_index = vbuf.max_index, +                buffer_offset = vbuf.buffer_offset, +                buffer = vbuf.buffer, +            ) + +    def set_vertex_elements(self, n, elements): +        for i in range(n): +            self.real.set_vertex_element(i, elements[i]) +        self.real.set_vertex_elements(n) + +    def set_edgeflags(self, bitfield): +        # FIXME +        pass +     +    def draw_arrays(self, mode, start, count): +        self.real.draw_arrays(mode, start, count) +        self._update() +         +    def flush(self, flags, fence): +        self.real.flush(flags) + +    def clear(self, surface, value): +        self.real.surface_clear(surface, value) +         +    def _update(self): +        self.real.flush() +     +        if self.cbufs and self.cbufs[0]: +            show_image(self.cbufs[0]) +     + +class Interpreter: +     +    def __init__(self): +        self.objects = {} +        self.result = None +        self.globl = Global(self, None) + +    def register_object(self, address, object): +        self.objects[address] = object +         +    def unregister_object(self, object): +        # FIXME: +        pass + +    def lookup_object(self, address): +        return self.objects[address] +     +    def interpret(self, trace): +        for call in trace.calls: +            self.interpret_call(call) + +    def interpret_call(self, call): +        sys.stderr.write("%s\n" % call) +         +        args = [self.interpret_arg(arg) for name, arg in call.args]  +         +        if call.klass: +            obj = args[0] +            args = args[1:] +        else: +            obj = self.globl +             +        method = getattr(obj, call.method) +        ret = method(*args) +         +        if call.ret and isinstance(call.ret, model.Pointer): +            self.register_object(call.ret.address, ret) + +    def interpret_arg(self, node): +        translator = Translator(self) +        return translator.visit(node) +     + +def main(): +    for arg in sys.argv[1:]: +        parser = TraceParser(open(arg, 'rt')) +        trace = parser.parse() +        interpreter = Interpreter() +        interpreter.interpret(trace) + + +if __name__ == '__main__': +    main() diff --git a/src/gallium/state_trackers/python/retrace/model.py b/src/gallium/state_trackers/python/retrace/model.py new file mode 100755 index 0000000000..c17b5db9f8 --- /dev/null +++ b/src/gallium/state_trackers/python/retrace/model.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python +############################################################################# +# +# Copyright 2008 Tungsten Graphics, Inc. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + + +'''Trace data model.''' + + +class Node: +     +    def visit(self, visitor): +        raise NotImplementedError + + +class Literal(Node): +     +    def __init__(self, value): +        self.value = value + +    def visit(self, visitor): +        visitor.visit_literal(self) +         +    def __str__(self): +        if isinstance(self.value, str) and len(self.value) > 32: +            return '...' +        else:  +            return repr(self.value) + + +class NamedConstant(Node): +     +    def __init__(self, name): +        self.name = name + +    def visit(self, visitor): +        visitor.visit_named_constant(self) +         +    def __str__(self): +        return self.name +     + +class Array(Node): +     +    def __init__(self, elements): +        self.elements = elements + +    def visit(self, visitor): +        visitor.visit_array(self) +         +    def __str__(self): +        return '{' + ', '.join([str(value) for value in self.elements]) + '}' + + +class Struct(Node): +     +    def __init__(self, name, members): +        self.name = name +        self.members = members         + +    def visit(self, visitor): +        visitor.visit_struct(self) +         +    def __str__(self): +        return '{' + ', '.join([name + ' = ' + str(value) for name, value in self.members]) + '}' + +         +class Pointer(Node): +     +    def __init__(self, address): +        self.address = address + +    def visit(self, visitor): +        visitor.visit_pointer(self) +         +    def __str__(self): +        return hex(self.address) + + +class Call: +     +    def __init__(self, klass, method, args, ret): +        self.klass = klass +        self.method = method +        self.args = args +        self.ret = ret +         +    def visit(self, visitor): +        visitor.visit_call(self) +         +    def __str__(self): +        s = self.method +        if self.klass: +            s = self.klass + '::' + s +        s += '(' + ', '.join([name + ' = ' + str(value) for name, value in self.args]) + ')' +        if self.ret is not None: +            s += ' = ' + str(self.ret) +        return s + + +class Trace: +     +    def __init__(self, calls): +        self.calls = calls +         +    def visit(self, visitor): +        visitor.visit_trace(self) +         +    def __str__(self): +        return '\n'.join([str(call) for call in self.calls]) +     +     +class Visitor: +     +    def visit_literal(self, node): +        raise NotImplementedError +     +    def visit_named_constant(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 +     + diff --git a/src/gallium/state_trackers/python/retrace/parser.py b/src/gallium/state_trackers/python/retrace/parser.py new file mode 100755 index 0000000000..571d8f6eae --- /dev/null +++ b/src/gallium/state_trackers/python/retrace/parser.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python +############################################################################# +# +# Copyright 2008 Tungsten Graphics, Inc. +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# +############################################################################# + + +import sys +import xml.parsers.expat +import binascii + +from model import * + + +ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF = range(4) + + +class XmlToken: + +    def __init__(self, type, name_or_data, attrs = None, line = None, column = None): +        assert type in (ELEMENT_START, ELEMENT_END, CHARACTER_DATA, EOF) +        self.type = type +        self.name_or_data = name_or_data +        self.attrs = attrs +        self.line = line +        self.column = column + +    def __str__(self): +        if self.type == ELEMENT_START: +            return '<' + self.name_or_data + ' ...>' +        if self.type == ELEMENT_END: +            return '</' + self.name_or_data + '>' +        if self.type == CHARACTER_DATA: +            return self.name_or_data +        if self.type == EOF: +            return 'end of file' +        assert 0 + + +class XmlTokenizer: +    """Expat based XML tokenizer.""" + +    def __init__(self, fp, skip_ws = True): +        self.fp = fp +        self.tokens = [] +        self.index = 0 +        self.final = False +        self.skip_ws = skip_ws +         +        self.character_pos = 0, 0 +        self.character_data = '' +         +        self.parser = xml.parsers.expat.ParserCreate() +        self.parser.StartElementHandler  = self.handle_element_start +        self.parser.EndElementHandler    = self.handle_element_end +        self.parser.CharacterDataHandler = self.handle_character_data +     +    def handle_element_start(self, name, attributes): +        self.finish_character_data() +        line, column = self.pos() +        token = XmlToken(ELEMENT_START, name, attributes, line, column) +        self.tokens.append(token) +     +    def handle_element_end(self, name): +        self.finish_character_data() +        line, column = self.pos() +        token = XmlToken(ELEMENT_END, name, None, line, column) +        self.tokens.append(token) + +    def handle_character_data(self, data): +        if not self.character_data: +            self.character_pos = self.pos() +        self.character_data += data +     +    def finish_character_data(self): +        if self.character_data: +            if not self.skip_ws or not self.character_data.isspace():  +                line, column = self.character_pos +                token = XmlToken(CHARACTER_DATA, self.character_data, None, line, column) +                self.tokens.append(token) +            self.character_data = '' +     +    def next(self): +        size = 16*1024 +        while self.index >= len(self.tokens) and not self.final: +            self.tokens = [] +            self.index = 0 +            data = self.fp.read(size) +            self.final = len(data) < size +            try: +                self.parser.Parse(data, self.final) +            except xml.parsers.expat.ExpatError, e: +                #if e.code == xml.parsers.expat.errors.XML_ERROR_NO_ELEMENTS: +                if e.code == 3: +                    pass +                else: +                    raise e +        if self.index >= len(self.tokens): +            line, column = self.pos() +            token = XmlToken(EOF, None, None, line, column) +        else: +            token = self.tokens[self.index] +            self.index += 1 +        return token + +    def pos(self): +        return self.parser.CurrentLineNumber, self.parser.CurrentColumnNumber + + +class TokenMismatch(Exception): + +    def __init__(self, expected, found): +        self.expected = expected +        self.found = found + +    def __str__(self): +        return '%u:%u: %s expected, %s found' % (self.found.line, self.found.column, str(self.expected), str(self.found)) + + + +class XmlParser: +    """Base XML document parser.""" + +    def __init__(self, fp): +        self.tokenizer = XmlTokenizer(fp) +        self.consume() +     +    def consume(self): +        self.token = self.tokenizer.next() + +    def match_element_start(self, name): +        return self.token.type == ELEMENT_START and self.token.name_or_data == name +     +    def match_element_end(self, name): +        return self.token.type == ELEMENT_END and self.token.name_or_data == name + +    def element_start(self, name): +        while self.token.type == CHARACTER_DATA: +            self.consume() +        if self.token.type != ELEMENT_START: +            raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token) +        if self.token.name_or_data != name: +            raise TokenMismatch(XmlToken(ELEMENT_START, name), self.token) +        attrs = self.token.attrs +        self.consume() +        return attrs +     +    def element_end(self, name): +        while self.token.type == CHARACTER_DATA: +            self.consume() +        if self.token.type != ELEMENT_END: +            raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token) +        if self.token.name_or_data != name: +            raise TokenMismatch(XmlToken(ELEMENT_END, name), self.token) +        self.consume() + +    def character_data(self, strip = True): +        data = '' +        while self.token.type == CHARACTER_DATA: +            data += self.token.name_or_data +            self.consume() +        if strip: +            data = data.strip() +        return data + + +class TraceParser(XmlParser): + +    def parse(self): +        self.element_start('trace') +        calls = [] +        while self.token.type not in (ELEMENT_END, EOF): +            calls.append(self.parse_call()) +        if self.token.type != EOF: +            self.element_end('trace') +        return Trace(calls) + +    def parse_call(self): +        attrs = self.element_start('call') +        klass = attrs['class'] +        method = attrs['method'] +        args = [] +        ret = None +        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 == 'call': +                # ignore nested function calls +                self.parse_call() +            else: +                raise TokenMismatch("<arg ...> or <ret ...>", self.token) +        self.element_end('call') +         +        return Call(klass, method, args, ret) + +    def parse_arg(self): +        attrs = self.element_start('arg') +        name = attrs['name'] +        value = self.parse_value() +        self.element_end('arg') + +        return name, value + +    def parse_ret(self): +        attrs = self.element_start('ret') +        value = self.parse_value() +        self.element_end('ret') + +        return value + +    def parse_value(self): +        expected_tokens = ('null', 'bool', 'int', 'uint', 'float', 'string', 'enum', 'array', 'struct', 'ptr', 'bytes') +        if self.token.type == ELEMENT_START: +            if self.token.name_or_data in expected_tokens: +                method = getattr(self, 'parse_' +  self.token.name_or_data) +                return method() +        raise TokenMismatch(" or " .join(expected_tokens), self.token) + +    def parse_null(self): +        self.element_start('null') +        self.element_end('null') +        return Literal(None) +         +    def parse_bool(self): +        self.element_start('bool') +        value = int(self.character_data()) +        self.element_end('bool') +        return Literal(value) +         +    def parse_int(self): +        self.element_start('int') +        value = int(self.character_data()) +        self.element_end('int') +        return Literal(value) +         +    def parse_uint(self): +        self.element_start('uint') +        value = int(self.character_data()) +        self.element_end('uint') +        return Literal(value) +         +    def parse_float(self): +        self.element_start('float') +        value = float(self.character_data()) +        self.element_end('float') +        return Literal(value) +         +    def parse_enum(self): +        self.element_start('enum') +        name = self.character_data() +        self.element_end('enum') +        return NamedConstant(name) +         +    def parse_string(self): +        self.element_start('string') +        value = self.character_data() +        self.element_end('string') +        return Literal(value) +         +    def parse_bytes(self): +        self.element_start('bytes') +        value = binascii.a2b_hex(self.character_data()) +        self.element_end('bytes') +        return Literal(value) +         +    def parse_array(self): +        self.element_start('array') +        elems = [] +        while self.token.type != ELEMENT_END: +            elems.append(self.parse_elem()) +        self.element_end('array') +        return Array(elems) + +    def parse_elem(self): +        self.element_start('elem') +        value = self.parse_value() +        self.element_end('elem') +        return value + +    def parse_struct(self): +        attrs = self.element_start('struct') +        name = attrs['name'] +        members = [] +        while self.token.type != ELEMENT_END: +            members.append(self.parse_member()) +        self.element_end('struct') +        return Struct(name, members) + +    def parse_member(self): +        attrs = self.element_start('member') +        name = attrs['name'] +        value = self.parse_value() +        self.element_end('member') + +        return name, value + +    def parse_ptr(self): +        self.element_start('ptr') +        address = self.character_data() +        self.element_end('ptr') + +        address = int(address, 16) +         +        return Pointer(address) + + +def main(): +    for arg in sys.argv[1:]: +        parser = TraceParser(open(arg, 'rt')) +        trace = parser.parse() +        print trace + + +if __name__ == '__main__': +    main() | 
