#!/usr/bin/python ''' Parse 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 argparse import os, sys, struct import json from collections import defaultdict from binascii import b2a_hex from etnaviv.util import rnndb_path # Parse execution data log files from etnaviv.parse_fdr import ENDIAN, WORD_SPEC, ADDR_SPEC, ADDR_CHAR, WORD_CHAR, FDRLoader, Event # Extract C structures from memory from etnaviv.extract_structure import extract_structure, ResolverBase, UNRESOLVED # Print C structures 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 # Vivante ioctls (only GCHAL_INTERFACE is actually used by the driver) IOCTL_GCHAL_INTERFACE = 30000 IOCTL_GCHAL_KERNEL_INTERFACE = 30001 IOCTL_GCHAL_TERMINATE = 30002 # HAL commands without input, can hide the input arguments structure completely CMDS_NO_INPUT = [ 'gcvHAL_GET_BASE_ADDRESS', 'gcvHAL_QUERY_CHIP_IDENTITY', 'gcvHAL_QUERY_VIDEO_MEMORY', ] # HAL commands without output, can hide the output arguments structure completely CMDS_NO_OUTPUT = [ 'gcvHAL_COMMIT', 'gcvHAL_EVENT_COMMIT' ] class HalResolver(ResolverBase): ''' Data type resolver for HAL interface commands. Acts as a filter for which union/struct fields to extract and show. ''' def __init__(self, dir): self.dir = dir def filter_fields(self, s, fields_in): name = s.type['name'] if name == '_u': if s.parent.members['command'] is UNRESOLVED: return {} enum = s.parent.members['command'].name if ((self.dir == 'in' and enum in CMDS_NO_INPUT) or (self.dir == 'out' and enum in CMDS_NO_OUTPUT)): return {} # no need to show input enum = enum[7:].split('_') # convert to camelcase FOO_BAR to FooBar field = ''.join(s[0] + s[1:].lower() for s in enum) if field == 'EventCommit': field = 'Event' # exception to the rule... return {field} elif name == '_gcsHAL_INTERFACE': # these fields contains uninitialized garbage fields_in.difference_update(['handle', 'pid']) if self.dir == 'in': # status is an output-only field fields_in.difference_update(['status']) elif name == '_gcsHAL_ALLOCATE_CONTIGUOUS_MEMORY': # see gckOS_AllocateNonPagedMemory if self.dir == 'in': # These fields are not used on input fields_in.difference_update(['physical','logical']) # All three fields are filled on output # bytes has the number of actually allocated bytes elif name == '_gcsHAL_ALLOCATE_LINEAR_VIDEO_MEMORY': # see gckVIDMEM_AllocateLinear if self.dir == 'in': # These fields are not used on input fields_in.difference_update(['node']) else: return {'node'} elif name == '_gcsHAL_LOCK_VIDEO_MEMORY': if self.dir == 'in': fields_in.difference_update(['address','memory']) else: fields_in.difference_update(['node']) elif name == '_gcsHAL_USER_SIGNAL': if self.dir == 'out': # not used as output by any subcommand fields_in.difference_update(['manualReset','wait','state']) return fields_in class Counter(object): '''Count unique values''' def __init__(self): self.d = {} self.c = 0 def __getitem__(self, key): try: return self.d[key] except KeyError: rv = self.c self.d[key] = rv self.c += 1 return rv COMPS = 'xyzw' def format_state(pos, value, fixp, state_map): try: path = state_map.lookup_address(pos) path_str = format_path(path) except KeyError: path = None path_str = '' desc = (' [%05X]' % pos) + ' ' + 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 spec = 'u%i.%s' % (num//16, COMPS[(num//4)%4]) desc += ' := %f (%s)' % (int_as_float(value), spec) elif path is not None: register = path[-1][0] desc += ' := ' if isinstance(register.type, Domain): desc += format_addr(value) else: desc += register.describe(value) return desc def dump_shader(f, name, states, start, end): '''Dump binary shader code to disk''' if not start in states: return # No shader detected # extract code from consecutive addresses pos = start code = [] while pos < end and (pos in states): code.append(states[pos]) pos += 4 global shader_num filename = '%s_%i.bin' % (name, shader_num) with open(filename, 'wb') as g: for word in code: g.write(struct.pack('