One element of making a solid application is to make it as easy as possible for users to report problems to you. But, it doesn't help you the developer much if you get a report such as "I received an error 42 when I tried to load my file." Wouldn't it be better to have the error report tell you the exact line of code.
I'm going to show you how to extract a stack trace from an NSException. Apple has kindly posted a guide to printing symbolic stack traces, however, their solution requires that the Developer Tools be installed because it uses the atos command-line utility.
The method I will outline below does not require any external command-line tools which may-or-may-not be installed. Instead, they require a couple of glibc functions.
And, just to get fancy, this code will also de-mangle C++ symbols in your stack traces. Those who are working in Objective-C only (lucky bastards) can delete half of the code with no repercussions.
This is the sort of problem best described with code, so here goes:
NSString * UTstackTraceForException(NSException * exception)
{
NSArray * addresses = [exception callStackReturnAddresses];
void * backtrace_frames[[addresses count]];
int i = 0;
NSString * trace = @"";
NSString * frame_description, * symbol;
for (NSNumber * address in addresses)
{
backtrace_frames[i] = (void *)[address unsignedLongValue];
i++;
}
char **frameStrings = backtrace_symbols(&backtrace_frames[0], [addresses count]);
if(frameStrings != NULL) {
int x;
for(x = 0; x < [addresses count]; x++) {
// Loop over the description of each stack frame.
// Try to find the symbol name and demangle it if possible
if(frameStrings[x] == NULL) { break; }
char * demangled = NULL;
size_t length;
int status;
frame_description = [NSString stringWithUTF8String:frameStrings[x]];
NSArray * components = [frame_description componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
int count = 0;
for (NSString * component in components)
{
if ([component length] == 0)
continue;
count++;
if (count == 4)
{
symbol = component;
demangled = __cxxabiv1::__cxa_demangle([symbol UTF8String],
demangled,
&length, &status);
break;
}
}
if (demangled)
{
NSString * demangled_string = [NSString stringWithUTF8String:demangled];
frame_description = [frame_description stringByReplacingOccurrencesOfString:symbol withString:demangled_string];
free(demangled);
}
trace = [trace stringByAppendingFormat:@"%@\n", frame_description];
}
free(frameStrings);
}
return trace;
}