aboutsummaryrefslogtreecommitdiff
path: root/Foundation/GTMStackTrace.m
diff options
context:
space:
mode:
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