diff options
Diffstat (limited to 'Foundation/GTMStackTrace.m')
-rw-r--r-- | Foundation/GTMStackTrace.m | 108 |
1 files changed, 88 insertions, 20 deletions
diff --git a/Foundation/GTMStackTrace.m b/Foundation/GTMStackTrace.m index c22c153..0b28743 100644 --- a/Foundation/GTMStackTrace.m +++ b/Foundation/GTMStackTrace.m @@ -22,16 +22,6 @@ #include "GTMStackTrace.h" #include "GTMObjC2Runtime.h" -// Structure representing a small portion of a stack, starting from the saved -// frame pointer, and continuing through the saved program counter. -struct GTMStackFrame { - void *saved_fp; -#if defined (__ppc__) || defined(__ppc64__) - void *padding; -#endif - void *saved_pc; -}; - struct GTMClassDescription { const char *class_name; Method *class_methods; @@ -187,13 +177,26 @@ static NSString *GTMStackTraceFromAddressDescriptors(struct GTMAddressDescriptor #pragma mark Public functions +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 +// Before 10.5, we have to do this ourselves. 10.5 adds +// +[NSThread callStackReturnAddresses]. + +// Structure representing a small portion of a stack, starting from the saved +// frame pointer, and continuing through the saved program counter. +struct GTMStackFrame { + void *saved_fp; +#if defined (__ppc__) || defined(__ppc64__) + void *padding; +#endif + void *saved_pc; +}; + // __builtin_frame_address(0) is a gcc builtin that returns a pointer to the // current frame pointer. We then use the frame pointer to walk the stack // picking off program counters and other saved frame pointers. This works // great on i386, but PPC requires a little more work because the PC (or link // register) isn't always stored on the stack. // -#ifdef GTM_MACOS_SDK // currently not supported on iPhone NSUInteger GTMGetStackProgramCounters(void *outPcs[], NSUInteger count) { if (!outPcs || (count < 1)) return 0; @@ -220,41 +223,106 @@ NSUInteger GTMGetStackProgramCounters(void *outPcs[], NSUInteger count) { return level; } +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 NSUInteger GTMGetStackAddressDescriptors(struct GTMAddressDescriptor outDescs[], NSUInteger count) { if (count < 1 || !outDescs) return 0; + NSUInteger result = 0; +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + // Before 10.5, we collect the stack ourselves. + void **pcs = calloc(count, sizeof(void*)); if (!pcs) return 0; NSUInteger newSize = GTMGetStackProgramCounters(pcs, count); - NSUInteger result - = GTMGetStackAddressDescriptorsForAddresses(pcs, outDescs, newSize); + result = GTMGetStackAddressDescriptorsForAddresses(pcs, outDescs, newSize); free(pcs); + +#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + // Use +[NSThread callStackReturnAddresses] + + NSArray *addresses = [NSThread callStackReturnAddresses]; + NSUInteger addrCount = [addresses count]; + if (addrCount) { + void **pcs = calloc(addrCount, sizeof(void*)); + if (pcs) { + void **pcsScanner = pcs; + for (NSNumber *address in addresses) { + NSUInteger addr = [address unsignedIntegerValue]; + *pcsScanner = (void *)addr; + ++pcsScanner; + } + if (count < addrCount) { + addrCount = count; + } + // Fill in the desc structures + result = GTMGetStackAddressDescriptorsForAddresses(pcs, outDescs, addrCount); + } + if (pcs) free(pcs); + } +#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + return result; } NSString *GTMStackTrace(void) { + // If we don't have enough frames, return an empty string + NSString *result = @""; + +#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + // Before 10.5, we collect the stack ourselves. + // The maximum number of stack frames that we will walk. We limit this so // that super-duper recursive functions (or bugs) don't send us for an // infinite loop. struct GTMAddressDescriptor descs[100]; size_t depth = sizeof(descs) / sizeof(struct GTMAddressDescriptor); depth = GTMGetStackAddressDescriptors(descs, depth); - + // Start at the second item so that GTMStackTrace and it's utility calls (of // which there is currently 1) is not included in the output. const size_t kTracesToStrip = 2; if (depth > kTracesToStrip) { - return GTMStackTraceFromAddressDescriptors(&descs[kTracesToStrip], - (depth - kTracesToStrip)); + result = GTMStackTraceFromAddressDescriptors(&descs[kTracesToStrip], + (depth - kTracesToStrip)); } - // If we didn't have enough frames, return an empty string - return @""; +#else // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + // Use +[NSThread callStackReturnAddresses] + + NSArray *addresses = [NSThread callStackReturnAddresses]; + NSUInteger count = [addresses count]; + if (count) { + void **pcs = calloc(count, sizeof(void*)); + struct GTMAddressDescriptor *descs + = calloc(count, sizeof(struct GTMAddressDescriptor)); + if (pcs && descs) { + void **pcsScanner = pcs; + for (NSNumber *address in addresses) { + NSUInteger addr = [address unsignedIntegerValue]; + *pcsScanner = (void *)addr; + ++pcsScanner; + } + // Fill in the desc structures + count = GTMGetStackAddressDescriptorsForAddresses(pcs, descs, count); + // Build the trace + // We skip 1 frame because the +[NSThread callStackReturnAddresses] will + // start w/ this frame. + const size_t kTracesToStrip = 1; + if (count > kTracesToStrip) { + result = GTMStackTraceFromAddressDescriptors(&descs[kTracesToStrip], + (count - kTracesToStrip)); + } + } + if (pcs) free(pcs); + if (descs) free(descs); + } +#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 + + return result; } -#endif // GTM_MACOS_SDK #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 @@ -287,4 +355,4 @@ NSString *GTMStackTraceFromException(NSException *e) { return trace; } -#endif // MAC_OS_X_VERSION_MIN_REQUIRED +#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 |