84

OK, so imagine that my breakpoint in objc_exception_throw has just triggered. I'm sitting at the debugger prompt, and I want to get some more information about the exception object. Where do I find it?

Karoy Lorentey
  • 4,843
  • 2
  • 28
  • 28

4 Answers4

163

The exception object is passed in as the first argument to objc_exception_throw. LLDB provides $arg1..$argn variables to refer to arguments in the correct calling convention, making it simple to print the exception details:

(lldb) po $arg1
(lldb) po [$arg1 name]
(lldb) po [$arg1 reason]

Make sure to select the objc_exception_throw frame in the call stack before executing these commands. See the "Advanced Debugging and the Address Sanitizer" in the WWDC15 session videos to see this performed on stage.

Outdated Information

If you're on GDB, the syntax to refer to the first argument depends on the calling conventions of the architecture you're running on. If you're debugging on an actual iOS device, the pointer to the object is in register r0. To print it or send messages to it, use the following simple syntax:

(gdb) po $r0
(gdb) po [$r0 name]
(gdb) po [$r0 reason]

On the iPhone Simulator, all function arguments are passed on the stack, so the syntax is considerably more horrible. The shortest expression I could construct that gets to it is *(id *)($ebp + 8). To make things less painful, I suggest using a convenience variable:

(gdb) set $exception = *(id *)($ebp + 8)
(gdb) po $exception
(gdb) po [$exception name]
(gdb) po [$exception reason]

You can also set $exception automatically whenever the breakpoint is triggered by adding a command list to the objc_exception_throw breakpoint.

(Note that in all cases I tested, the exception object was also present in the eax and edx registers at the time the breakpoint hit. I'm not sure that'll always be the case, though.)

Added from comment below:

In lldb, select the stack frame for objc_exception_throw and then enter this command:

(lldb) po *(id *)($esp + 4)
Karoy Lorentey
  • 4,843
  • 2
  • 28
  • 28
  • 6
    How would one do this in lldb? I get an error "error: reference to 'id' is ambiguous" – offex Apr 10 '12 at 17:41
  • 2
    can you provide the source of this info? i would like to read more about it – João Nunes Feb 27 '13 at 11:17
  • The iOS/ARM calling conventions are documented in the [iOS ABI Function Call Guide](http://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/iPhoneOSABIReference.pdf), which is based on the [Procedure Call Standard for the ARM Architecture ABI](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ihi0042e/index.html). The iOS Simulator uses the [OS X calling conventions on x86](http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/LowLevelABI/130-IA-32_Function_Calling_Conventions/IA32.html#//apple_ref/doc/uid/TP40002492-SW4). – Karoy Lorentey Apr 17 '13 at 08:24
  • 3
    Currently the following works for me before the prologue when breaing on `objc_exception_throw ` in _LLDB_: `po *(id *)($esp + 4)`. – wbyoung May 10 '13 at 21:14
  • 7
    This worked! However, **it didn't work until I selected stack frame 0**. (`objc_exception_throw`). – funroll May 15 '13 at 11:40
  • 7
    `po $eax` works for me in the simulator as pendant to the `$r0` when on device. – monkeydom Oct 20 '13 at 21:58
  • Check out [this awesome script](http://stackoverflow.com/a/29671227/991816) that automates the answer. – DanSkeel Apr 16 '15 at 10:00
  • 1
    I get `error: use of undeclared identifier '$r0'` on iPad with iOS 7 – Iulian Onofrei May 15 '15 at 15:58
12

on new simulators (iOS 8, 64bit) xcode 6 im using in the exception frame: objc_exception_throw

po $rax

in 32bit:

po $eax

What is rax?

Rax is a 64bits register that replaces the old eax

How to find all the registers?

register read

Source wikipedia

João Nunes
  • 3,751
  • 31
  • 32
  • Hmm... In Xcode 6.1, I'm getting: (lldb) po $rax error: Couldn't materialize: couldn't read the value of register rax Errored out in Execute, couldn't PrepareToExecuteJITExpression – bradheintz Oct 08 '14 at 18:58
  • @bradheintz simulator or device? i tried this with 6.0.1 – João Nunes Oct 09 '14 at 10:09
  • Can you point provide a link to your source for that? Thanks! – Chris Conover Oct 23 '14 at 00:27
  • I just wrote in lldb: register read . Then with this info we know that the first register in the exception frame holds the exception message. – João Nunes Oct 23 '14 at 07:29
  • Ok i found some docs: rax is a 64bits register: In 64-bit long mode you can use 64-bit registers (e.g. rax instead of eax, rbx instead of ebx, etc..) – João Nunes Oct 23 '14 at 07:32
  • It seems that now you need to typecast these registers to an NSException. `po (NSException*)$eax` `po (NSException*)$rax` – Rickster Feb 19 '15 at 06:26
7

At the time of this writing, this post is my top Google hit for: lldb print exception. Thus, I am adding this answer to account for lldb and x86_64.

My attempts to find the exception using po $eax failed with error: Couldn't materialize struct: Couldn't read eax (materialize). Other attempts described in linked documents from earlier answers also failed.

The key was I had to first click on the objc_exception_throw frame in my main thread. lldb does not start off in that frame.

In all my searching and following examples, this blog entry was the first to explain things in a way that worked for me. It is more modern, being posted in Aug 2012.

Jeff
  • 3,829
  • 1
  • 31
  • 49
1

If you have a catch statement, put a breakpoint in there and you can inspect the exception object at that point.

If you don't have a catch statement, continue.

You'll get a message in your terminal like this:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]'

However, you're probably looking for a way to inspect it without continuing since you'll lose your nice stack trace when the application is terminated.

For that it sounds like Fnord's answer is best, but I wasn't able to get it working in LLDB.

funroll
  • 35,925
  • 7
  • 54
  • 59