summaryrefslogtreecommitdiff
path: root/lib/mesa/src/gallium/tools/trace
diff options
context:
space:
mode:
authorJonathan Gray <jsg@cvs.openbsd.org>2015-11-22 02:46:45 +0000
committerJonathan Gray <jsg@cvs.openbsd.org>2015-11-22 02:46:45 +0000
commit0784c49c0f8fcc8b3abd4c9286d9fd8bc089dd7d (patch)
treea6394e3e264a0f80b57f4ce0f5d9526aa543d4b0 /lib/mesa/src/gallium/tools/trace
parentd91d0007eecf589ea5699e34aa4d748fce2c57b2 (diff)
import Mesa 11.0.6
Diffstat (limited to 'lib/mesa/src/gallium/tools/trace')
-rw-r--r--lib/mesa/src/gallium/tools/trace/README.txt39
-rw-r--r--lib/mesa/src/gallium/tools/trace/TODO.txt9
-rwxr-xr-xlib/mesa/src/gallium/tools/trace/diff_state.py357
-rwxr-xr-xlib/mesa/src/gallium/tools/trace/dump.py34
-rwxr-xr-xlib/mesa/src/gallium/tools/trace/dump_state.py787
-rwxr-xr-xlib/mesa/src/gallium/tools/trace/format.py173
-rwxr-xr-xlib/mesa/src/gallium/tools/trace/model.py241
-rwxr-xr-xlib/mesa/src/gallium/tools/trace/parse.py400
-rwxr-xr-xlib/mesa/src/gallium/tools/trace/tracediff.sh66
9 files changed, 2106 insertions, 0 deletions
diff --git a/lib/mesa/src/gallium/tools/trace/README.txt b/lib/mesa/src/gallium/tools/trace/README.txt
new file mode 100644
index 000000000..830cd150f
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/README.txt
@@ -0,0 +1,39 @@
+These directory contains tools for manipulating traces produced by the trace
+pipe driver.
+
+
+Most debug builds of state trackers already load the trace driver by default.
+To produce a trace do
+
+ export GALLIUM_TRACE=foo.gtrace
+
+and run the application. You can choose any name, but the .gtrace is
+recommended to avoid confusion with the .trace produced by apitrace.
+
+
+You can dump a trace by doing
+
+ ./dump.py foo.gtrace | less
+
+
+You can dump a JSON file describing the static state at any given draw call
+(e.g., 12345) by
+doing
+
+ ./dump_state.py -v -c 12345 foo.gtrace > foo.json
+
+or by specifying the n-th (e.g, 1st) draw call by doing
+
+ ./dump_state.py -v -d 1 foo.gtrace > foo.json
+
+The state is derived from the call sequence in the trace file, so no dynamic
+(eg. rendered textures) is included.
+
+
+You can compare two JSON files by doing
+
+ ./diff_state.py foo.json boo.json | less
+
+If you're investigating a regression in a state tracker, you can obtain a good
+and bad trace, dump respective state in JSON, and then compare the states to
+identify the problem.
diff --git a/lib/mesa/src/gallium/tools/trace/TODO.txt b/lib/mesa/src/gallium/tools/trace/TODO.txt
new file mode 100644
index 000000000..8bb8cfdc0
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/TODO.txt
@@ -0,0 +1,9 @@
+* track more state
+
+ * constant buffers
+
+* organize state better (e.g., group stuff according to the place in the
+ pipeline)
+
+* write an utility that generated a simple graw C code that matches a
+ state dump.
diff --git a/lib/mesa/src/gallium/tools/trace/diff_state.py b/lib/mesa/src/gallium/tools/trace/diff_state.py
new file mode 100755
index 000000000..00853bacb
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/diff_state.py
@@ -0,0 +1,357 @@
+#!/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 json
+import optparse
+import re
+import difflib
+import sys
+
+
+def strip_object_hook(obj):
+ if '__class__' in obj:
+ return None
+ for name in obj.keys():
+ if name.startswith('__') and name.endswith('__'):
+ del obj[name]
+ return obj
+
+
+class Visitor:
+
+ def visit(self, node, *args, **kwargs):
+ if isinstance(node, dict):
+ return self.visitObject(node, *args, **kwargs)
+ elif isinstance(node, list):
+ return self.visitArray(node, *args, **kwargs)
+ else:
+ return self.visitValue(node, *args, **kwargs)
+
+ def visitObject(self, node, *args, **kwargs):
+ pass
+
+ def visitArray(self, node, *args, **kwargs):
+ pass
+
+ def visitValue(self, node, *args, **kwargs):
+ pass
+
+
+class Dumper(Visitor):
+
+ def __init__(self, stream = sys.stdout):
+ self.stream = stream
+ self.level = 0
+
+ def _write(self, s):
+ self.stream.write(s)
+
+ def _indent(self):
+ self._write(' '*self.level)
+
+ def _newline(self):
+ self._write('\n')
+
+ def visitObject(self, node):
+ self.enter_object()
+
+ members = node.keys()
+ members.sort()
+ for i in range(len(members)):
+ name = members[i]
+ value = node[name]
+ self.enter_member(name)
+ self.visit(value)
+ self.leave_member(i == len(members) - 1)
+ self.leave_object()
+
+ def enter_object(self):
+ self._write('{')
+ self._newline()
+ self.level += 1
+
+ def enter_member(self, name):
+ self._indent()
+ self._write('%s: ' % name)
+
+ def leave_member(self, last):
+ if not last:
+ self._write(',')
+ self._newline()
+
+ def leave_object(self):
+ self.level -= 1
+ self._indent()
+ self._write('}')
+ if self.level <= 0:
+ self._newline()
+
+ def visitArray(self, node):
+ self.enter_array()
+ for i in range(len(node)):
+ value = node[i]
+ self._indent()
+ self.visit(value)
+ if i != len(node) - 1:
+ self._write(',')
+ self._newline()
+ self.leave_array()
+
+ def enter_array(self):
+ self._write('[')
+ self._newline()
+ self.level += 1
+
+ def leave_array(self):
+ self.level -= 1
+ self._indent()
+ self._write(']')
+
+ def visitValue(self, node):
+ self._write(json.dumps(node, allow_nan=True))
+
+
+
+class Comparer(Visitor):
+
+ def __init__(self, ignore_added = False, tolerance = 2.0 ** -24):
+ self.ignore_added = ignore_added
+ self.tolerance = tolerance
+
+ def visitObject(self, a, b):
+ if not isinstance(b, dict):
+ return False
+ if len(a) != len(b) and not self.ignore_added:
+ return False
+ ak = a.keys()
+ bk = b.keys()
+ ak.sort()
+ bk.sort()
+ if ak != bk and not self.ignore_added:
+ return False
+ for k in ak:
+ ae = a[k]
+ try:
+ be = b[k]
+ except KeyError:
+ return False
+ if not self.visit(ae, be):
+ return False
+ return True
+
+ def visitArray(self, a, b):
+ if not isinstance(b, list):
+ return False
+ if len(a) != len(b):
+ return False
+ for ae, be in zip(a, b):
+ if not self.visit(ae, be):
+ return False
+ return True
+
+ def visitValue(self, a, b):
+ if isinstance(a, float) or isinstance(b, float):
+ if a == 0:
+ return abs(b) < self.tolerance
+ else:
+ return abs((b - a)/a) < self.tolerance
+ else:
+ return a == b
+
+
+class Differ(Visitor):
+
+ def __init__(self, stream = sys.stdout, ignore_added = False):
+ self.dumper = Dumper(stream)
+ self.comparer = Comparer(ignore_added = ignore_added)
+
+ def visit(self, a, b):
+ if self.comparer.visit(a, b):
+ return
+ Visitor.visit(self, a, b)
+
+ def visitObject(self, a, b):
+ if not isinstance(b, dict):
+ self.replace(a, b)
+ else:
+ self.dumper.enter_object()
+ names = set(a.keys())
+ if not self.comparer.ignore_added:
+ names.update(b.keys())
+ names = list(names)
+ names.sort()
+
+ for i in range(len(names)):
+ name = names[i]
+ ae = a.get(name, None)
+ be = b.get(name, None)
+ if not self.comparer.visit(ae, be):
+ self.dumper.enter_member(name)
+ self.visit(ae, be)
+ self.dumper.leave_member(i == len(names) - 1)
+
+ self.dumper.leave_object()
+
+ def visitArray(self, a, b):
+ if not isinstance(b, list):
+ self.replace(a, b)
+ else:
+ self.dumper.enter_array()
+ max_len = max(len(a), len(b))
+ for i in range(max_len):
+ try:
+ ae = a[i]
+ except IndexError:
+ ae = None
+ try:
+ be = b[i]
+ except IndexError:
+ be = None
+ self.dumper._indent()
+ if self.comparer.visit(ae, be):
+ self.dumper.visit(ae)
+ else:
+ self.visit(ae, be)
+ if i != max_len - 1:
+ self.dumper._write(',')
+ self.dumper._newline()
+
+ self.dumper.leave_array()
+
+ def visitValue(self, a, b):
+ if a != b:
+ self.replace(a, b)
+
+ def replace(self, a, b):
+ if isinstance(a, basestring) and isinstance(b, basestring):
+ if '\n' in a or '\n' in b:
+ a = a.splitlines()
+ b = b.splitlines()
+ differ = difflib.Differ()
+ result = differ.compare(a, b)
+ self.dumper.level += 1
+ for entry in result:
+ self.dumper._newline()
+ self.dumper._indent()
+ tag = entry[:2]
+ text = entry[2:]
+ if tag == '? ':
+ tag = ' '
+ prefix = ' '
+ text = text.rstrip()
+ suffix = ''
+ else:
+ prefix = '"'
+ suffix = '\\n"'
+ line = tag + prefix + text + suffix
+ self.dumper._write(line)
+ self.dumper.level -= 1
+ return
+ self.dumper.visit(a)
+ self.dumper._write(' -> ')
+ self.dumper.visit(b)
+
+ def isMultilineString(self, value):
+ return isinstance(value, basestring) and '\n' in value
+
+ def replaceMultilineString(self, a, b):
+ self.dumper.visit(a)
+ self.dumper._write(' -> ')
+ self.dumper.visit(b)
+
+
+#
+# Unfortunately JSON standard does not include comments, but this is a quite
+# useful feature to have on regressions tests
+#
+
+_token_res = [
+ r'//[^\r\n]*', # comment
+ r'"[^"\\]*(\\.[^"\\]*)*"', # string
+]
+
+_tokens_re = re.compile(r'|'.join(['(' + token_re + ')' for token_re in _token_res]), re.DOTALL)
+
+
+def _strip_comment(mo):
+ if mo.group(1):
+ return ''
+ else:
+ return mo.group(0)
+
+
+def _strip_comments(data):
+ '''Strip (non-standard) JSON comments.'''
+ return _tokens_re.sub(_strip_comment, data)
+
+
+assert _strip_comments('''// a comment
+"// a comment in a string
+"''') == '''
+"// a comment in a string
+"'''
+
+
+def load(stream, strip_images = True, strip_comments = True):
+ if strip_images:
+ object_hook = strip_object_hook
+ else:
+ object_hook = None
+ if strip_comments:
+ data = stream.read()
+ data = _strip_comments(data)
+ return json.loads(data, strict=False, object_hook = object_hook)
+ else:
+ return json.load(stream, strict=False, object_hook = object_hook)
+
+
+def main():
+ optparser = optparse.OptionParser(
+ usage="\n\t%prog [options] <ref_json> <src_json>")
+ optparser.add_option(
+ '--keep-images',
+ action="store_false", dest="strip_images", default=True,
+ help="compare images")
+
+ (options, args) = optparser.parse_args(sys.argv[1:])
+
+ if len(args) != 2:
+ optparser.error('incorrect number of arguments')
+
+ a = load(open(sys.argv[1], 'rt'), options.strip_images)
+ b = load(open(sys.argv[2], 'rt'), options.strip_images)
+
+ if False:
+ dumper = Dumper()
+ dumper.visit(a)
+
+ differ = Differ()
+ differ.visit(a, b)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/lib/mesa/src/gallium/tools/trace/dump.py b/lib/mesa/src/gallium/tools/trace/dump.py
new file mode 100755
index 000000000..4a8ce863c
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/dump.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+##########################################################################
+#
+# Copyright 2008 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, sub license, 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 (including the
+# next paragraph) 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 NON-INFRINGEMENT.
+# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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.
+#
+##########################################################################
+
+
+from parse import *
+
+
+if __name__ == '__main__':
+ Main().main()
diff --git a/lib/mesa/src/gallium/tools/trace/dump_state.py b/lib/mesa/src/gallium/tools/trace/dump_state.py
new file mode 100755
index 000000000..e726f7b2a
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/dump_state.py
@@ -0,0 +1,787 @@
+#!/usr/bin/env python
+##########################################################################
+#
+# Copyright 2008-2013, 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, sub license, 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 (including the
+# next paragraph) 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 NON-INFRINGEMENT.
+# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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
+import struct
+import json
+import binascii
+import re
+import copy
+
+import model
+import parse as parser
+
+
+try:
+ from struct import unpack_from
+except ImportError:
+ def unpack_from(fmt, buf, offset=0):
+ size = struct.calcsize(fmt)
+ return struct.unpack(fmt, buf[offset:offset + size])
+
+#
+# Some constants
+#
+PIPE_BUFFER = 0
+PIPE_SHADER_VERTEX = 0
+PIPE_SHADER_FRAGMENT = 1
+PIPE_SHADER_GEOMETRY = 2
+PIPE_SHADER_COMPUTE = 3
+PIPE_SHADER_TYPES = 4
+
+
+def serialize(obj):
+ '''JSON serializer function for non-standard Python objects.'''
+
+ if isinstance(obj, bytearray):
+ # TODO: Decide on a single way of dumping blobs
+ if False:
+ # Don't dump full blobs, but merely a description of their size and
+ # CRC32 hash.
+ crc32 = binascii.crc32(obj)
+ if crc32 < 0:
+ crc32 += 0x100000000
+ return 'blob(size=%u,crc32=0x%08x)' % (len(obj), crc32)
+ if True:
+ # Dump blobs as an array of 16byte hexadecimals
+ res = []
+ for i in range(0, len(obj), 16):
+ res.append(binascii.b2a_hex(obj[i: i+16]))
+ return res
+ # Dump blobs as a single hexadecimal string
+ return binascii.b2a_hex(obj)
+
+ # If the object has a __json__ method, use it.
+ try:
+ method = obj.__json__
+ except AttributeError:
+ raise TypeError(obj)
+ else:
+ return method()
+
+
+class Struct:
+ """C-like struct.
+
+ Python doesn't have C structs, but do its dynamic nature, any object is
+ pretty close.
+ """
+
+ def __json__(self):
+ '''Convert the structure to a standard Python dict, so it can be
+ serialized.'''
+
+ obj = {}
+ for name, value in self.__dict__.items():
+ if not name.startswith('_'):
+ obj[name] = value
+ return obj
+
+ def __repr__(self):
+ return repr(self.__json__())
+
+
+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_blob(self, node):
+ self.result = node
+
+ def visit_named_constant(self, node):
+ self.result = 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 = Struct()
+ for member_name, member_node in node.members:
+ member_value = self.visit(member_node)
+ setattr(struct, member_name, member_value)
+ self.result = struct
+
+ def visit_pointer(self, node):
+ self.result = self.interpreter.lookup_object(node.address)
+
+
+class Dispatcher:
+ '''Base class for classes whose methods can dispatch Gallium calls.'''
+
+ def __init__(self, interpreter):
+ self.interpreter = interpreter
+
+
+class Global(Dispatcher):
+ '''Global name space.
+
+ For calls that are not associated with objects, i.e, functions and not
+ methods.
+ '''
+
+ def pipe_screen_create(self):
+ return Screen(self.interpreter)
+
+ def pipe_context_create(self, screen):
+ return screen.context_create()
+
+
+class Transfer:
+ '''pipe_transfer'''
+
+ def __init__(self, resource, usage, subresource, box):
+ self.resource = resource
+ self.usage = usage
+ self.subresource = subresource
+ self.box = box
+
+
+class Screen(Dispatcher):
+ '''pipe_screen'''
+
+ def __init__(self, interpreter):
+ Dispatcher.__init__(self, interpreter)
+
+ def destroy(self):
+ pass
+
+ def context_create(self):
+ return Context(self.interpreter)
+
+ def is_format_supported(self, format, target, sample_count, bind, geom_flags):
+ pass
+
+ def resource_create(self, templat):
+ resource = templat
+ # Normalize state to avoid spurious differences
+ if resource.nr_samples == 0:
+ resource.nr_samples = 1
+ if resource.target == PIPE_BUFFER:
+ # We will keep track of buffer contents
+ resource.data = bytearray(resource.width)
+ # Ignore format
+ del resource.format
+ return resource
+
+ def resource_destroy(self, resource):
+ self.interpreter.unregister_object(resource)
+
+ def fence_finish(self, fence, timeout=None):
+ pass
+
+ def fence_signalled(self, fence):
+ pass
+
+ def fence_reference(self, dst, src):
+ pass
+
+ def flush_frontbuffer(self, resource):
+ pass
+
+
+class Context(Dispatcher):
+ '''pipe_context'''
+
+ # Internal methods variable should be prefixed with '_'
+
+ def __init__(self, interpreter):
+ Dispatcher.__init__(self, interpreter)
+
+ # Setup initial state
+ self._state = Struct()
+ self._state.scissors = []
+ self._state.viewports = []
+ self._state.vertex_buffers = []
+ self._state.vertex_elements = []
+ self._state.vs = Struct()
+ self._state.gs = Struct()
+ self._state.fs = Struct()
+ self._state.vs.shader = None
+ self._state.gs.shader = None
+ self._state.fs.shader = None
+ self._state.vs.sampler = []
+ self._state.gs.sampler = []
+ self._state.fs.sampler = []
+ self._state.vs.sampler_views = []
+ self._state.gs.sampler_views = []
+ self._state.fs.sampler_views = []
+ self._state.vs.constant_buffer = []
+ self._state.gs.constant_buffer = []
+ self._state.fs.constant_buffer = []
+ self._state.render_condition_condition = 0
+ self._state.render_condition_mode = 0
+
+ self._draw_no = 0
+
+ def destroy(self):
+ pass
+
+ def create_blend_state(self, state):
+ # Normalize state to avoid spurious differences
+ if not state.logicop_enable:
+ del state.logicop_func
+ if not state.rt[0].blend_enable:
+ del state.rt[0].rgb_src_factor
+ del state.rt[0].rgb_dst_factor
+ del state.rt[0].rgb_func
+ del state.rt[0].alpha_src_factor
+ del state.rt[0].alpha_dst_factor
+ del state.rt[0].alpha_func
+ return state
+
+ def bind_blend_state(self, state):
+ # Normalize state
+ self._state.blend = state
+
+ def delete_blend_state(self, state):
+ pass
+
+ def create_sampler_state(self, state):
+ return state
+
+ def delete_sampler_state(self, state):
+ pass
+
+ def bind_sampler_states(self, shader, start, num_states, states):
+ # FIXME: Handle non-zero start
+ assert start == 0
+ self._get_stage_state(shader).sampler = states
+
+ def bind_vertex_sampler_states(self, num_states, states):
+ # XXX: deprecated method
+ self._state.vs.sampler = states
+
+ def bind_geometry_sampler_states(self, num_states, states):
+ # XXX: deprecated method
+ self._state.gs.sampler = states
+
+ def bind_fragment_sampler_states(self, num_states, states):
+ # XXX: deprecated method
+ self._state.fs.sampler = states
+
+ def create_rasterizer_state(self, state):
+ return state
+
+ def bind_rasterizer_state(self, state):
+ self._state.rasterizer = state
+
+ def delete_rasterizer_state(self, state):
+ pass
+
+ def create_depth_stencil_alpha_state(self, state):
+ # Normalize state to avoid spurious differences
+ if not state.alpha.enabled:
+ del state.alpha.func
+ del state.alpha.ref_value
+ for i in range(2):
+ if not state.stencil[i].enabled:
+ del state.stencil[i].func
+ return state
+
+ def bind_depth_stencil_alpha_state(self, state):
+ self._state.depth_stencil_alpha = state
+
+ def delete_depth_stencil_alpha_state(self, state):
+ pass
+
+ _tokenLabelRE = re.compile('^\s*\d+: ', re.MULTILINE)
+
+ def _create_shader_state(self, state):
+ # Strip the labels from the tokens
+ if state.tokens is not None:
+ state.tokens = self._tokenLabelRE.sub('', state.tokens)
+ return state
+
+ create_vs_state = _create_shader_state
+ create_gs_state = _create_shader_state
+ create_fs_state = _create_shader_state
+
+ def bind_vs_state(self, state):
+ self._state.vs.shader = state
+
+ def bind_gs_state(self, state):
+ self._state.gs.shader = state
+
+ def bind_fs_state(self, state):
+ self._state.fs.shader = state
+
+ def _delete_shader_state(self, state):
+ return state
+
+ delete_vs_state = _delete_shader_state
+ delete_gs_state = _delete_shader_state
+ delete_fs_state = _delete_shader_state
+
+ def set_blend_color(self, state):
+ self._state.blend_color = state
+
+ def set_stencil_ref(self, state):
+ self._state.stencil_ref = state
+
+ def set_clip_state(self, state):
+ self._state.clip = state
+
+ def _dump_constant_buffer(self, buffer):
+ if not self.interpreter.verbosity(2):
+ return
+
+ data = self.real.buffer_read(buffer)
+ format = '4f'
+ index = 0
+ for offset in range(0, len(data), struct.calcsize(format)):
+ x, y, z, w = unpack_from(format, data, offset)
+ sys.stdout.write('\tCONST[%2u] = {%10.4f, %10.4f, %10.4f, %10.4f}\n' % (index, x, y, z, w))
+ index += 1
+ sys.stdout.flush()
+
+ def _get_stage_state(self, shader):
+ if shader == PIPE_SHADER_VERTEX:
+ return self._state.vs
+ if shader == PIPE_SHADER_GEOMETRY:
+ return self._state.gs
+ if shader == PIPE_SHADER_FRAGMENT:
+ return self._state.fs
+ assert False
+
+ def set_constant_buffer(self, shader, index, constant_buffer):
+ self._update(self._get_stage_state(shader).constant_buffer, index, 1, [constant_buffer])
+
+ def set_framebuffer_state(self, state):
+ self._state.fb = state
+
+ def set_polygon_stipple(self, state):
+ self._state.polygon_stipple = state
+
+ def _update(self, array, start_slot, num_slots, states):
+ if not isinstance(states, list):
+ # XXX: trace is not serializing multiple scissors/viewports properly yet
+ num_slots = 1
+ states = [states]
+ while len(array) < start_slot + num_slots:
+ array.append(None)
+ for i in range(num_slots):
+ array[start_slot + i] = states[i]
+
+ def set_scissor_states(self, start_slot, num_scissors, states):
+ self._update(self._state.scissors, start_slot, num_scissors, states)
+
+ def set_viewport_states(self, start_slot, num_viewports, states):
+ self._update(self._state.viewports, start_slot, num_viewports, states)
+
+ def create_sampler_view(self, resource, templ):
+ templ.resource = resource
+ return templ
+
+ def sampler_view_destroy(self, view):
+ pass
+
+ def set_sampler_views(self, shader, start, num, views):
+ # FIXME: Handle non-zero start
+ assert start == 0
+ self._get_stage_state(shader).sampler_views = views
+
+ def set_fragment_sampler_views(self, num, views):
+ # XXX: deprecated
+ self._state.fs.sampler_views = views
+
+ def set_geometry_sampler_views(self, num, views):
+ # XXX: deprecated
+ self._state.gs.sampler_views = views
+
+ def set_vertex_sampler_views(self, num, views):
+ # XXX: deprecated
+ self._state.vs.sampler_views = views
+
+ def set_vertex_buffers(self, start_slot, num_buffers, buffers):
+ self._update(self._state.vertex_buffers, start_slot, num_buffers, buffers)
+
+ def create_vertex_elements_state(self, num_elements, elements):
+ return elements[0:num_elements]
+
+ def bind_vertex_elements_state(self, state):
+ self._state.vertex_elements = state
+
+ def delete_vertex_elements_state(self, state):
+ pass
+
+ def set_index_buffer(self, ib):
+ self._state.index_buffer = ib
+
+ # Don't dump more than this number of indices/vertices
+ MAX_ELEMENTS = 16
+
+ def _merge_indices(self, info):
+ '''Merge the vertices into our state.'''
+
+ index_size = self._state.index_buffer.index_size
+
+ format = {
+ 1: 'B',
+ 2: 'H',
+ 4: 'I',
+ }[index_size]
+
+ assert struct.calcsize(format) == index_size
+
+ if self._state.index_buffer.buffer is None:
+ # Could happen with index in user memory
+ return 0, 0
+
+ data = self._state.index_buffer.buffer.data
+ max_index, min_index = 0, 0xffffffff
+
+ count = min(info.count, self.MAX_ELEMENTS)
+ indices = []
+ for i in xrange(info.start, info.start + count):
+ offset = self._state.index_buffer.offset + i*index_size
+ if offset + index_size > len(data):
+ index = 0
+ else:
+ index, = unpack_from(format, data, offset)
+ indices.append(index)
+ min_index = min(min_index, index)
+ max_index = max(max_index, index)
+
+ self._state.indices = indices
+
+ return min_index + info.index_bias, max_index + info.index_bias
+
+ def _merge_vertices(self, start, count):
+ '''Merge the vertices into our state.'''
+
+ count = min(count, self.MAX_ELEMENTS)
+ vertices = []
+ for index in xrange(start, start + count):
+ if index >= start + 16:
+ sys.stdout.write('\t...\n')
+ break
+ vertex = []
+ for velem in self._state.vertex_elements:
+ vbuf = self._state.vertex_buffers[velem.vertex_buffer_index]
+ if vbuf.buffer is None:
+ continue
+
+ data = vbuf.buffer.data
+
+ offset = vbuf.buffer_offset + velem.src_offset + vbuf.stride*index
+ format = {
+ 'PIPE_FORMAT_R32_FLOAT': 'f',
+ 'PIPE_FORMAT_R32G32_FLOAT': '2f',
+ 'PIPE_FORMAT_R32G32B32_FLOAT': '3f',
+ 'PIPE_FORMAT_R32G32B32A32_FLOAT': '4f',
+ 'PIPE_FORMAT_R32_UINT': 'I',
+ 'PIPE_FORMAT_R32G32_UINT': '2I',
+ 'PIPE_FORMAT_R32G32B32_UINT': '3I',
+ 'PIPE_FORMAT_R32G32B32A32_UINT': '4I',
+ 'PIPE_FORMAT_R8_UINT': 'B',
+ 'PIPE_FORMAT_R8G8_UINT': '2B',
+ 'PIPE_FORMAT_R8G8B8_UINT': '3B',
+ 'PIPE_FORMAT_R8G8B8A8_UINT': '4B',
+ 'PIPE_FORMAT_A8R8G8B8_UNORM': '4B',
+ 'PIPE_FORMAT_R8G8B8A8_UNORM': '4B',
+ 'PIPE_FORMAT_B8G8R8A8_UNORM': '4B',
+ 'PIPE_FORMAT_R16G16B16_SNORM': '3h',
+ }[velem.src_format]
+
+ data = vbuf.buffer.data
+ attribute = unpack_from(format, data, offset)
+ vertex.append(attribute)
+
+ vertices.append(vertex)
+
+ self._state.vertices = vertices
+
+ def render_condition(self, query, condition = 0, mode = 0):
+ self._state.render_condition_query = query
+ self._state.render_condition_condition = condition
+ self._state.render_condition_mode = mode
+
+ def set_stream_output_targets(self, num_targets, tgs, offsets):
+ self._state.so_targets = tgs
+ self._state.offsets = offsets
+
+ def draw_vbo(self, info):
+ self._draw_no += 1
+
+ if self.interpreter.call_no < self.interpreter.options.call and \
+ self._draw_no < self.interpreter.options.draw:
+ return
+
+ # Merge the all draw state
+
+ self._state.draw = info
+
+ if info.indexed:
+ min_index, max_index = self._merge_indices(info)
+ else:
+ min_index = info.start
+ max_index = info.start + info.count - 1
+ self._merge_vertices(min_index, max_index - min_index + 1)
+
+ self._dump_state()
+
+ _dclRE = re.compile('^DCL\s+(IN|OUT|SAMP|SVIEW)\[([0-9]+)\].*$', re.MULTILINE)
+
+ def _normalize_stage_state(self, stage):
+
+ registers = {}
+
+ if stage.shader is not None and stage.shader.tokens is not None:
+ for mo in self._dclRE.finditer(stage.shader.tokens):
+ file_ = mo.group(1)
+ index = mo.group(2)
+ register = registers.setdefault(file_, set())
+ register.add(int(index))
+
+ if 'SAMP' in registers and 'SVIEW' not in registers:
+ registers['SVIEW'] = registers['SAMP']
+
+ mapping = [
+ #("CONST", "constant_buffer"),
+ ("SAMP", "sampler"),
+ ("SVIEW", "sampler_views"),
+ ]
+
+ for fileName, attrName in mapping:
+ register = registers.setdefault(fileName, set())
+ attr = getattr(stage, attrName)
+ for index in range(len(attr)):
+ if index not in register:
+ attr[index] = None
+ while attr and attr[-1] is None:
+ attr.pop()
+
+ def _dump_state(self):
+ '''Dump our state to JSON and terminate.'''
+
+ state = copy.deepcopy(self._state)
+
+ self._normalize_stage_state(state.vs)
+ self._normalize_stage_state(state.gs)
+ self._normalize_stage_state(state.fs)
+
+ json.dump(
+ obj = state,
+ fp = sys.stdout,
+ default = serialize,
+ sort_keys = True,
+ indent = 4,
+ separators = (',', ': ')
+ )
+
+ sys.exit(0)
+
+ def resource_copy_region(self, dst, dst_level, dstx, dsty, dstz, src, src_level, src_box):
+ if dst.target == PIPE_BUFFER or src.target == PIPE_BUFFER:
+ assert dst.target == PIPE_BUFFER and src.target == PIPE_BUFFER
+ assert dst_level == 0
+ assert dsty == 0
+ assert dstz == 0
+ assert src_level == 0
+ assert src_box.y == 0
+ assert src_box.z == 0
+ assert src_box.height == 1
+ assert src_box.depth == 1
+ dst.data[dstx : dstx + src_box.width] = src.data[src_box.x : src_box.x + src_box.width]
+ pass
+
+ def is_resource_referenced(self, texture, face, level):
+ pass
+
+ def get_transfer(self, texture, sr, usage, box):
+ if texture is None:
+ return None
+ transfer = Transfer(texture, sr, usage, box)
+ return transfer
+
+ def tex_transfer_destroy(self, transfer):
+ self.interpreter.unregister_object(transfer)
+
+ def transfer_inline_write(self, resource, level, usage, box, stride, layer_stride, data):
+ if resource is not None and resource.target == PIPE_BUFFER:
+ data = data.getValue()
+ assert len(data) >= box.width
+ assert box.x + box.width <= len(resource.data)
+ resource.data[box.x : box.x + box.width] = data[:box.width]
+
+ def flush(self, flags):
+ # Return a fake fence
+ return self.interpreter.call_no
+
+ def clear(self, buffers, color, depth, stencil):
+ pass
+
+ def clear_render_target(self, dst, rgba, dstx, dsty, width, height):
+ pass
+
+ def clear_depth_stencil(self, dst, clear_flags, depth, stencil, dstx, dsty, width, height):
+ pass
+
+ def create_surface(self, resource, surf_tmpl):
+ assert resource is not None
+ surf_tmpl.resource = resource
+ return surf_tmpl
+
+ def surface_destroy(self, surface):
+ self.interpreter.unregister_object(surface)
+
+ def create_query(self, query_type, index):
+ return query_type
+
+ def destroy_query(self, query):
+ pass
+
+ def begin_query(self, query):
+ pass
+
+ def end_query(self, query):
+ pass
+
+ def create_stream_output_target(self, res, buffer_offset, buffer_size):
+ so_target = Struct()
+ so_target.resource = res
+ so_target.offset = buffer_offset
+ so_target.size = buffer_size
+ return so_target
+
+
+class Interpreter(parser.TraceDumper):
+ '''Specialization of a trace parser that interprets the calls as it goes
+ along.'''
+
+ ignoredCalls = set((
+ ('pipe_screen', 'is_format_supported'),
+ ('pipe_screen', 'get_name'),
+ ('pipe_screen', 'get_vendor'),
+ ('pipe_screen', 'get_param'),
+ ('pipe_screen', 'get_paramf'),
+ ('pipe_screen', 'get_shader_param'),
+ ('pipe_context', 'clear_render_target'), # XXX workaround trace bugs
+ ))
+
+ def __init__(self, stream, options):
+ parser.TraceDumper.__init__(self, stream, sys.stderr)
+ self.options = options
+ self.objects = {}
+ self.result = None
+ self.globl = Global(self)
+ self.call_no = None
+
+ def register_object(self, address, object):
+ self.objects[address] = object
+
+ def unregister_object(self, object):
+ # TODO
+ pass
+
+ def lookup_object(self, address):
+ try:
+ return self.objects[address]
+ except KeyError:
+ # Could happen, e.g., with user memory pointers
+ return address
+
+ def interpret(self, trace):
+ for call in trace.calls:
+ self.interpret_call(call)
+
+ def handle_call(self, call):
+ if (call.klass, call.method) in self.ignoredCalls:
+ return
+
+ self.call_no = call.no
+
+ if self.verbosity(1):
+ # Write the call to stderr (as stdout would corrupt the JSON output)
+ sys.stderr.flush()
+ sys.stdout.flush()
+ parser.TraceDumper.handle_call(self, call)
+ sys.stderr.flush()
+ sys.stdout.flush()
+
+ args = [(str(name), self.interpret_arg(arg)) for name, arg in call.args]
+
+ if call.klass:
+ name, obj = args[0]
+ args = args[1:]
+ else:
+ obj = self.globl
+
+ method = getattr(obj, call.method)
+ ret = method(**dict(args))
+
+ # Keep track of created pointer objects.
+ if call.ret and isinstance(call.ret, model.Pointer):
+ if ret is None:
+ sys.stderr.write('warning: NULL returned\n')
+ self.register_object(call.ret.address, ret)
+
+ self.call_no = None
+
+ def interpret_arg(self, node):
+ translator = Translator(self)
+ return translator.visit(node)
+
+ def verbosity(self, level):
+ return self.options.verbosity >= level
+
+
+class Main(parser.Main):
+
+ def get_optparser(self):
+ '''Custom options.'''
+
+ optparser = parser.Main.get_optparser(self)
+ optparser.add_option("-q", "--quiet", action="store_const", const=0, dest="verbosity", help="no messages")
+ optparser.add_option("-v", "--verbose", action="count", dest="verbosity", default=0, help="increase verbosity level")
+ optparser.add_option("-c", "--call", action="store", type="int", dest="call", default=0xffffffff, help="dump on this call")
+ optparser.add_option("-d", "--draw", action="store", type="int", dest="draw", default=0xffffffff, help="dump on this draw")
+ return optparser
+
+ def process_arg(self, stream, options):
+ parser = Interpreter(stream, options)
+ parser.parse()
+
+
+if __name__ == '__main__':
+ Main().main()
diff --git a/lib/mesa/src/gallium/tools/trace/format.py b/lib/mesa/src/gallium/tools/trace/format.py
new file mode 100755
index 000000000..e50f61299
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/format.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+##########################################################################
+#
+# Copyright 2008 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, sub license, 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 (including the
+# next paragraph) 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 NON-INFRINGEMENT.
+# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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/lib/mesa/src/gallium/tools/trace/model.py b/lib/mesa/src/gallium/tools/trace/model.py
new file mode 100755
index 000000000..fb56701bf
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/model.py
@@ -0,0 +1,241 @@
+#!/usr/bin/env python
+##########################################################################
+#
+# Copyright 2008 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, sub license, 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 (including the
+# next paragraph) 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 NON-INFRINGEMENT.
+# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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 binascii
+
+try:
+ from cStringIO import StringIO
+except ImportError:
+ from StringIO import StringIO
+
+import format
+
+
+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 Blob(Node):
+
+ def __init__(self, value):
+ self._rawValue = None
+ self._hexValue = value
+
+ def getValue(self):
+ if self._rawValue is None:
+ self._rawValue = binascii.a2b_hex(self._hexValue)
+ self._hexValue = None
+ return self._rawValue
+
+ def visit(self, visitor):
+ visitor.visit_blob(self)
+
+
+class NamedConstant(Node):
+
+ def __init__(self, name):
+ self.name = name
+
+ def visit(self, visitor):
+ visitor.visit_named_constant(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):
+ self.address = address
+
+ def visit(self, visitor):
+ visitor.visit_pointer(self)
+
+
+class Call:
+
+ def __init__(self, no, klass, method, args, ret, time):
+ self.no = no
+ self.klass = klass
+ self.method = method
+ self.args = args
+ self.ret = ret
+ self.time = time
+
+ 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_blob(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
+
+
+class PrettyPrinter:
+
+ def __init__(self, formatter):
+ self.formatter = formatter
+
+ def visit_literal(self, node):
+ if node.value is None:
+ self.formatter.literal('NULL')
+ return
+
+ if isinstance(node.value, basestring):
+ self.formatter.literal('"' + node.value + '"')
+ return
+
+ self.formatter.literal(repr(node.value))
+
+ def visit_blob(self, node):
+ self.formatter.address('blob()')
+
+ def visit_named_constant(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 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)
+ if node.klass is not None:
+ self.formatter.function(node.klass + '::' + node.method)
+ else:
+ self.formatter.function(node.method)
+ 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)
+ if node.time is not None:
+ self.formatter.text(' // time ')
+ node.time.visit(self)
+
+ def visit_trace(self, node):
+ for call in node.calls:
+ call.visit(self)
+ self.formatter.newline()
+
diff --git a/lib/mesa/src/gallium/tools/trace/parse.py b/lib/mesa/src/gallium/tools/trace/parse.py
new file mode 100755
index 000000000..25482c889
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/parse.py
@@ -0,0 +1,400 @@
+#!/usr/bin/env python
+##########################################################################
+#
+# Copyright 2008 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, sub license, 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 (including the
+# next paragraph) 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 NON-INFRINGEMENT.
+# IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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
+import xml.parsers.expat
+import optparse
+
+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
+ data = data.rstrip('\0')
+ 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 __init__(self, fp):
+ XmlParser.__init__(self, fp)
+ self.last_call_no = 0
+
+ def parse(self):
+ self.element_start('trace')
+ while self.token.type not in (ELEMENT_END, EOF):
+ call = self.parse_call()
+ self.handle_call(call)
+ if self.token.type != EOF:
+ self.element_end('trace')
+
+ def parse_call(self):
+ attrs = self.element_start('call')
+ try:
+ no = int(attrs['no'])
+ except KeyError:
+ self.last_call_no += 1
+ no = self.last_call_no
+ else:
+ self.last_call_no = no
+ klass = attrs['class']
+ method = attrs['method']
+ args = []
+ ret = None
+ time = 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()
+ elif self.token.name_or_data == 'time':
+ time = self.parse_time()
+ else:
+ raise TokenMismatch("<arg ...> or <ret ...>", self.token)
+ self.element_end('call')
+
+ return Call(no, klass, method, args, ret, time)
+
+ 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_time(self):
+ attrs = self.element_start('time')
+ time = self.parse_value();
+ self.element_end('time')
+ return time
+
+ 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 = self.character_data()
+ self.element_end('bytes')
+ return Blob(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')
+
+ return Pointer(address)
+
+ def handle_call(self, call):
+ pass
+
+
+class TraceDumper(TraceParser):
+
+ def __init__(self, fp, outStream = sys.stdout):
+ TraceParser.__init__(self, fp)
+ self.formatter = format.DefaultFormatter(outStream)
+ self.pretty_printer = PrettyPrinter(self.formatter)
+
+ def handle_call(self, call):
+ call.visit(self.pretty_printer)
+ self.formatter.newline()
+
+
+class Main:
+ '''Common main class for all retrace command line utilities.'''
+
+ def __init__(self):
+ pass
+
+ def main(self):
+ optparser = self.get_optparser()
+ (options, args) = optparser.parse_args(sys.argv[1:])
+
+ if not args:
+ optparser.error('insufficient number of arguments')
+
+ for arg in args:
+ if arg.endswith('.gz'):
+ from gzip import GzipFile
+ stream = GzipFile(arg, 'rt')
+ elif arg.endswith('.bz2'):
+ from bz2 import BZ2File
+ stream = BZ2File(arg, 'rU')
+ else:
+ stream = open(arg, 'rt')
+ self.process_arg(stream, options)
+
+ def get_optparser(self):
+ optparser = optparse.OptionParser(
+ usage="\n\t%prog [options] TRACE [...]")
+ return optparser
+
+ def process_arg(self, stream, options):
+ parser = TraceDumper(stream)
+ parser.parse()
+
+
+if __name__ == '__main__':
+ Main().main()
diff --git a/lib/mesa/src/gallium/tools/trace/tracediff.sh b/lib/mesa/src/gallium/tools/trace/tracediff.sh
new file mode 100755
index 000000000..c7827c0ff
--- /dev/null
+++ b/lib/mesa/src/gallium/tools/trace/tracediff.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+##########################################################################
+#
+# 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.
+#
+##########################################################################/
+
+set -e
+
+TRACEDUMP=${TRACEDUMP:-`dirname "$0"`/dump.py}
+
+stripdump () {
+ python $TRACEDUMP "$1" \
+ | sed \
+ -e 's@ // time .*@@' \
+ -e 's/\x1b\[[0-9]\{1,2\}\(;[0-9]\{1,2\}\)\{0,2\}m//g' \
+ -e '/pipe_screen::is_format_supported/d' \
+ -e '/pipe_screen::get_\(shader_\)\?paramf\?/d' \
+ -e 's/\r$//g' \
+ -e 's/^[0-9]\+ //' \
+ -e 's/pipe = \w\+/pipe/g' \
+ -e 's/screen = \w\+/screen/g' \
+ -e 's/, /,\n\t/g' \
+ -e 's/) = /)\n\t= /' \
+ > "$2"
+ echo \
+ -e 's/\<0x[0-9a-fA-F]\+\>/xxx/g' \
+ > /dev/null
+}
+
+FIFODIR=`mktemp -d`
+FIFO1="$FIFODIR/1"
+FIFO2="$FIFODIR/2"
+
+mkfifo "$FIFO1"
+mkfifo "$FIFO2"
+
+stripdump "$1" "$FIFO1" &
+stripdump "$2" "$FIFO2" &
+
+sdiff \
+ --width=`tput cols` \
+ --speed-large-files \
+ "$FIFO1" "$FIFO2" \
+| less
+
+rm -rf "$FIFODIR"