コード例 #1
0
ファイル: dump_cmdstream.py プロジェクト: wusanshou/etna_viv
def extract_meta(fdr, defs, resolver, ifin):
    '''Extract metadata for ifin, for tracking'''
    field = command_to_field(ifin.members['command'].name)
    gcin = ifin.members['u'].members.get(field)
    if field in {'Commit', 'Event'} and '_gcsQUEUE' in defs:
        queue = gcin.members['queue'].addr
        # Extract queue
        q = []
        while queue:
            s = extract_structure(fdr, queue, defs, '_gcsQUEUE', resolver=resolver)
            if s.members['next'] is UNRESOLVED: # Queue is not in fdr
                break
            q.append(s)
            queue = s.members['next'].addr
        return q
    return None
コード例 #2
0
ファイル: dump_cmdstream.py プロジェクト: VCTLabs/etna_viv
    def handle_pointer(f, ptr, depth):
        parent = depth[-1][0]
        field = depth[-1][1]
        if ptr.type in ['_gcoCMDBUF', '_gcoCONTEXT', '_gcsQUEUE']:
            s = extract_structure(fdr,
                                  ptr.addr,
                                  defs,
                                  ptr.type,
                                  resolver=resolver)
            f.write('&(%s)0x%x' % (ptr.type, ptr.addr))
            dump_structure(f, s, handle_pointer, handle_comment, depth)
            return
        elif field == 'logical' and ptr.addr != 0:
            # Command stream
            if parent.type['name'] == '_gcoCMDBUF':
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(
                    f, fdr, ptr.addr + parent.members['startOffset'].value,
                    ptr.addr + parent.members['offset'].value, depth,
                    state_map, cmdstream_info)
                return
            if parent.type[
                    'name'] == '_gcoCONTEXT' and options.show_context_commands:
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(
                    f, fdr, ptr.addr,
                    ptr.addr + parent.members['bufferSize'].value, depth,
                    state_map, cmdstream_info)
                return
        elif parent.type[
                'name'] == '_gcoCONTEXT' and field == 'map' and ptr.addr != 0 and options.show_state_map:
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_context_map(f, fdr, ptr.addr,
                             ptr.addr + parent.members['stateCount'].value * 4,
                             depth, state_map)
            return
        elif parent.type[
                'name'] == '_gcoCONTEXT' and field == 'buffer' and ptr.addr != 0 and options.show_context_buffer:
            # Equivalent to gcoCONTEXT.map
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_command_buffer(f, fdr, ptr.addr,
                                ptr.addr + parent.members['bufferSize'].value,
                                depth, state_map, cmdstream_info)
            return

        print_address(f, ptr, depth)
コード例 #3
0
ファイル: dump_cmdstream.py プロジェクト: wusanshou/etna_viv
    def handle_pointer(f, ptr, depth):
        parent = depth[-1][0]
        field = depth[-1][1]
        if ptr.type in ['_gcoCMDBUF','_gcsQUEUE']:
            s = extract_structure(fdr, ptr.addr, defs, ptr.type, resolver=resolver)
            f.write('&(%s)0x%x' % (ptr.type,ptr.addr))
            dump_structure(f, s, handle_pointer, handle_comment, depth)
            return
        elif field == 'logical' and ptr.addr != 0:
            # Command stream
            if parent.type['name'] == '_gcoCMDBUF':
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(f, fdr, ptr.addr + parent.members['startOffset'].value, 
                         ptr.addr + parent.members['offset'].value,
                         depth, state_map, cmdstream_info, tracking, txdesc_map)
                return

        print_address(f, ptr, depth)
