sniffing the exception in objc_exception_throw

When I don't put a breakpoint on objc_exception_throw, I get the
exception description in my console, along with addresses of the
return pointers in the call stack. These addresses aren't
particularly useful for debugging (for me, anyway).

When I put a breakpoint on objc_exception_throw to debug Objective-C
exceptions, I can inspect the call stack properly, but I don't get the
exception description in the console.

Setting a breakpoint command is no good because I need different
commands for each of the arm, i386, and x86_64 architectures. I want
to set up the breakpoint once as an Xcode user breakpoint.

Enrico Granata and Sean Callanan helped me implement a new command in
Python that works across all three architectures (thanks again!), and
asked that I send it to this mailing list.

# ~/.lldbinit

command script import ~/Library/lldb/sniff_objc_exception_throw.py

# ~/Library/lldb/sniff_objc_exception_throw.py

import lldb

def GetFirstArgumentAsValue(target, frame):
    # Note: I assume the PC is at the first instruction of the
function, before the stack and registers have been modified.
    if target.triple.startswith('x86_64'):
        return frame.regs[0].GetChildMemberWithName("rdi")
    elif target.triple.startswith('i386'):
        espValue = frame.regs[0].GetChildMemberWithName("esp")
        address = espValue.GetValueAsUnsigned() + target.addr_size
        return espValue.CreateValueFromAddress('arg0', address,
target.FindFirstType('id'))
    else:
        return frame.regs[0].GetChildMemberWithName("r0")

def command(debugger, user_input, result, unused):
    target = debugger.GetSelectedTarget()
    frame = target.GetProcess().GetSelectedThread().GetFrameAtIndex(0)
    description = GetFirstArgumentAsValue(target, frame).GetObjectDescription()
    if description is None:
        output = "I couldn't get the description of the exception being thrown."
    else:
        output = "Description of exception being thrown: " + repr(description)
    result.PutCString(output)
    return None

def __lldb_init_module(debugger, unused):
    debugger.HandleCommand('command script add --function
sniff_objc_exception_throw.command sniff_objc_exception_throw')

That's cool. Note that for x86_64 and arm (and any other architecture that passes arguments in registers) lldb provides a register alias "arg1" for whatever register is used for passing the first argument. So you have to treat i386 separately, but for other architectures you can just use

frame.regs[0].GetChildMemberWithName("arg1")

Jim