Writing a Python script to print out an array of recs in lldb
Question: I need help with the `SBValue class` used in the `lldb` Python module which is
used for creating scripts for lldb debugging sessions.

I am in the process of porting my kext test and debug system from `gdb` to
`lldb` so I can start using the latest version of `Xcode` and `Mac OS 10.9.1`.
Part of this process is rewriting my gdb debug scripts in python so they can
be used with lldb.

I have the two mac setup working, can drop into lldb and poke around in the
kernel of the victim Mac. I can also my Python script to be called when I am
running an lldb session. I'm stuck though in that I am unable to figure out
how to display the contents of an array of records.

For instance, given an array of `TraceRecs`:

    
    
    typedef struct
    {
    
        mach_timespec_t timeStamp;
        thread_t thread;
        TraceRecordType recordType;
        char entry[kEntrySize];
    } TraceRec;
    

a class which holds an array of trace records

    
    
    class com_softraid_TraceLog
    {
        private:
            UInt32 fMaxNumberEntries;
            UInt32 fNextEntryNumber;
            TraceRec * fTraceRecArray;
            .
            .
            .
    

and a global in the kernel which points to an object of this class:

    
    
    extern com_softraid_TraceLog * com_softraid_gTraceLogPtr;
    

I can use the following Python script to get the value of
com_softraid_gTraceLogPtr in lldb:

    
    
    #! /usr/bin/env python
    # -*- coding: utf-8 -*-
    
    import lldb
    import commands
    import optparse
    import shlex
    
    def __lldb_init_module(debugger, internal_dict):
        debugger.HandleCommand('command script add -f sr_debug_macros.srtrace srtrace')
    
    def srtrace(debugger, user_input, result, internal_dict):
        """srtrace [number_entries]  dump out that number of entries, default = 0 = all entries"""
    
        target = debugger.GetSelectedTarget()
        traceLog = target.FindFirstGlobalVariable("com_softraid_gTraceLogPtr")
        traceRecordArray = traceLog.GetChildMemberWithName("fTraceRecArray")
        maxNumberEntries = traceLog.GetChildMemberWithName("fMaxNumberEntries").GetValueAsUnsigned()
        nextEntryNumber = traceLog.GetChildMemberWithName("fNextEntryNumber").GetValueAsUnsigned()
    
        print >>result, "SRLog Current Entry: %d, Log Size: %d" % (nextEntryNumber, maxNumberEntries)
    
        print >>result, traceRecordArray
    

and the output is:

    
    
    (lldb) srtrace
    SRLog Current Entry: 388, Log Size: 8192
    (TraceRec *) fTraceRecArray = 0xffffff80a48fd000
    

but I can't figure out how to display the values in fields of any of records
in the array. I have tried most of the methods in the SBValue class without
any luck.

Does anyone understand how this is supposed to work? Any help would be great.

P. S.: If anyone else is trying to get this to work, the first step should be
to update to Xcode 5.1 b5. The version of lldb which ships with Xcode 5.1 b3
crashes frequently when displaying the contents subclasses of IOKit classes.


Answer: You're looking for `SBValue::GetChildAtIndex()` but you need to use the long
form of that API. For instance, with a standalone user process C file,

    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    typedef struct
    {
        int datum;
    } TraceRec;
    
    typedef struct
    {
            uint32_t fMaxNumberEntries;
            uint32_t fNextEntryNumber;
            TraceRec *fTraceRecArray;
    } com_softraid_TraceLog;
    
    com_softraid_TraceLog *com_softraid_gTraceLogPtr;
    
    int main ()
    {
        com_softraid_TraceLog log;
        com_softraid_gTraceLogPtr = &log;
        log.fTraceRecArray = (TraceRec *) malloc (sizeof (TraceRec) * 100);
        log.fMaxNumberEntries = 100;
        log.fNextEntryNumber = 4;
        log.fTraceRecArray[0].datum = 0;
        log.fTraceRecArray[1].datum = 1;
        log.fTraceRecArray[2].datum = 2;
        log.fTraceRecArray[3].datum = 3;
    
        puts ("break here");
    
        return 0;
    }
    

we can experiment a little in the interactive script mode:

    
    
    (lldb) br s -p break
    (lldb) r
    (lldb) scri
    >>> debugger = lldb.debugger
    >>> target = debugger.GetSelectedTarget()
    >>> traceLog = target.FindFirstGlobalVariable("com_softraid_gTraceLogPtr")
    >>> traceRecordArray = traceLog.GetChildMemberWithName("fTraceRecArray")
    >>> print traceRecordArray.GetChildAtIndex(1, lldb.eNoDynamicValues, 1)
    (TraceRec) [1] = {
      datum = 1
    }
    >>> print traceRecordArray.GetChildAtIndex(2, lldb.eNoDynamicValues, 1)
    (TraceRec) [2] = {
      datum = 2
    }
    >>> 
    

There's also `SBValue::GetPointeeData()` which would give you the raw bytes of
each member of the array in an `SBData` object but then you'd need to coerce
those bytes back into your structure so I wouldn't go that way.