コード例 #4
0
ファイル: dump_cmdstream.py プロジェクト: CSRedRat/etna_viv
    def handle_pointer(f, ptr, depth):
        parent = depth[-1][0]
        field = depth[-1][1]
        if ptr.type in ['_gcoCMDBUF','_gcoCONTEXT','_gcsQUEUE']:
            s = extract_structure(fdr, ptr.addr, defs, ptr.type, resolver=resolver)
            f.write('&(%s)0x%x' % (ptr.type,ptr.addr))
            dump_structure(f, s, handle_pointer, handle_comment, depth)
            return
        elif field == 'logical' and ptr.addr != 0:
            # Command stream
            if parent.type['name'] == '_gcoCMDBUF':
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(f, fdr, ptr.addr + parent.members['startOffset'].value, 
                         ptr.addr + parent.members['offset'].value,
                         depth, state_map)
                return
            if parent.type['name'] == '_gcoCONTEXT' and options.show_context_commands:
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(f, fdr, ptr.addr, 
                         ptr.addr + parent.members['bufferSize'].value,
                         depth, state_map)
                return
        elif parent.type['name'] == '_gcoCONTEXT' and field == 'map' and ptr.addr != 0 and options.show_state_map:
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_context_map(f, fdr, ptr.addr, 
                     ptr.addr + parent.members['stateCount'].value*4,
                     depth, state_map)
            return
        elif parent.type['name'] == '_gcoCONTEXT' and field == 'buffer' and ptr.addr != 0 and options.show_context_buffer:
            # Equivalent to gcoCONTEXT.map
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_command_buffer(f, fdr, ptr.addr, 
                     ptr.addr + parent.members['bufferSize'].value,
                     depth, state_map)
            return

        print_address(f, ptr, depth)
