4

In researching debugging exceptions using LLDB, I found the following article and thread, as well as others giving the same information:

https://www.natashatherobot.com/xcode-debugging-trick/

Xcode/LLDB: How to get information about an exception that was just thrown?

When trying variations of these, the best I can get is an int as a result:

(lldb) po $rax

106377751137688

When plugging this into the Xcode memory viewer, trying it as both a base-10 and hex value, there didn't seem to be an object stored there. I get results such as B8 0B 0C 16 01 00 00 00 03... followed by zeros as far as the eye can see. I've tried calling methods like description on the int as if it were an address, casting it as NSException*, which yield the result:

error: Execution was interrupted, reason: Attempted to dereference an invalid ObjC Object or send it an unrecognized selector. The process has been returned to the state before expression evaluation.

Was there a recent change to LLDB that would have broken the expected functionality? I'm using Xcode 9.2 and a mix of swift and objective-c. It might also be worth noting that I don't see the frame objc_exception_throw in the call stack, but rather __cxa_throw at frame 0, which is what I select to get a result.

The exception in particular I'm looking at is generated by a call to -[UIStoryboard instantiateViewControllerWithIdentifier:]

EDIT: If I manually create an NSException and @throw it, I can view it with po $rax. I noticed in this case, frame 0 of the call stack is objc_exception_throw. I've edited the title to specify the type of exception I'm asking about.

Amorphant
  • 43
  • 5

1 Answers1

5

The normal course of an ObjC exception is that the system calls objc_exception_throw passing in the exception object to start the exception. But under the covers, ObjC uses the same exception throwing mechanism that C++ uses to implement the actual unwinding of the stack. So objc_exception_throw will turn around and call __cxa_throw - which also happens to be the C++ exception start point.

When ObjC does this, the object thrown at __cxa_throw - which happens to be its first argument - is an ObjC object. But there are also parts of the system that throw real C++ exceptions on occasion. If you stop in __cxa_throw without first stopping in objc_exception_throw, that's going to be a C++ exception object NOT an NSException, and po does nothing for them.

BTW, when you stop in __cxa_throw the stack hasn't been unwound yet, so the backtrace should show you who is throwing the exception, if you are curious.

In sum, if you want to only see ObjC exceptions, don't stop at the __cxa_throw, just stop at objc_exception_throw. Stopping at __cxa_throw won't add any information, and will cause you to have to sort the spurious pure C++ exceptions from the ObjC ones you care about.

In Xcode, you do this by choosing the ObjC exception breakpoint not "All Exceptions". In command line lldb, do this with:

(lldb) break set -E objc
Jim Ingham
  • 25,260
  • 2
  • 55
  • 63
  • The first link above shows setting the exception breakpoint for All languages, but for the purposes of that post, it should really be just ObjC. – Jim Ingham Jan 19 '18 at 02:07
  • Thanks for the descriptive answer. Should I generally ignore C++ exceptions that don't interfere with operation of the app, or is there a mechanism for viewing them? – Amorphant Jan 19 '18 at 16:47
  • Editing the above comment wasn't working -- I did find the following question that indicated that without C++ in our code, we shouldn't worry about them. https://stackoverflow.com/questions/12861748/how-do-i-know-when-safe-to-ignore-cxa-throw-on-an-all-exceptions-breakpoint – Amorphant Jan 19 '18 at 17:01
  • Yes, if your app isn't crashing with some C++ exception you don't understand, it means that all the exceptions were caught by the throwing code for its own internal purposes, and is uninteresting to you. – Jim Ingham Jan 19 '18 at 19:51