Writing a Python script to print out an array of r

2019-02-20 06:57发布

问题:

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.

回答1:

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.