コード例 #5
0
ファイル: dump_cmdstream.py プロジェクト: VCTLabs/etna_viv
def main():
    args = parse_arguments()

    if args.struct_file is None:
        args.struct_file = guess_from_fdr(args.input)
        print('[Using struct file %s]' % args.struct_file)
    if args.struct_file is None:
        print(
            'Could not determine struct file from GCABI in FDR, provide --struct-file= argument'
        )

    defs = load_data_definitions(args.struct_file)
    state_xml = parse_rng_file(args.rules_file)
    state_map = state_xml.lookup_domain('VIVS')

    cmdstream_xml = parse_rng_file(args.cmdstream_file)
    fe_opcode = cmdstream_xml.lookup_type('FE_OPCODE')
    cmdstream_map = cmdstream_xml.lookup_domain('VIV_FE')
    cmdstream_info = CmdStreamInfo(fe_opcode, cmdstream_map)
    #print(fe_opcode.values_by_value[1].name)
    #print(format_path(cmdstream_map.lookup_address(0,('LOAD_STATE','FE_OPCODE'))))

    fdr = FDRLoader(args.input)
    global options
    options = args

    def handle_comment(f, val, depth):
        '''Annotate value with a comment'''
        if not depth:
            return None
        parent = depth[-1][0]
        field = depth[-1][1]
        parent_type = parent.type['name']
        # Show names for features and minor feature bits
        if parent_type == '_gcsHAL_QUERY_CHIP_IDENTITY':
            if field == 'chipMinorFeatures':
                field = 'chipMinorFeatures0'
            if field in state_xml.types and isinstance(state_xml.types[field],
                                                       BitSet):
                feat = state_xml.types[field]
                active_feat = [
                    bit.name for bit in feat.bitfields
                    if bit.extract(val.value)
                ]
                return ' '.join(active_feat)
        elif parent_type == '_gcsHAL_LOCK_VIDEO_MEMORY':
            if field == 'address':  # annotate addresses with unique identifier
                return format_addr(val.value)

    def handle_pointer(f, ptr, depth):
        parent = depth[-1][0]
        field = depth[-1][1]
        if ptr.type in ['_gcoCMDBUF', '_gcoCONTEXT', '_gcsQUEUE']:
            s = extract_structure(fdr,
                                  ptr.addr,
                                  defs,
                                  ptr.type,
                                  resolver=resolver)
            f.write('&(%s)0x%x' % (ptr.type, ptr.addr))
            dump_structure(f, s, handle_pointer, handle_comment, depth)
            return
        elif field == 'logical' and ptr.addr != 0:
            # Command stream
            if parent.type['name'] == '_gcoCMDBUF':
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(
                    f, fdr, ptr.addr + parent.members['startOffset'].value,
                    ptr.addr + parent.members['offset'].value, depth,
                    state_map, cmdstream_info)
                return
            if parent.type[
                    'name'] == '_gcoCONTEXT' and options.show_context_commands:
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(
                    f, fdr, ptr.addr,
                    ptr.addr + parent.members['bufferSize'].value, depth,
                    state_map, cmdstream_info)
                return
        elif parent.type[
                'name'] == '_gcoCONTEXT' and field == 'map' and ptr.addr != 0 and options.show_state_map:
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_context_map(f, fdr, ptr.addr,
                             ptr.addr + parent.members['stateCount'].value * 4,
                             depth, state_map)
            return
        elif parent.type[
                'name'] == '_gcoCONTEXT' and field == 'buffer' and ptr.addr != 0 and options.show_context_buffer:
            # Equivalent to gcoCONTEXT.map
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_command_buffer(f, fdr, ptr.addr,
                                ptr.addr + parent.members['bufferSize'].value,
                                depth, state_map, cmdstream_info)
            return

        print_address(f, ptr, depth)

    f = sys.stdout
    thread_id = Counter()

    def thread_name(rec):
        return '[thread %i]' % thread_id[rec.parameters['thread'].value]

    for seq, rec in enumerate(fdr):
        if isinstance(rec, Event):  # Print events as they appear in the fdr
            f.write(('[seq %i] ' % seq) + thread_name(rec) + ' ')
            params = rec.parameters
            if rec.event_type == 'MMAP_AFTER':
                f.write(
                    'mmap addr=0x%08x length=0x%08x prot=0x%08x flags=0x%08x offset=0x%08x = 0x%08x\n'
                    % (params['addr'].value, params['length'].value,
                       params['prot'].value, params['flags'].value,
                       params['offset'].value, params['ret'].value))
            elif rec.event_type == 'MUNMAP_AFTER':
                f.write('munmap addr=0x%08x length=0x%08x = 0x%08x\n' %
                        (params['addr'].value, params['length'].value,
                         params['ret'].value))
            elif rec.event_type == 'IOCTL_BEFORE':  # addr, length, prot, flags, offset
                if params['request'].value == IOCTL_GCHAL_INTERFACE:
                    ptr = params['ptr'].value
                    inout = vivante_ioctl_data_t.unpack(
                        fdr[ptr:ptr + vivante_ioctl_data_t.size])
                    f.write('in=')
                    resolver = HalResolver('in')
                    s = extract_structure(fdr,
                                          inout[0],
                                          defs,
                                          '_gcsHAL_INTERFACE',
                                          resolver=resolver)
                    dump_structure(f, s, handle_pointer, handle_comment)
                    f.write('\n')
                else:
                    f.write('Unknown Vivante ioctl %i\n' %
                            rec.parameters['request'].value)
            elif rec.event_type == 'IOCTL_AFTER':
                if params['request'].value == IOCTL_GCHAL_INTERFACE:
                    ptr = params['ptr'].value
                    inout = vivante_ioctl_data_t.unpack(
                        fdr[ptr:ptr + vivante_ioctl_data_t.size])
                    f.write('out=')
                    resolver = HalResolver('out')
                    s = extract_structure(fdr,
                                          inout[2],
                                          defs,
                                          '_gcsHAL_INTERFACE',
                                          resolver=resolver)
                    dump_structure(f, s, handle_pointer, handle_comment)
                    f.write('\n')
                    f.write(
                        '/* ================================================ */\n'
                    )
                else:
                    f.write('Unknown Vivante ioctl %i\n' %
                            rec.parameters['request'].value)
            else:
                f.write('unhandled event ' + rec.event_type + '\n')
        elif isinstance(rec, Comment):
            t = WORD_SPEC.unpack(rec.data[0:4])[0]
            if t == 0x594e4f50:
                v = struct.unpack(ENDIAN + WORD_CHAR * 3, rec.data[4:])
                print('[end of frame %d: %d.%09d]' % v)
            elif t == 0x424f4c42:
                v = struct.unpack(ENDIAN + WORD_CHAR * 4, rec.data[4:])
                print('[driver version %d.%d.%d.%d]' % v)
