aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMStackTrace.m
diff options
context:
space:
mode:
authorGravatar thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-12-12 15:24:34 +0000
committerGravatar thomasvl@gmail.com <thomasvl@gmail.com@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2008-12-12 15:24:34 +0000
commit2e8516354aacef064d01425808da06d2cdcb4791 (patch)
tree9da4758828930280d32f18d54ece7a249df742c7 /Foundation/GTMStackTrace.m
parent9f64d056dd70f2f938ac6f5adb8e75b650dc2e1a (diff)
- 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.
Diffstat (limited to 'Foundation/GTMStackTrace.m')
-rw-r--r--Foundation/GTMStackTrace.m108
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