From 2e8516354aacef064d01425808da06d2cdcb4791 Mon Sep 17 00:00:00 2001 From: "thomasvl@gmail.com" Date: Fri, 12 Dec 2008 15:24:34 +0000 Subject: - GTMStackTrace works on 10.5+ (and iPhone) using NSThread to build the call stack. - Added GTM_EXTERN that makes it easier to mix and match objc and objc++ code. - Added GTMHotKeysTextField for display and editing of hot key settings. - Added GTMCarbonEvent for dealing with Carbon Events and HotKeys in a ObjC like way. - Backported the Atomic Barrier Swap functions for Objective C back to Tiger. - Added a variety of new functions to GTMUnitTestingUtilities for checking if the screensaver is in the way, waiting on user events, and generating keystrokes. - If you are using any Carbon routines that log (DebugStr, AssertMacros.h) and use GTMUnitTestDevLog, the log routines now go through _GTMDevLog so that they can be caught in GTMUnitTestDevLog and verified like any _GTMDevLog calls you may make. For an example of this in action see GTMCarbonEventTest.m. - Added GTMFileSystemKQueue. It provides a simple wrapper for kqueuing something in the file system and tracking changes to it. - RunIPhoneUnitTest.sh now cleans up the user home directory and creates a documents directory within it, used when requesting a NSDocumentDirectory. - Added GTMNSFileManager+Carbon which contains routines for path <-> Alias conversion and path <-> FSRef conversion. - Added GTMNSArray+Merge for merging one array into another with or without a custom merging function, returning a new array with the merged contents. --- Foundation/GTMStackTrace.m | 108 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 88 insertions(+), 20 deletions(-) (limited to 'Foundation/GTMStackTrace.m') 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 -- cgit v1.2.3