summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2013-07-31 21:26:21 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2013-07-31 21:33:26 +0200
commite6de10d61792467ea2ffeab3a65fe8b8b8402880 (patch)
treebeb3cc1dfe44ea18abcfc2db6871c759485c2189 /tools
parent08dc97c8ee7b33c53ded60b4d3e5d0443adb9b8b (diff)
tools: add gpu-trace gdb command
This command traces and dumps all submitted command buffers, along with the physcial address of each command. This should come in handy for searching back the physical address that the rendering is stuck on according to the kernel.
Diffstat (limited to 'tools')
-rwxr-xr-xtools/dump_cmdstream.py91
-rwxr-xr-xtools/dump_separate_cmdbuf.py93
-rw-r--r--tools/etnaviv/dump_cmdstream_util.py7
-rw-r--r--tools/etnaviv/parse_command_buffer.py77
-rw-r--r--tools/etnaviv_gdb.py126
5 files changed, 240 insertions, 154 deletions
diff --git a/tools/dump_cmdstream.py b/tools/dump_cmdstream.py
index 69b80d2..0041d36 100755
--- a/tools/dump_cmdstream.py
+++ b/tools/dump_cmdstream.py
@@ -40,6 +40,7 @@ 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
+from etnaviv.parse_command_buffer import parse_command_buffer
DEBUG = False
@@ -60,10 +61,6 @@ CMDS_NO_OUTPUT = [
'gcvHAL_EVENT_COMMIT'
]
-# Number of words to ignore at start of command buffer
-# A PIPE3D command will be inserted here by the kernel if necessary
-CMDBUF_IGNORE_INITIAL = 8
-
class HalResolver(ResolverBase):
'''
Data type resolver for HAL interface commands.
@@ -177,6 +174,14 @@ def dump_shader(f, name, states, start, end):
f.write('/* [dumped %s to %s] */ ' % (name, filename))
shader_num += 1
+def iter_memory(mem, addr, end_addr):
+ size = (end_addr - addr)//4
+ ptr = 0
+ while ptr < size:
+ hide = False
+ (value,) = WORD_SPEC.unpack(mem[addr+ptr*4:addr+ptr*4+4])
+ yield value
+ ptr += 1
def dump_command_buffer(f, mem, addr, end_addr, depth, state_map):
'''
@@ -185,79 +190,25 @@ def dump_command_buffer(f, mem, addr, end_addr, depth, state_map):
'''
indent = ' ' * len(depth)
f.write('{\n')
- state_base = 0
- state_count = 0
- state_format = 0
- next_cmd = CMDBUF_IGNORE_INITIAL
- payload_start_ptr = 0
- payload_end_ptr = 0
- op = 0
- size = (end_addr - addr)//4
- ptr = 0
states = [] # list of (ptr, state_addr) tuples
- while ptr < size:
+ size = (end_addr - addr)//4
+ for rec in parse_command_buffer(iter_memory(mem, addr, end_addr)):
hide = False
- (value,) = WORD_SPEC.unpack(mem[addr+ptr*4:addr+ptr*4+4])
- if ptr >= next_cmd:
- #f.write('\n')
- op = value >> 27
- payload_start_ptr = payload_end_ptr = ptr + 1
- if op == 1:
- state_base = (value & 0xFFFF)<<2
- state_count = (value >> 16) & 0x3FF
- if state_count == 0:
- state_count = 0x400
- state_format = (value >> 26) & 1
- payload_end_ptr = payload_start_ptr + state_count
- desc = "LOAD_STATE (1) Base: 0x%05X Size: %i Fixp: %i" % (state_base, state_count, state_format)
- if options.hide_load_state:
- hide = True
- elif op == 2:
- desc = "END (2)"
- elif op == 3:
- desc = "NOP (3)"
- elif op == 4:
- desc = "DRAW_2D (4)"
- elif op == 5:
- desc = "DRAW_PRIMITIVES (5)"
- payload_end_ptr = payload_start_ptr + 3
- elif op == 6:
- desc = "DRAW_INDEXED_PRIMITIVES (6)"
- payload_end_ptr = payload_start_ptr + 4
- elif op == 7:
- desc = "WAIT (7)"
- elif op == 8:
- desc = "LINK (8)"
- payload_end_ptr = payload_start_ptr + 1
- elif op == 9:
- desc = "STALL (9)"
- payload_end_ptr = payload_start_ptr + 1
- elif op == 10:
- desc = "CALL (10)"
- payload_end_ptr = payload_start_ptr + 1
- elif op == 11:
- desc = "RETURN (11)"
- elif op == 13:
- desc = "CHIP_SELECT (13)"
- else:
- desc = "UNKNOWN (%i)" % op
- next_cmd = (payload_end_ptr + 1) & (~1)
- elif ptr < payload_end_ptr: # Parse payload
- if op == 1:
- pos = (ptr - payload_start_ptr)*4 + state_base
- states.append((ptr, pos, state_format, value))
- desc = format_state(pos, value, state_format, state_map)
- else:
- desc = ""
+ if rec.op == 1 and rec.payload_ofs == -1:
+ if options.hide_load_state:
+ hide = True
+ if rec.state_info is not None:
+ states.append((rec.ptr, rec.state_info.pos, rec.state_info.format, rec.value))
+ desc = format_state(rec.state_info.pos, rec.value, rec.state_info.format, state_map)
else:
- desc = "PAD"
+ desc = rec.desc
+
if not hide:
- f.write(indent + ' 0x%08x' % value)
- if ptr != (size-1):
+ f.write(indent + ' 0x%08x' % rec.value)
+ if rec.ptr != (size-1):
f.write(", /* %s */\n" % desc)
else:
f.write(" /* %s */\n" % desc)
- ptr += 1
f.write(indent + '}')
if options.list_address_states:
# Print addresses; useful for making a re-play program
diff --git a/tools/dump_separate_cmdbuf.py b/tools/dump_separate_cmdbuf.py
index b4608da..0e76a29 100755
--- a/tools/dump_separate_cmdbuf.py
+++ b/tools/dump_separate_cmdbuf.py
@@ -33,21 +33,11 @@ from binascii import b2a_hex
# Parse rules-ng-ng format for state space
from etnaviv.util import rnndb_path
from etnaviv.parse_rng import parse_rng_file, format_path, BitSet, Domain
+from etnaviv.dump_cmdstream_util import int_as_float, fixp_as_float
+from etnaviv.parse_command_buffer import parse_command_buffer
DEBUG = False
-# Number of words to ignore at start of command buffer
-# A PIPE3D command will be inserted here by the kernel if necessary
-CMDBUF_IGNORE_INITIAL = 8
-
-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:
@@ -78,78 +68,27 @@ def dump_command_buffer(f, buf, depth, state_map):
'''
indent = ' ' * len(depth)
f.write('{\n')
- state_base = 0
- state_count = 0
- state_format = 0
- next_cmd = 0 #CMDBUF_IGNORE_INITIAL
- payload_start_ptr = 0
- payload_end_ptr = 0
- op = 0
size = len(buf)
- ptr = 0
states = [] # list of (ptr, state_addr) tuples
- while ptr < size:
+ ptr = 0
+ for rec in parse_command_buffer(buf):
hide = False
- value = buf[ptr]
- if ptr >= next_cmd:
- #f.write('\n')
- op = value >> 27
- payload_start_ptr = payload_end_ptr = ptr + 1
- if op == 1:
- state_base = (value & 0xFFFF)<<2
- state_count = (value >> 16) & 0x3FF
- if state_count == 0:
- state_count = 0x400
- state_format = (value >> 26) & 1
- payload_end_ptr = payload_start_ptr + state_count
- desc = "LOAD_STATE (1) Base: 0x%05X Size: %i Fixp: %i" % (state_base, state_count, state_format)
- if options.hide_load_state:
- hide = True
- elif op == 2:
- desc = "END (2)"
- elif op == 3:
- desc = "NOP (3)"
- elif op == 4:
- desc = "DRAW_2D (4)"
- elif op == 5:
- desc = "DRAW_PRIMITIVES (5)"
- payload_end_ptr = payload_start_ptr + 3
- elif op == 6:
- desc = "DRAW_INDEXED_PRIMITIVES (6)"
- payload_end_ptr = payload_start_ptr + 4
- elif op == 7:
- desc = "WAIT (7)"
- elif op == 8:
- desc = "LINK (8)"
- payload_end_ptr = payload_start_ptr + 1
- elif op == 9:
- desc = "STALL (9)"
- payload_end_ptr = payload_start_ptr + 1
- elif op == 10:
- desc = "CALL (10)"
- payload_end_ptr = payload_start_ptr + 1
- elif op == 11:
- desc = "RETURN (11)"
- elif op == 13:
- desc = "CHIP_SELECT (13)"
- else:
- desc = "UNKNOWN (%i)" % op
- next_cmd = (payload_end_ptr + 1) & (~1)
- elif ptr < payload_end_ptr: # Parse payload
- if op == 1:
- pos = (ptr - payload_start_ptr)*4 + state_base
- states.append((ptr, pos, state_format, value))
- desc = format_state(pos, value, state_format, state_map)
- else:
- desc = ""
+ if rec.op == 1 and rec.payload_ofs == -1:
+ if options.hide_load_state:
+ hide = True
+
+ if rec.state_info is not None:
+ states.append((rec.ptr, rec.state_info.pos, rec.state_info.format, rec.value))
+ desc = format_state(rec.state_info.pos, rec.value, rec.state_info.format, state_map)
else:
- desc = "PAD"
+ desc = rec.desc
+
if not hide:
- f.write(indent + ' 0x%08x' % value)
+ f.write(indent + ' 0x%08x' % rec.value)
if ptr != (size-1):
- f.write(", /* %s */\n" % desc)
+ f.write(", /* %s */\n" % rec.desc)
else:
- f.write(" /* %s */\n" % desc)
+ f.write(" /* %s */\n" % rec.desc)
ptr += 1
f.write(indent + '}')
diff --git a/tools/etnaviv/dump_cmdstream_util.py b/tools/etnaviv/dump_cmdstream_util.py
index 71b18f2..fa9619b 100644
--- a/tools/etnaviv/dump_cmdstream_util.py
+++ b/tools/etnaviv/dump_cmdstream_util.py
@@ -33,3 +33,10 @@ def fixp_as_float(i):
'''Return float from 16.16 fixed-point value of i'''
return i / 65536.0
+COMPS = 'xyzw'
+def offset_to_uniform(num):
+ '''
+ Register offset to u0.x.
+ '''
+ return 'u%i.%s' % (num//16, COMPS[(num//4)%4])
+
diff --git a/tools/etnaviv/parse_command_buffer.py b/tools/etnaviv/parse_command_buffer.py
new file mode 100644
index 0000000..dbb75de
--- /dev/null
+++ b/tools/etnaviv/parse_command_buffer.py
@@ -0,0 +1,77 @@
+from collections import namedtuple
+CommandInfo = namedtuple('CommandInfo', ['ptr', 'value', 'op', 'payload_ofs', 'desc', 'state_info'])
+StateInfo = namedtuple('StateInfo', ['pos', 'format'])
+
+# Number of words to ignore at start of command buffer
+# A PIPE3D command will be inserted here by the kernel if necessary
+CMDBUF_IGNORE_INITIAL = 8
+
+def parse_command_buffer(buffer_words):
+ '''
+ Parse Vivante command buffer contents, return a sequence of
+ CommandInfo records.
+ '''
+ state_base = 0
+ state_count = 0
+ state_format = 0
+ next_cmd = CMDBUF_IGNORE_INITIAL
+ payload_start_ptr = 0
+ payload_end_ptr = 0
+ op = 0
+ ptr = 0
+ for value in buffer_words:
+ state_info = None
+ if ptr >= next_cmd:
+ payload_ofs = -1
+ op = value >> 27
+ payload_start_ptr = payload_end_ptr = ptr + 1
+ if op == 1:
+ state_base = (value & 0xFFFF)<<2
+ state_count = (value >> 16) & 0x3FF
+ if state_count == 0:
+ state_count = 0x400
+ state_format = (value >> 26) & 1
+ payload_end_ptr = payload_start_ptr + state_count
+ desc = "LOAD_STATE (1) Base: 0x%05X Size: %i Fixp: %i" % (state_base, state_count, state_format)
+ elif op == 2:
+ desc = "END (2)"
+ elif op == 3:
+ desc = "NOP (3)"
+ elif op == 4:
+ desc = "DRAW_2D (4)"
+ elif op == 5:
+ desc = "DRAW_PRIMITIVES (5)"
+ payload_end_ptr = payload_start_ptr + 3
+ elif op == 6:
+ desc = "DRAW_INDEXED_PRIMITIVES (6)"
+ payload_end_ptr = payload_start_ptr + 4
+ elif op == 7:
+ desc = "WAIT (7)"
+ elif op == 8:
+ desc = "LINK (8)"
+ payload_end_ptr = payload_start_ptr + 1
+ elif op == 9:
+ desc = "STALL (9)"
+ payload_end_ptr = payload_start_ptr + 1
+ elif op == 10:
+ desc = "CALL (10)"
+ payload_end_ptr = payload_start_ptr + 1
+ elif op == 11:
+ desc = "RETURN (11)"
+ elif op == 13:
+ desc = "CHIP_SELECT (13)"
+ else:
+ desc = "UNKNOWN (%i)" % op
+ next_cmd = (payload_end_ptr + 1) & (~1)
+ elif ptr < payload_end_ptr: # Parse payload
+ payload_ofs = ptr - payload_start_ptr
+ desc = ""
+ if op == 1:
+ pos = payload_ofs*4 + state_base
+ state_info = StateInfo(pos, state_format)
+ else:
+ desc = "PAD"
+ payload_ofs = -2 # padding
+ yield CommandInfo(ptr, value, op, payload_ofs, desc, state_info)
+ ptr += 1
+
diff --git a/tools/etnaviv_gdb.py b/tools/etnaviv_gdb.py
index 5dc48d6..13401c4 100644
--- a/tools/etnaviv_gdb.py
+++ b/tools/etnaviv_gdb.py
@@ -21,8 +21,9 @@ if not os.path.dirname(__file__) in sys.path:
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.dump_cmdstream_util import int_as_float, fixp_as_float, offset_to_uniform
from etnaviv.rnn_domain_visitor import DomainVisitor
+from etnaviv.parse_command_buffer import parse_command_buffer
# (gdb) print ((struct etna_pipe_context*)((struct gl_context*)_glapi_Context)->st->cso_context->pipe)
# pipe-> gpu3d has current GPU state
@@ -137,9 +138,9 @@ def format_state(reg, key, val):
class GPUState(gdb.Command):
"""Etnaviv: show GPU state."""
- def __init__ (self):
+ def __init__ (self, state_xml):
super(GPUState, self).__init__ ("gpu-state", gdb.COMMAND_USER)
- self.state_xml = parse_rng_file(rnndb_path('state.xml'))
+ self.state_xml = state_xml
self.state_map = self.state_xml.lookup_domain('VIVS')
self.registers = build_registers_dict(self.state_map)
@@ -204,9 +205,9 @@ from etnaviv.asm_common import format_instruction, disassemble
class GPUDisassemble(gdb.Command):
"""Etnaviv: disassemble shaders"""
- def __init__ (self):
+ def __init__ (self, isa_xml):
super(GPUDisassemble, self).__init__ ("gpu-disassemble", gdb.COMMAND_USER)
- self.isa = parse_rng_file(rnndb_path('isa.xml'))
+ self.isa = isa_xml
def invoke(self, arg, from_tty):
self.dont_repeat()
@@ -235,6 +236,117 @@ class GPUDisassemble(gdb.Command):
# XXX show current values when loading from uniforms
out.write('\n')
-GPUState()
-GPUDisassemble()
+### gpu-trace ###
+from etnaviv.asm_common import format_instruction, disassemble
+
+class CommitBreakpoint(gdb.Breakpoint):
+ def __init__(self, state_map, do_stop):
+ super(CommitBreakpoint, self).__init__('viv_commit')
+ self.state_map = state_map
+ self.size_t_type = gdb.lookup_type('size_t') # must have same size as pointer
+ self.uint32_t_ptr_type = gdb.lookup_type('uint32_t').pointer() # command words
+ self.do_stop = do_stop
+
+ def memory_iterator(self, start_addr, end_addr):
+ addr = start_addr
+ while addr < end_addr:
+ yield int(self.viv_read_u32(addr))
+ addr += 4
+
+ def format_state(self, pos, value, fixp):
+ try:
+ path = self.state_map.lookup_address(pos)
+ path_str = format_path(path)
+ except KeyError:
+ path = None
+ path_str = '0x%05X' % pos
+ desc = ' ' + path_str
+ if fixp:
+ desc += ' = %f' % fixp_as_float(value)
+ else:
+ # For uniforms, show float value
+ if (pos >= 0x05000 and pos < 0x06000) or (pos >= 0x07000 and pos < 0x08000):
+ num = pos & 0xFFF
+ desc += ' := %f (%s)' % (int_as_float(value), offset_to_uniform(num))
+ elif path is not None:
+ register = path[-1][0]
+ desc += ' := ' + register.describe(value)
+ return desc
+
+ def stop(self):
+ commandBuffer_sym,_ = gdb.lookup_symbol('commandBuffer')
+ commandBuffer = commandBuffer_sym.value(gdb.newest_frame())
+ # offset, startOffset
+ # physical
+ offset = int(commandBuffer['offset'])
+ startOffset = int(commandBuffer['startOffset'])
+ physical = int(commandBuffer['physical'].cast(self.size_t_type)) # GPU address
+ logical = int(commandBuffer['logical'].cast(self.size_t_type)) # CPU address
+ print('viv_commit:')
+ # need this function from viv.c to work around gdb having no access to the mapped buffers
+ self.viv_read_u32 = gdb.lookup_symbol('_viv_read_u32')[0].value()
+ # iterate over buffer, one 32 bit word at a time
+ for rec in parse_command_buffer(self.memory_iterator(logical + startOffset, logical + offset)):
+ if rec.state_info is not None:
+ desc = self.format_state(rec.state_info.pos, rec.value, rec.state_info.format)
+ else:
+ desc = rec.desc
+ print(' [%08x] %08x %s' % (physical + startOffset + rec.ptr*4, rec.value, desc))
+ return self.do_stop
+
+class GPUTrace(gdb.Command):
+ """Etnaviv: trace submitted command buffers
+ Usage:
+ gpu-trace <on|off> Enable/disable cmdbuffer trace
+ gpu-trace stop <on|off> Enable/disable stopping on commit
+ """
+
+ def __init__ (self, state_xml):
+ super(GPUTrace, self).__init__ ("gpu-trace", gdb.COMMAND_USER)
+ self.enabled = False
+ self.state_xml = state_xml
+ self.state_map = self.state_xml.lookup_domain('VIVS')
+ self.bp = None
+ self.stop_on_commit = False
+
+ def invoke(self, arg, from_tty):
+ self.dont_repeat()
+ arg = arg.split()
+ if not arg:
+ pass # just check status
+ elif arg[0] == 'off': # disable
+ if not self.enabled:
+ print("GPU tracing is not enabled")
+ return
+ self.enabled = False
+ self.bp.delete()
+ self.bp = None
+ elif arg[0] == 'on': # enable
+ if self.enabled:
+ print("GPU tracing is already enabled")
+ return
+ self.enabled = True
+ self.bp = CommitBreakpoint(self.state_map, self.stop_on_commit)
+ elif arg[0] == 'stop':
+ if arg[1] == 'off':
+ self.stop_on_commit = False
+ elif arg[1] == 'on':
+ self.stop_on_commit = True
+ else:
+ print("Unrecognized stop mode %s" % arg[1])
+ if self.bp: # if breakpoint currently exists, change parameter on the fly
+ self.bp.do_stop = self.stop_on_commit
+ else:
+ print("Unrecognized tracing mode %s" % arg[0])
+ return
+ print("Etnaviv command buffer tracing %s (stop %s)" % (
+ ['disabled','enabled'][self.enabled],
+ ['disabled','enabled'][self.stop_on_commit]))
+
+state_xml = parse_rng_file(rnndb_path('state.xml'))
+isa_xml = parse_rng_file(rnndb_path('isa.xml'))
+
+GPUState(state_xml)
+GPUDisassemble(isa_xml)
+GPUTrace(state_xml)