diff options
author | Wladimir J. van der Laan <laanwj@gmail.com> | 2013-07-27 15:30:04 +0200 |
---|---|---|
committer | Wladimir J. van der Laan <laanwj@gmail.com> | 2013-07-27 15:30:04 +0200 |
commit | 188bf5d94887b1e377c62fd011ef3fa27b1b4e53 (patch) | |
tree | 70bcfd2253dbc7010d628d86e22bf53d5ddaf475 /tools | |
parent | 18c9233fae05d763e6ceda29183f0b5bbb35bb34 (diff) |
tools: add etnaviv_gdb plugin
GDB plugin for etnaviv driver debugging. This needs gdb 7.5+ to work.
usage (from gdb):
source /path/to/etnaviv_gdb.py
Commands:
gpu-state (prefix|uniforms)
Show full GPU state (default) or only registers with a certain prefix.
The special prefix 'uniforms' shows only the shader uniforms.
gpu-dis
Disassemble the current shaders.
Diffstat (limited to 'tools')
-rwxr-xr-x | tools/dump_cmdstream.py | 9 | ||||
-rw-r--r-- | tools/etnaviv/dump_cmdstream_util.py | 35 | ||||
-rw-r--r-- | tools/etnaviv/rnn_domain_visitor.py | 37 | ||||
-rw-r--r-- | tools/etnaviv_gdb.py | 229 | ||||
-rwxr-xr-x | tools/gen_weave_state.py | 3 |
5 files changed, 304 insertions, 9 deletions
diff --git a/tools/dump_cmdstream.py b/tools/dump_cmdstream.py index 8f173c0..69b80d2 100755 --- a/tools/dump_cmdstream.py +++ b/tools/dump_cmdstream.py @@ -39,6 +39,7 @@ from etnaviv.extract_structure import extract_structure, ResolverBase, UNRESOLVE from etnaviv.dump_structure import dump_structure, print_address # Parse rules-ng-ng format for state space from etnaviv.parse_rng import parse_rng_file, format_path, BitSet, Domain +from etnaviv.dump_cmdstream_util import int_as_float, fixp_as_float DEBUG = False @@ -132,14 +133,6 @@ class Counter(object): self.c += 1 return rv -def int_as_float(i): - '''Return float with binary representation of unsigned int i''' - return struct.unpack(b'f', struct.pack(b'I', i))[0] - -def fixp_as_float(i): - '''Return float from 16.16 fixed-point value of i''' - return i / 65536.0 - COMPS = 'xyzw' def format_state(pos, value, fixp, state_map): try: diff --git a/tools/etnaviv/dump_cmdstream_util.py b/tools/etnaviv/dump_cmdstream_util.py new file mode 100644 index 0000000..71b18f2 --- /dev/null +++ b/tools/etnaviv/dump_cmdstream_util.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +''' +Utilities for parsing execution data log stream. +''' +# Copyright (c) 2012-2013 Wladimir J. van der Laan +# +# 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 +# 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. +from __future__ import print_function, division, unicode_literals +import struct + +def int_as_float(i): + '''Return float with binary representation of unsigned int i''' + return struct.unpack(b'f', struct.pack(b'I', i))[0] + +def fixp_as_float(i): + '''Return float from 16.16 fixed-point value of i''' + return i / 65536.0 + diff --git a/tools/etnaviv/rnn_domain_visitor.py b/tools/etnaviv/rnn_domain_visitor.py new file mode 100644 index 0000000..fe11006 --- /dev/null +++ b/tools/etnaviv/rnn_domain_visitor.py @@ -0,0 +1,37 @@ +from etnaviv.parse_rng import parse_rng_file, format_path, BitSet, Domain, Stripe, Register, Array, BaseType + +class DomainVisitor(object): + ''' + Walk a rnndb domain, visit all registers recursively. + ''' + def __init__(self): + pass + + def visit(self, node): + if isinstance(node, Domain): + self.visit_domain(node) + elif isinstance(node, Stripe): + self.visit_stripe(node) + elif isinstance(node, Array): + self.visit_array(node) + elif isinstance(node, Register): + self.visit_register(node) + else: + raise ValueError('DomainVisitor: unknown node type %s' % node.__class__.__name__) + + def visit_domain(self, node): + for child in node.contents: + self.visit(child) + + def visit_stripe(self, node): + for child in node.contents: + self.visit(child) + + def visit_register(self, node): + for child in node.contents: + self.visit(child) + + def visit_array(self, node): + for child in node.contents: + self.visit(child) + diff --git a/tools/etnaviv_gdb.py b/tools/etnaviv_gdb.py new file mode 100644 index 0000000..2620cef --- /dev/null +++ b/tools/etnaviv_gdb.py @@ -0,0 +1,229 @@ +# GDB plugin for etnaviv driver debugging. +# This needs gdb 7.5+ to work. +# +# usage (from gdb): +# source /path/to/etnaviv_gdb.py +# +# Commands: +# gpu-state (prefix|uniforms) +# Show full GPU state (default) or only registers with a certain prefix. +# The special prefix 'uniforms' shows only the shader uniforms. +# gpu-dis +# Disassemble the current shaders. +# +from __future__ import print_function, division, unicode_literals +import sys,os +from collections import namedtuple +# Add script directory to python path (seems that gdb does not do this automatically) +# need this to import etnaviv.* +if not os.path.dirname(__file__) in sys.path: + sys.path.append(os.path.dirname(__file__)) + +from etnaviv.util import rnndb_path +from etnaviv.parse_rng import parse_rng_file, format_path, BitSet, Domain, Stripe, Register, Array, BaseType +from etnaviv.dump_cmdstream_util import int_as_float, fixp_as_float +from etnaviv.rnn_domain_visitor import DomainVisitor + +# (gdb) print ((struct etna_pipe_context*)((struct gl_context*)_glapi_Context)->st->cso_context->pipe) +# pipe-> gpu3d has current GPU state +# pipe->ctx (etna_ctx) has command buffers + +# Ideas: +# - print changes in current GPU state highlighted + +# - can we hook etna_flush and display the current command buffer contents? +# set breakpoint that invokes python code? is that possible? +# (see gdb.Breakpoint) + +RegStride = namedtuple('RegStride', ['stride', 'length']) +RegInfo = namedtuple('RegInfo', ['reg', 'offset', 'strides']) + +class StateCollector(DomainVisitor): + ''' + Walk rnndb domain, collect all register names, + build a dictionary of register name to object. + ''' + def __init__(self): + self.registers = {} + self.path = [(0, [], [])] + + def extend_path(self, node): + offset, name, strides = self.path[-1] + offset = offset + node.offset + if node.name is not None: # don't append, we want a copy + name = name + [node.name] + if node.length != 1: # don't append, we want a copy + strides = strides + [RegStride(node.stride, node.length)] + self.path.append((offset, name, strides)) + + def visit_stripe(self, node): + self.extend_path(node) + DomainVisitor.visit_stripe(self, node) + self.path.pop() + + def visit_array(self, node): + self.extend_path(node) + DomainVisitor.visit_array(self, node) + self.path.pop() + + def visit_register(self, node): + self.extend_path(node) + offset, name, strides = self.path[-1] + name = '_'.join(name) + # sort strides by decreasing size to make sure child addresses are in increasing order + strides.sort(key=lambda x:-x.stride) + self.registers[name] = RegInfo(node, offset, strides) + self.path.pop() + +def build_registers_dict(domain): + ''' + Build dictionary of GPU registers from their C name (_ delimited) + to a RegisterInfo named tuple with (register description, offset, strides). + ''' + col = StateCollector() + col.visit(domain) + return col.registers + +def lookup_etna_state(): + ''' + Return etna pipe and screen from current Mesa GL context. + @returns a tuple (pipe, screen) + ''' + glapi_context_sym,_ = gdb.lookup_symbol('_glapi_Context') + gl_context_type = gdb.lookup_type('struct gl_context').pointer() + etna_pipe_context_type = gdb.lookup_type('struct etna_pipe_context').pointer() + etna_screen_type = gdb.lookup_type('struct etna_screen').pointer() + + glapi_context = glapi_context_sym.value() + glapi_context = glapi_context.cast(gl_context_type) + pipe = glapi_context['st']['cso_context']['pipe'] + screen = pipe['screen'] + # case to specific types + pipe = pipe.cast(etna_pipe_context_type) + screen = pipe.cast(etna_screen_type) + return (pipe, screen) + +### gpu-state ### +# state formatting +def hex_and_float(x): + return '%08x (%f)' % (x, int_as_float(x)) +def hex_and_float_fixp(x): + return '%08x (%f)' % (x, fixp_as_float(x)) + +special_format = { + 'VS_UNIFORMS': hex_and_float, + 'PS_UNIFORMS': hex_and_float, + 'SE_SCISSOR_LEFT': hex_and_float_fixp, + 'SE_SCISSOR_RIGHT': hex_and_float_fixp, + 'SE_SCISSOR_TOP': hex_and_float_fixp, + 'SE_SCISSOR_BOTTOM': hex_and_float_fixp +} + +def format_state(reg, key, val): + if key in special_format: + return special_format[key](int(val)) + else: + return reg.describe(int(val)) + +class GPUState(gdb.Command): + """Etnaviv: show GPU state.""" + + def __init__ (self): + super(GPUState, self).__init__ ("gpu-state", gdb.COMMAND_USER) + self.state_xml = parse_rng_file(rnndb_path('state.xml')) + self.state_map = self.state_xml.lookup_domain('VIVS') + self.registers = build_registers_dict(self.state_map) + + def print_uniforms_for(self, out, stype, uniforms, count): + out.write('[%s uniforms]:\n' % stype) + base = 0 + comps = 'xyzw' + while base < count: + sub = [] + for idx in xrange(0, 4): # last uniform can be partial + if (base + idx)<count: + sub.append(int(uniforms[base + idx])) + subf = (int_as_float(x) for x in sub) + subfs = ', '.join((str(x) for x in subf)) + out.write(' u%i.%s = (%s)\n' % (base//4, comps[0:len(sub)], subfs)) + base += 4 + + def print_uniforms(self, pipe): + gpu3d = pipe['gpu3d'] + self.print_uniforms_for(sys.stdout, 'vs', gpu3d['VS_UNIFORMS'], int(pipe['shader_state']['vs_uniforms_size'])) + self.print_uniforms_for(sys.stdout, 'ps', gpu3d['PS_UNIFORMS'], int(pipe['shader_state']['ps_uniforms_size'])) + + def invoke(self, arg, from_tty): + (pipe, screen) = lookup_etna_state() + gpu3d = pipe['gpu3d'] + + # Parse arguments + if arg == 'uniforms': + self.print_uniforms(pipe) + return + else: # register prefix + prefix = arg.lower() + + for key in gpu3d.type.keys(): + if key in {'VS_UNIFORMS', 'PS_UNIFORMS'} or not key.lower().startswith(prefix): + # handled separately + continue + val = gpu3d[key] + typ = val.type.strip_typedefs() + if key in self.registers: + reg_desc = self.registers[key] + else: + print('Warning: no such register %s' % key) + continue + if typ.code == gdb.TYPE_CODE_INT: + print(" %s: %s" % (key, format_state(reg_desc.reg,key,val))) + elif typ.code == gdb.TYPE_CODE_ARRAY: + size = typ.range()[1]+1 + print(" %s:" % key) + for x in xrange(0, size): + subval = val[x] + subtyp = subval.type.strip_typedefs() + subaddr = reg_desc.offset + x * reg_desc.strides[0].stride + if subtyp.code == gdb.TYPE_CODE_INT: + print(" [%d] %s" % (x, format_state(reg_desc.reg,key,subval))) + if not prefix: + self.print_uniforms(pipe) + +### gpu-dis(assemble) ### +from etnaviv.asm_common import format_instruction, disassemble +class GPUDisassemble(gdb.Command): + """Etnaviv: disassemble shaders""" + + def __init__ (self): + super(GPUDisassemble, self).__init__ ("gpu-disassemble", gdb.COMMAND_USER) + self.isa = parse_rng_file(rnndb_path('isa.xml')) + + def invoke(self, arg, from_tty): + (pipe, screen) = lookup_etna_state() + shader_state = pipe['shader_state'] + vs_inst_size = int(shader_state['vs_inst_mem_size']) + ps_inst_size = int(shader_state['ps_inst_mem_size']) + vs_inst_mem = shader_state['VS_INST_MEM'] + ps_inst_mem = shader_state['PS_INST_MEM'] + self.disassemble(sys.stdout, 'vs', vs_inst_mem, vs_inst_size//4) + self.disassemble(sys.stdout, 'ps', ps_inst_mem, ps_inst_size//4) + + def disassemble(self, out, stype, mem, count): + out.write('[%s code]:\n' % stype) + for idx in xrange(count): + inst = (int(mem[idx*4+0]), int(mem[idx*4+1]), int(mem[idx*4+2]), int(mem[idx*4+3])) + out.write(' %3x: ' % idx) + out.write('%08x %08x %08x %08x ' % inst) + warnings = [] + parsed = disassemble(self.isa, inst, warnings) + text = format_instruction(self.isa, parsed) + out.write(text) + if warnings: + out.write(' ; ') + out.write(' '.join(warnings)) + # XXX show current values when loading from uniforms + out.write('\n') + +GPUState() +GPUDisassemble() + diff --git a/tools/gen_weave_state.py b/tools/gen_weave_state.py index abc6e93..54b3dbc 100755 --- a/tools/gen_weave_state.py +++ b/tools/gen_weave_state.py @@ -169,7 +169,8 @@ def main(): # build target field reference target_field = name # to sort destination state addresses in order, sort array indices by decreasing stride - dest_strides = sorted([(idx,stride,length) for idx,(stride,length) in enumerate(strides)], key=lambda x:-x[1]) + dest_strides = sorted([(idx,stride,length) for idx,(stride,length) in enumerate(strides)], + key=lambda x:-x[1]) for src_idx,stride,length in dest_strides: target_field += '[{%i}]' % (src_idx) |