Getting a useful stack trace from NSException#callStackReturnAddresses

December 30, 2008 — 6 Comments

Its always been a little painful when you get an exception and the debugger decides not to cooperate, leaving you with something like:

Stack Trace

Thanks!

As far as I could work out, traditional methods of grabbing a symbolic stack trace don’t work on the iPhone. (If I am remembering right, I don’t think NSStackTraceKey exists in UIKit.)

A little while ago I hacked the GTMStackTrace from google-toolbox-for-mac, to lookup the symbolic stack trace from the call stack return addresses (NSException#callStackReturnAddresses introduced in 10.5) and print it out using NSSetUncaughtExceptionHandler. Life has been a little bit easier ever since. Thankfully, the awesome people who work on GTM added it in themselves (see GTMStackTrace.h#61). Here is how you might set it up, in main.m:

#ifdef DEBUG
#import 
#import "GTMStackTrace.h"

void exceptionHandler(NSException *exception) {
	NSLog(@"%@", GTMStackTraceFromException(exception));
}
#endif

int main(int argc, char *argv[]) {
    
#ifdef DEBUG
	NSLog(@"Debug enabled");	
	NSDebugEnabled = YES;
	NSZombieEnabled = YES;
	NSDeallocateZombies = NO;
	NSHangOnUncaughtException = YES;
	[NSAutoreleasePool enableFreedObjectCheck:YES];
	NSSetUncaughtExceptionHandler(&exceptionHandler);
#endif	

	NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
	int retVal = UIApplicationMain(argc, argv, nil, nil);
	[pool release];
	return retVal;
}

For me, DEBUG is set in GCC_PREPROCESSOR_DEFINITIONS via the awesome GTM xcconfg files, which you should also be using. Hopefully, you should get a trace that looks like:

[Session started at 2008-12-30 00:10:13 -0800.]
2008-12-30 00:10:15.772 TestExceptionIPhone[8493:20b] Debug enabled
2008-12-30 00:10:15.830 TestExceptionIPhone[8493:20b] #0  0x92c5614b _NSExceptionSetRubyToken()  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#1  0x9657ce3b objc_exception_throw()  (/usr/lib/libobjc.A.dylib)
#2  0x92c55f2b +[NSException raise:format:arguments:]  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#3  0x92c55f6a +[NSException raise:format:]  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#4  0x002350 +[TestExceptionIPhoneAppDelegate applicationDidFinishLaunching:]  (/Users/gabe/Library/Application Support/iPhone Simulator/User/Applications/586E48B6-B758-4DBD-A534-D6927DA74583/TestExceptionIPhone.app/TestExceptionIPhone)
#5  0x30a4e01a +[UIApplication performInitializationWithURL:asPanel:]  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit)
#6  0x30a57363 +[UIApplication _runWithURL:]  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit)
#7  0x900425ee +[NSRunLoop runMode:beforeDate:]  (/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation)
#8  0x92bdcb45 CFRunLoopRunSpecific()  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#9  0x92bdccf8 CFRunLoopRunInMode()  (/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation)
#10 0x31564600 GSEventRunModal()  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices)
#11 0x315646c5 GSEventRun()  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/PrivateFrameworks/GraphicsServices.framework/GraphicsServices)
#12 0x30a4ec98 +[UIApplication _run]  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit)
#13 0x30a5a094 UIApplicationMain()  (/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.2.sdk/System/Library/Frameworks/UIKit.framework/UIKit)
2008-12-30 00:10:15.834 TestExceptionIPhone[8493:20b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Give me a good stack trace'

And the source of the exception usually starts around the 5th line.

6 responses to Getting a useful stack trace from NSException#callStackReturnAddresses

  1. Hi,

    This would be great! How can I get GTM set up in my iPhone app (with as small a footprint as possible please).

    Thanks

  2. Actually, atos works for the iPhone, at least when you have a debug binary of your app.

    For those times when the debugger doesn’t play nice, you can use the cryptic stack trace: take the lower addresses (usually, as in the example above, those that are five or six digits long) that actually correspond to your symbols (as opposed to the system functions that are using much higher addresses), convert to hex and run atos with ‘-arch armv6′ on the debug executable (found in build/Debug-iphoneos).

  3. Oh right on. Yeah I guess I was referring to how you can’t use NSTask in an iphone project, and the advantage of running with the GTMStackTrace stuff is that you can have it automatically dump the trace instead of having to manually run atos after the fact.

  4. Works great. Really.

Trackbacks and Pingbacks:

  1. Ben’s Dev Blog » Blog Archive » Stack Traces for iPhone development - January 11, 2009

    [...] Hanford posted a great example of using the Google Toolbox for Mac to print human readable stack [...]

  2. August Atlanta iPhone User Group Meeting Follow-up | Atlanta iPhone & iTouch Developers - August 12, 2009

    [...] I had more information about this stack trace!” – come back here and follow the link to:  GTM stacktrace information.  Its a guide to using Google Toolbox for Mac.  This is the same toolbox that provides a simpler [...]

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s