コード例 #6
0
ファイル: dump_cmdstream.py プロジェクト: CSRedRat/etna_viv
def main():
    args = parse_arguments()
    defs = load_data_definitions(args.struct_file)
    state_xml = parse_rng_file(args.rules_file)
    state_map = state_xml.lookup_domain('VIVS')
    fdr = FDRLoader(args.input)
    global options
    options = args
    global isa
    isa = parse_rng_file(args.isa_file)

    def handle_comment(f, val, depth):
        '''Annotate value with a comment'''
        if not depth:
            return None
        parent = depth[-1][0]
        field = depth[-1][1]
        parent_type = parent.type['name']
        # Show names for features and minor feature bits
        if parent_type == '_gcsHAL_QUERY_CHIP_IDENTITY':
            if field == 'chipMinorFeatures':
                field = 'chipMinorFeatures0'
            if field in state_xml.types and isinstance(state_xml.types[field], BitSet):
                feat = state_xml.types[field]
                active_feat = [bit.name for bit in feat.bitfields if bit.extract(val.value)]
                return ' '.join(active_feat)
        elif parent_type == '_gcsHAL_LOCK_VIDEO_MEMORY':
            if field == 'address': # annotate addresses with unique identifier
                return format_addr(val.value)

    def handle_pointer(f, ptr, depth):
        parent = depth[-1][0]
        field = depth[-1][1]
        if ptr.type in ['_gcoCMDBUF','_gcoCONTEXT','_gcsQUEUE']:
            s = extract_structure(fdr, ptr.addr, defs, ptr.type, resolver=resolver)
            f.write('&(%s)0x%x' % (ptr.type,ptr.addr))
            dump_structure(f, s, handle_pointer, handle_comment, depth)
            return
        elif field == 'logical' and ptr.addr != 0:
            # Command stream
            if parent.type['name'] == '_gcoCMDBUF':
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(f, fdr, ptr.addr + parent.members['startOffset'].value, 
                         ptr.addr + parent.members['offset'].value,
                         depth, state_map)
                return
            if parent.type['name'] == '_gcoCONTEXT' and options.show_context_commands:
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(f, fdr, ptr.addr, 
                         ptr.addr + parent.members['bufferSize'].value,
                         depth, state_map)
                return
        elif parent.type['name'] == '_gcoCONTEXT' and field == 'map' and ptr.addr != 0 and options.show_state_map:
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_context_map(f, fdr, ptr.addr, 
                     ptr.addr + parent.members['stateCount'].value*4,
                     depth, state_map)
            return
        elif parent.type['name'] == '_gcoCONTEXT' and field == 'buffer' and ptr.addr != 0 and options.show_context_buffer:
            # Equivalent to gcoCONTEXT.map
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_command_buffer(f, fdr, ptr.addr, 
                     ptr.addr + parent.members['bufferSize'].value,
                     depth, state_map)
            return

        print_address(f, ptr, depth)

    vivante_ioctl_data_t = struct.Struct(ENDIAN + ADDR_CHAR + WORD_CHAR + ADDR_CHAR + WORD_CHAR)
    f = sys.stdout
    thread_id = Counter()

    def thread_name(rec):
        return '[thread %i]' % thread_id[rec.parameters['thread'].value]

    for seq,rec in enumerate(fdr):
        if isinstance(rec, Event): # Print events as they appear in the fdr
            f.write(('[seq %i] ' % seq) + thread_name(rec) + ' ')
            params = rec.parameters
            if rec.event_type == 'MMAP_AFTER':
                f.write('mmap addr=0x%08x length=0x%08x prot=0x%08x flags=0x%08x offset=0x%08x = 0x%08x\n' % (
                    params['addr'].value, params['length'].value, params['prot'].value, params['flags'].value, params['offset'].value, 
                    params['ret'].value))
            elif rec.event_type == 'MUNMAP_AFTER':
                f.write('munmap addr=0x%08x length=0x%08x = 0x%08x\n' % (
                    params['addr'].value, params['length'].value, params['ret'].value))
            elif rec.event_type == 'IOCTL_BEFORE': # addr, length, prot, flags, offset
                if params['request'].value == IOCTL_GCHAL_INTERFACE:
                    ptr = params['ptr'].value
                    inout = vivante_ioctl_data_t.unpack(fdr[ptr:ptr+vivante_ioctl_data_t.size])
                    f.write('in=')
                    resolver = HalResolver('in')
                    s = extract_structure(fdr, inout[0], defs, '_gcsHAL_INTERFACE', resolver=resolver)
                    dump_structure(f, s, handle_pointer, handle_comment)
                    f.write('\n')
                else:
                    f.write('Unknown Vivante ioctl %i\n' % rec.parameters['request'].value)
            elif rec.event_type == 'IOCTL_AFTER':
                if params['request'].value == IOCTL_GCHAL_INTERFACE:
                    ptr = params['ptr'].value
                    inout = vivante_ioctl_data_t.unpack(fdr[ptr:ptr+vivante_ioctl_data_t.size])
                    f.write('out=')
                    resolver = HalResolver('out')
                    s = extract_structure(fdr, inout[2], defs, '_gcsHAL_INTERFACE', resolver=resolver)
                    dump_structure(f, s, handle_pointer, handle_comment)
                    f.write('\n')
                    f.write('/* ================================================ */\n')
                else:
                    f.write('Unknown Vivante ioctl %i\n' % rec.parameters['request'].value)
            else:
                f.write('unhandled event ' + rec.event_type + '\n')
