python/retrace: Application capable of replaying gallium traces.
At the moment it is capable of replaying trivial/tri kind of apps. See README for status.
This commit is contained in:
18
src/gallium/state_trackers/python/retrace/README
Normal file
18
src/gallium/state_trackers/python/retrace/README
Normal file
@@ -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>
|
418
src/gallium/state_trackers/python/retrace/interpreter.py
Executable file
418
src/gallium/state_trackers/python/retrace/interpreter.py
Executable file
@@ -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()
|
151
src/gallium/state_trackers/python/retrace/model.py
Executable file
151
src/gallium/state_trackers/python/retrace/model.py
Executable file
@@ -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
|
||||
|
||||
|
332
src/gallium/state_trackers/python/retrace/parser.py
Executable file
332
src/gallium/state_trackers/python/retrace/parser.py
Executable file
@@ -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()
|
Reference in New Issue
Block a user