コード例 #7
0
ファイル: dump_cmdstream.py プロジェクト: etnaviv/etna_viv
def main():
    args = parse_arguments()
    
    if args.struct_file is None:
        args.struct_file = guess_from_fdr(args.input)
        print('[Using struct file %s]' % args.struct_file)
    if args.struct_file is None:
        print('Could not determine struct file from GCABI in FDR, provide --struct-file= argument')

    defs = load_data_definitions(args.struct_file)
    state_xml = parse_rng_file(args.rules_file)
    state_map = state_xml.lookup_domain('VIVS')

    cmdstream_xml = parse_rng_file(args.cmdstream_file)
    fe_opcode = cmdstream_xml.lookup_type('FE_OPCODE')
    cmdstream_map = cmdstream_xml.lookup_domain('VIV_FE')
    cmdstream_info = CmdStreamInfo(fe_opcode, cmdstream_map)

    txdesc_xml = parse_rng_file(args.txdesc_file)
    txdesc_map = txdesc_xml.lookup_domain('TEXDESC')

    fdr = FDRLoader(args.input)
    global options
    options = args

    def handle_comment(f, val, depth):
        '''Annotate value with a comment'''
        if not depth:
            return None
        parent = depth[-1][0]
        field = depth[-1][1]
        parent_type = parent.type['name']
        # Show names for features and minor feature bits
        if parent_type == '_gcsHAL_QUERY_CHIP_IDENTITY':
            if field == 'chipMinorFeatures':
                field = 'chipMinorFeatures0'
            if field in state_xml.types and isinstance(state_xml.types[field], BitSet):
                feat = state_xml.types[field]
                active_feat = [bit.name for bit in feat.bitfields if bit.extract(val.value)]
                return ' '.join(active_feat)
        elif parent_type == '_gcsHAL_LOCK_VIDEO_MEMORY':
            if field == 'address': # annotate addresses with unique identifier
                return tracking.format_addr(val.value)
        elif parent_type == '_gcsUSER_MEMORY_DESC':
            if field == 'physical': # annotate addresses with unique identifier
                return tracking.format_addr(val.value)
        elif parent_type == '_gcsHAL_SIGNAL':
            if field == 'signal':
                return 'id = %d' % val.value
        if field == 'node':
            if val.value in tracking.nodes:
                return tracking.nodes[val.value].name

    def handle_pointer(f, ptr, depth):
        parent = depth[-1][0]
        field = depth[-1][1]
        if ptr.type in ['_gcoCMDBUF','_gcsQUEUE']:
            s = extract_structure(fdr, ptr.addr, defs, ptr.type, resolver=resolver)
            f.write('&(%s)0x%x' % (ptr.type,ptr.addr))
            dump_structure(f, s, handle_pointer, handle_comment, depth)
            return
        elif field == 'logical' and ptr.addr != 0:
            # Command stream
            if parent.type['name'] == '_gcoCMDBUF':
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(f, fdr, ptr.addr + parent.members['startOffset'].value, 
                         ptr.addr + parent.members['offset'].value,
                         depth, state_map, cmdstream_info, tracking, txdesc_map)
                return

        print_address(f, ptr, depth)

    f = sys.stdout
    tracking = DriverState(defs)

    for seq,rec in enumerate(fdr):
        if isinstance(rec, Event): # Print events as they appear in the fdr
            if not args.summary:
                f.write(('[seq %i] ' % seq) + tracking.thread_name(rec) + ' ')
            params = rec.parameters
            if rec.event_type == 'MMAP_BEFORE': # ignore
                pass
            elif rec.event_type == 'MMAP_AFTER':
                f.write('mmap addr=0x%08x length=0x%08x prot=0x%08x flags=0x%08x offset=0x%08x = 0x%08x\n' % (
                    params['addr'].value, params['length'].value, params['prot'].value, params['flags'].value, params['offset'].value, 
                    params['ret'].value))
            elif rec.event_type == 'MUNMAP_AFTER':
                f.write('munmap addr=0x%08x length=0x%08x = 0x%08x\n' % (
                    params['addr'].value, params['length'].value, params['ret'].value))
            elif rec.event_type == 'IOCTL_BEFORE': # addr, length, prot, flags, offset
                if params['request'].value == IOCTL_GCHAL_INTERFACE:
                    ptr = params['ptr'].value
                    inout = vivante_ioctl_data_t.unpack(fdr[ptr:ptr+vivante_ioctl_data_t.size])
                    if not args.summary:
                        f.write('in=')
                    resolver = HalResolver('in')
                    s = extract_structure(fdr, inout[0], defs, '_gcsHAL_INTERFACE', resolver=resolver)
                    tracking.handle_gcin(rec.parameters['thread'].value, s, extract_meta(fdr, defs, resolver, s))
                    if not args.summary or s.members['command'].name == 'gcvHAL_COMMIT':
                        dump_structure(f, s, handle_pointer, handle_comment)
                        f.write('\n')
                else:
                    f.write('Unknown Vivante ioctl %i\n' % rec.parameters['request'].value)
            elif rec.event_type == 'IOCTL_AFTER':
                if params['request'].value == IOCTL_GCHAL_INTERFACE:
                    ptr = params['ptr'].value
                    inout = vivante_ioctl_data_t.unpack(fdr[ptr:ptr+vivante_ioctl_data_t.size])
                    if not args.summary:
                        f.write('out=')
                    resolver = HalResolver('out')
                    s = extract_structure(fdr, inout[2], defs, '_gcsHAL_INTERFACE', resolver=resolver)
                    tracking.handle_gcout(rec.parameters['thread'].value, s)
                    if not args.summary:
                        dump_structure(f, s, handle_pointer, handle_comment)
                        f.write('\n')
                        f.write('/* ================================================ */\n')
                else:
                    f.write('Unknown Vivante ioctl %i\n' % rec.parameters['request'].value)
            else:
                f.write('unhandled event ' + rec.event_type + '\n')
        elif isinstance(rec, Comment):
            t = WORD_SPEC.unpack(rec.data[0:4])[0]
            if t == 0x594e4f50:
                v = struct.unpack(ENDIAN + WORD_CHAR * 3, rec.data[4:])
                print('[end of frame %d: %d.%09d]' % v)
            elif t == 0x424f4c42:
                v = struct.unpack(ENDIAN + WORD_CHAR * 4, rec.data[4:])
                print('[driver version %d.%d.%d.%d]' % v)
コード例 #8
0
def main():
    args = parse_arguments()
    defs = load_data_definitions(args.struct_file)
    state_xml = parse_rng_file(args.rules_file)
    state_map = state_xml.lookup_domain('VIVS')
    fdr = FDRLoader(args.input)
    global options
    options = args

    def handle_comment(f, val, depth):
        '''Annotate value with a comment'''
        if not depth:
            return None
        parent = depth[-1][0]
        field = depth[-1][1]
        parent_type = parent.type['name']
        # Show names for features and minor feature bits
        if parent_type == '_gcsHAL_QUERY_CHIP_IDENTITY':
            if field == 'chipMinorFeatures':
                field = 'chipMinorFeatures0'
            if field in state_xml.types and isinstance(state_xml.types[field],
                                                       BitSet):
                feat = state_xml.types[field]
                active_feat = [
                    bit.name for bit in feat.bitfields
                    if bit.extract(val.value)
                ]
                return ' '.join(active_feat)
        elif parent_type == '_gcsHAL_LOCK_VIDEO_MEMORY':
            if field == 'address':  # annotate addresses with unique identifier
                return format_addr(val.value)

    def handle_pointer(f, ptr, depth):
        parent = depth[-1][0]
        field = depth[-1][1]
        if ptr.type in ['_gcoCMDBUF', '_gcoCONTEXT', '_gcsQUEUE']:
            s = extract_structure(fdr,
                                  ptr.addr,
                                  defs,
                                  ptr.type,
                                  resolver=resolver)
            f.write('&(%s)0x%x' % (ptr.type, ptr.addr))
            dump_structure(f, s, handle_pointer, handle_comment, depth)
            return
        elif field == 'logical' and ptr.addr != 0:
            # Command stream
            if parent.type['name'] == '_gcoCMDBUF':
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(
                    f, fdr, ptr.addr + parent.members['startOffset'].value,
                    ptr.addr + parent.members['offset'].value, depth,
                    state_map)
                return
            if parent.type[
                    'name'] == '_gcoCONTEXT' and options.show_context_commands:
                f.write('&(uint32[])0x%x' % (ptr.addr))
                dump_command_buffer(
                    f, fdr, ptr.addr,
                    ptr.addr + parent.members['bufferSize'].value, depth,
                    state_map)
                return
        elif parent.type[
                'name'] == '_gcoCONTEXT' and field == 'map' and ptr.addr != 0 and options.show_state_map:
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_context_map(f, fdr, ptr.addr,
                             ptr.addr + parent.members['stateCount'].value * 4,
                             depth, state_map)
            return
        elif parent.type[
                'name'] == '_gcoCONTEXT' and field == 'buffer' and ptr.addr != 0 and options.show_context_buffer:
            # Equivalent to gcoCONTEXT.map
            f.write('&(uint32[])0x%x' % (ptr.addr))
            dump_command_buffer(f, fdr, ptr.addr,
                                ptr.addr + parent.members['bufferSize'].value,
                                depth, state_map)
            return

        print_address(f, ptr, depth)

    if options.ioctl_64bit:
        vivante_ioctl_data_t = IOCTL_SIZE_SPEC_64B
    else:
        vivante_ioctl_data_t = IOCTL_SIZE_SPEC

    f = sys.stdout
    thread_id = Counter()

    def thread_name(rec):
        return '[thread %i]' % thread_id[rec.parameters['thread'].value]

    for seq, rec in enumerate(fdr):
        if isinstance(rec, Event):  # Print events as they appear in the fdr
            f.write(('[seq %i] ' % seq) + thread_name(rec) + ' ')
            params = rec.parameters
            if rec.event_type == 'MMAP_AFTER':
                f.write(
                    'mmap addr=0x%08x length=0x%08x prot=0x%08x flags=0x%08x offset=0x%08x = 0x%08x\n'
                    % (params['addr'].value, params['length'].value,
                       params['prot'].value, params['flags'].value,
                       params['offset'].value, params['ret'].value))
            elif rec.event_type == 'MUNMAP_AFTER':
                f.write('munmap addr=0x%08x length=0x%08x = 0x%08x\n' %
                        (params['addr'].value, params['length'].value,
                         params['ret'].value))
            elif rec.event_type == 'IOCTL_BEFORE':  # addr, length, prot, flags, offset
                if params['request'].value == IOCTL_GCHAL_INTERFACE:
                    ptr = params['ptr'].value
                    inout = vivante_ioctl_data_t.unpack(
                        fdr[ptr:ptr + vivante_ioctl_data_t.size])
                    f.write('in=')
                    resolver = HalResolver('in')
                    s = extract_structure(fdr,
                                          inout[0],
                                          defs,
                                          '_gcsHAL_INTERFACE',
                                          resolver=resolver)
                    dump_structure(f, s, handle_pointer, handle_comment)
                    f.write('\n')
                else:
                    f.write('Unknown Vivante ioctl %i\n' %
                            rec.parameters['request'].value)
            elif rec.event_type == 'IOCTL_AFTER':
                if params['request'].value == IOCTL_GCHAL_INTERFACE:
                    ptr = params['ptr'].value
                    inout = vivante_ioctl_data_t.unpack(
                        fdr[ptr:ptr + vivante_ioctl_data_t.size])
                    f.write('out=')
                    resolver = HalResolver('out')
                    s = extract_structure(fdr,
                                          inout[2],
                                          defs,
                                          '_gcsHAL_INTERFACE',
                                          resolver=resolver)
                    dump_structure(f, s, handle_pointer, handle_comment)
                    f.write('\n')
                    f.write(
                        '/* ================================================ */\n'
                    )
                else:
                    f.write('Unknown Vivante ioctl %i\n' %
                            rec.parameters['request'].value)
            else:
                f.write('unhandled event ' + rec.event_type + '\n')