From a0b3607dd654497832032ef5ee24b1554f07a266 Mon Sep 17 00:00:00 2001 From: rfm Date: Mon, 3 Dec 2007 14:13:57 +0000 Subject: [PATCH] Improved stacktrace/debug handling. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@25667 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 13 ++++ Source/GSPrivate.h | 5 ++ Source/NSDebug.m | 182 +++++++++++++++++++++++++++++++++++++++---- Source/NSException.m | 91 +++++++++++----------- Source/NSThread.m | 9 +-- 5 files changed, 234 insertions(+), 66 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7a4ff2f14..01afad227 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2007-12-03 Richard Frith-Macdonald + + * Source/NSDebug.m: + * Source/GSPrivate.h: + * Source/NSException.m: + * Source/NSThread.m: + Rewrite some stackframe handling code for greater efficiency. + When raising an exception, just get the current stack and don't + bother translating addresses to method/function information until + needed by the -description method. Improves performance of + exception handling and eliminates the possibility of a recursive + exception due to a problem in the stacktrace code. + 2007-12-02 David Ayers * Source/NSDecimal.m (GSDecimalDouble): Use NAN when available. diff --git a/Source/GSPrivate.h b/Source/GSPrivate.h index e397c063a..c73f000b2 100644 --- a/Source/GSPrivate.h +++ b/Source/GSPrivate.h @@ -378,6 +378,11 @@ void GSPrivateNotifyIdle(void) GS_ATTRIB_PRIVATE; */ BOOL GSPrivateNotifyMore(void) GS_ATTRIB_PRIVATE; +/* Function to return the current stack return addresses. + */ +NSMutableArray * +GSPrivateStackAddresses(void) GS_ATTRIB_PRIVATE; + /* Function to return the hash value for a small integer (used by NSNumber). */ unsigned diff --git a/Source/NSDebug.m b/Source/NSDebug.m index d7e35e05a..7707adbb8 100644 --- a/Source/NSDebug.m +++ b/Source/NSDebug.m @@ -29,6 +29,7 @@ #include "config.h" #include +#include "GSPrivate.h" #include "GNUstepBase/GSLock.h" #include "Foundation/NSArray.h" #include "Foundation/NSData.h" @@ -38,6 +39,7 @@ #include "Foundation/NSNotification.h" #include "Foundation/NSNotificationQueue.h" #include "Foundation/NSThread.h" +#include "Foundation/NSValue.h" typedef struct { Class class; @@ -854,15 +856,6 @@ GSDebugMethodMsg(id obj, SEL sel, const char *file, int line, NSString *fmt) return message; } -unsigned NSCountFrames(void) -{ - unsigned x = 0; - - while (NSFrameAddress(x + 1)) x++; - - return x; -} - #define _NS_FRAME_HACK(a) case a: val = __builtin_frame_address(a + 1); break; #define _NS_RETURN_HACK(a) case a: val = __builtin_return_address(a + 1); break; @@ -893,7 +886,7 @@ jbuf() if (d == nil) { d = [[NSMutableData alloc] initWithLength: - sizeof(jmp_buf) + sizeof(void(*)(int))]; + sizeof(jmp_buf) + sizeof(void(*)(int)) + sizeof(void*)]; [[[NSThread currentThread] threadDictionary] setObject: d forKey: @"GSjbuf"]; RELEASE(d); @@ -920,7 +913,9 @@ NSFrameAddress(int offset) if (setjmp(*env) == 0) { old = signal(SIGSEGV, recover); - memcpy(env + 1, &old, sizeof(old)); + val = (void*)env; + val += sizeof(jmp_buf); + memcpy(val, &old, sizeof(old)); switch (offset) { _NS_FRAME_HACK(0); _NS_FRAME_HACK(1); _NS_FRAME_HACK(2); @@ -963,14 +958,88 @@ NSFrameAddress(int offset) } else { - env = jbuf(); - memcpy(&old, env + 1, sizeof(old)); + val = jbuf(); + val += sizeof(jmp_buf); + memcpy(&old, val, sizeof(old)); signal(SIGSEGV, old); val = NULL; } return val; } +unsigned NSCountFrames(void) +{ + jmp_buf *env; + void (*old)(int); + void *val; + + env = jbuf(); + if (setjmp(*env) == 0) + { + unsigned *loc; + + old = signal(SIGSEGV, recover); + val = (void*)env; + val += sizeof(jmp_buf); + memcpy(val, &old, sizeof(old)); + val += sizeof(old); + loc = (unsigned*)val; + *loc = 0; + +#define _NS_COUNT_HACK(X) if (__builtin_frame_address(X + 1) == 0) \ + goto done; else *loc = X + 1; + + _NS_COUNT_HACK(0); _NS_COUNT_HACK(1); _NS_COUNT_HACK(2); + _NS_COUNT_HACK(3); _NS_COUNT_HACK(4); _NS_COUNT_HACK(5); + _NS_COUNT_HACK(6); _NS_COUNT_HACK(7); _NS_COUNT_HACK(8); + _NS_COUNT_HACK(9); _NS_COUNT_HACK(10); _NS_COUNT_HACK(11); + _NS_COUNT_HACK(12); _NS_COUNT_HACK(13); _NS_COUNT_HACK(14); + _NS_COUNT_HACK(15); _NS_COUNT_HACK(16); _NS_COUNT_HACK(17); + _NS_COUNT_HACK(18); _NS_COUNT_HACK(19); _NS_COUNT_HACK(20); + _NS_COUNT_HACK(21); _NS_COUNT_HACK(22); _NS_COUNT_HACK(23); + _NS_COUNT_HACK(24); _NS_COUNT_HACK(25); _NS_COUNT_HACK(26); + _NS_COUNT_HACK(27); _NS_COUNT_HACK(28); _NS_COUNT_HACK(29); + _NS_COUNT_HACK(30); _NS_COUNT_HACK(31); _NS_COUNT_HACK(32); + _NS_COUNT_HACK(33); _NS_COUNT_HACK(34); _NS_COUNT_HACK(35); + _NS_COUNT_HACK(36); _NS_COUNT_HACK(37); _NS_COUNT_HACK(38); + _NS_COUNT_HACK(39); _NS_COUNT_HACK(40); _NS_COUNT_HACK(41); + _NS_COUNT_HACK(42); _NS_COUNT_HACK(43); _NS_COUNT_HACK(44); + _NS_COUNT_HACK(45); _NS_COUNT_HACK(46); _NS_COUNT_HACK(47); + _NS_COUNT_HACK(48); _NS_COUNT_HACK(49); _NS_COUNT_HACK(50); + _NS_COUNT_HACK(51); _NS_COUNT_HACK(52); _NS_COUNT_HACK(53); + _NS_COUNT_HACK(54); _NS_COUNT_HACK(55); _NS_COUNT_HACK(56); + _NS_COUNT_HACK(57); _NS_COUNT_HACK(58); _NS_COUNT_HACK(59); + _NS_COUNT_HACK(60); _NS_COUNT_HACK(61); _NS_COUNT_HACK(62); + _NS_COUNT_HACK(63); _NS_COUNT_HACK(64); _NS_COUNT_HACK(65); + _NS_COUNT_HACK(66); _NS_COUNT_HACK(67); _NS_COUNT_HACK(68); + _NS_COUNT_HACK(69); _NS_COUNT_HACK(70); _NS_COUNT_HACK(71); + _NS_COUNT_HACK(72); _NS_COUNT_HACK(73); _NS_COUNT_HACK(74); + _NS_COUNT_HACK(75); _NS_COUNT_HACK(76); _NS_COUNT_HACK(77); + _NS_COUNT_HACK(78); _NS_COUNT_HACK(79); _NS_COUNT_HACK(80); + _NS_COUNT_HACK(81); _NS_COUNT_HACK(82); _NS_COUNT_HACK(83); + _NS_COUNT_HACK(84); _NS_COUNT_HACK(85); _NS_COUNT_HACK(86); + _NS_COUNT_HACK(87); _NS_COUNT_HACK(88); _NS_COUNT_HACK(89); + _NS_COUNT_HACK(90); _NS_COUNT_HACK(91); _NS_COUNT_HACK(92); + _NS_COUNT_HACK(93); _NS_COUNT_HACK(94); _NS_COUNT_HACK(95); + _NS_COUNT_HACK(96); _NS_COUNT_HACK(97); _NS_COUNT_HACK(98); + _NS_COUNT_HACK(99); + +done: + signal(SIGSEGV, old); + } + else + { + env = jbuf(); + val = (void*)env; + val += sizeof(jmp_buf); + memcpy(&old, val, sizeof(old)); + signal(SIGSEGV, old); + } + + val = (void*)env + sizeof(jmp_buf) + sizeof(old); + return *(unsigned*)val; +} + void * NSReturnAddress(int offset) { @@ -982,7 +1051,9 @@ NSReturnAddress(int offset) if (setjmp(*env) == 0) { old = signal(SIGSEGV, recover); - memcpy(env + 1, &old, sizeof(old)); + val = (void*)env; + val += sizeof(jmp_buf); + memcpy(val, &old, sizeof(old)); switch (offset) { _NS_RETURN_HACK(0); _NS_RETURN_HACK(1); _NS_RETURN_HACK(2); @@ -1025,8 +1096,9 @@ NSReturnAddress(int offset) } else { - env = jbuf(); - memcpy(&old, env + 1, sizeof(old)); + val = jbuf(); + val += sizeof(jmp_buf); + memcpy(&old, val, sizeof(old)); signal(SIGSEGV, old); val = NULL; } @@ -1034,6 +1106,84 @@ NSReturnAddress(int offset) return val; } +NSMutableArray * +GSPrivateStackAddresses(void) +{ + unsigned n = NSCountFrames(); + NSMutableArray *stack = [NSMutableArray arrayWithCapacity: n]; + unsigned i; + jmp_buf *env; + void (*old)(int); + void *val; + + env = jbuf(); + if (setjmp(*env) == 0) + { + old = signal(SIGSEGV, recover); + val = (void*)env; + val += sizeof(jmp_buf); + memcpy(val, &old, sizeof(old)); + + for (i = 0; i < n; i++) + { + switch (i) + { + _NS_RETURN_HACK(0); _NS_RETURN_HACK(1); _NS_RETURN_HACK(2); + _NS_RETURN_HACK(3); _NS_RETURN_HACK(4); _NS_RETURN_HACK(5); + _NS_RETURN_HACK(6); _NS_RETURN_HACK(7); _NS_RETURN_HACK(8); + _NS_RETURN_HACK(9); _NS_RETURN_HACK(10); _NS_RETURN_HACK(11); + _NS_RETURN_HACK(12); _NS_RETURN_HACK(13); _NS_RETURN_HACK(14); + _NS_RETURN_HACK(15); _NS_RETURN_HACK(16); _NS_RETURN_HACK(17); + _NS_RETURN_HACK(18); _NS_RETURN_HACK(19); _NS_RETURN_HACK(20); + _NS_RETURN_HACK(21); _NS_RETURN_HACK(22); _NS_RETURN_HACK(23); + _NS_RETURN_HACK(24); _NS_RETURN_HACK(25); _NS_RETURN_HACK(26); + _NS_RETURN_HACK(27); _NS_RETURN_HACK(28); _NS_RETURN_HACK(29); + _NS_RETURN_HACK(30); _NS_RETURN_HACK(31); _NS_RETURN_HACK(32); + _NS_RETURN_HACK(33); _NS_RETURN_HACK(34); _NS_RETURN_HACK(35); + _NS_RETURN_HACK(36); _NS_RETURN_HACK(37); _NS_RETURN_HACK(38); + _NS_RETURN_HACK(39); _NS_RETURN_HACK(40); _NS_RETURN_HACK(41); + _NS_RETURN_HACK(42); _NS_RETURN_HACK(43); _NS_RETURN_HACK(44); + _NS_RETURN_HACK(45); _NS_RETURN_HACK(46); _NS_RETURN_HACK(47); + _NS_RETURN_HACK(48); _NS_RETURN_HACK(49); _NS_RETURN_HACK(50); + _NS_RETURN_HACK(51); _NS_RETURN_HACK(52); _NS_RETURN_HACK(53); + _NS_RETURN_HACK(54); _NS_RETURN_HACK(55); _NS_RETURN_HACK(56); + _NS_RETURN_HACK(57); _NS_RETURN_HACK(58); _NS_RETURN_HACK(59); + _NS_RETURN_HACK(60); _NS_RETURN_HACK(61); _NS_RETURN_HACK(62); + _NS_RETURN_HACK(63); _NS_RETURN_HACK(64); _NS_RETURN_HACK(65); + _NS_RETURN_HACK(66); _NS_RETURN_HACK(67); _NS_RETURN_HACK(68); + _NS_RETURN_HACK(69); _NS_RETURN_HACK(70); _NS_RETURN_HACK(71); + _NS_RETURN_HACK(72); _NS_RETURN_HACK(73); _NS_RETURN_HACK(74); + _NS_RETURN_HACK(75); _NS_RETURN_HACK(76); _NS_RETURN_HACK(77); + _NS_RETURN_HACK(78); _NS_RETURN_HACK(79); _NS_RETURN_HACK(80); + _NS_RETURN_HACK(81); _NS_RETURN_HACK(82); _NS_RETURN_HACK(83); + _NS_RETURN_HACK(84); _NS_RETURN_HACK(85); _NS_RETURN_HACK(86); + _NS_RETURN_HACK(87); _NS_RETURN_HACK(88); _NS_RETURN_HACK(89); + _NS_RETURN_HACK(90); _NS_RETURN_HACK(91); _NS_RETURN_HACK(92); + _NS_RETURN_HACK(93); _NS_RETURN_HACK(94); _NS_RETURN_HACK(95); + _NS_RETURN_HACK(96); _NS_RETURN_HACK(97); _NS_RETURN_HACK(98); + _NS_RETURN_HACK(99); + default: val = 0; break; + } + if (val == 0) + { + break; + } + [stack addObject: [NSValue valueWithPointer: val]]; + } + signal(SIGSEGV, old); + } + else + { + env = jbuf(); + val = (void*)env; + val += sizeof(jmp_buf); + memcpy(&old, val, sizeof(old)); + signal(SIGSEGV, old); + } + + return stack; +} + const char *_NSPrintForDebugger(id object) { diff --git a/Source/NSException.m b/Source/NSException.m index 5b9637e33..3a43f023b 100644 --- a/Source/NSException.m +++ b/Source/NSException.m @@ -25,6 +25,7 @@ */ #import "config.h" +#import "GSPrivate.h" #import "GNUstepBase/preface.h" #import #import @@ -55,6 +56,7 @@ typedef struct { @defs(NSThread) } *TInfo; - (NSMutableArray*) frames; - (id) frameAt: (unsigned)index; - (unsigned) frameCount; +- (id) initWithAddresses: (NSArray*)stack; - (NSEnumerator*) reverseEnumerator; @end @@ -561,17 +563,24 @@ GSListModules() // grab the current stack - (id) init { + NSMutableArray *stack = GSPrivateStackAddresses(); + + return [self initWithAddresses: stack]; +} + +- (id) initWithAddresses: (NSArray*)stack +{ +#if defined(STACKSYMBOLS) int i; int n; - frames = [[NSMutableArray alloc] init]; - n = NSCountFrames(); + n = [stack count]; + frames = [[NSMutableArray alloc] initWithCapacity: n]; -#if defined(STACKSYMBOLS) for (i = 0; i < n; i++) { GSFunctionInfo *aFrame = nil; - void *address = NSReturnAddress(i); + void *address = [[stack objectAtIndex: i] pointerValue]; void *base; NSString *modulePath = GSPrivateBaseAddress(address, &base); GSBinaryFileInfo *bfi; @@ -628,12 +637,7 @@ GSListModules() [frames addObject: aFrame]; } #else - for (i = 0; i < n; i++) - { - void *address = NSReturnAddress(i); - - [frames addObject: [NSValue valueWithPointer: address]]; - } + frames = [stack copy]; #endif return self; @@ -646,35 +650,6 @@ GSListModules() @end -/** - * Get a stack trace and convert it to an array of return addresses. - */ -@interface NSThread (Frames) -+ (NSArray*) callStackReturnAddresses; -@end -@implementation NSThread (Frames) -+ (NSArray*) callStackReturnAddresses -{ - NSMutableArray *frames = [[GSStackTrace currentStack] frames]; -#if defined(STACKSYMBOLS) - unsigned count = [frames count]; - - while (count-- > 0) - { - GSFunctionInfo *info = [frames objectAtIndex: count]; - NSValue *address; - - address = [NSValue valueWithPointer: [info address]]; - [frames replaceObjectAtIndex: count - withObject: address]; - } -#endif - return frames; -} -@end - - - NSString* const NSGenericException = @"NSGenericException"; @@ -697,8 +672,6 @@ NSString* const NSCharacterConversionException NSString* const NSParseErrorException = @"NSParseErrorException"; -#include "GSPrivate.h" - static void _terminate() { BOOL shouldAbort; @@ -826,7 +799,7 @@ _NSFoundationUncaughtExceptionHandler (NSException *exception) RELEASE(_e_info); _e_info = m; } - [m setObject: [GSStackTrace currentStack] forKey: @"GSStackTraceKey"]; + [m setObject: GSPrivateStackAddresses() forKey: @"GSStackTraceKey"]; } #endif @@ -948,11 +921,41 @@ _NSFoundationUncaughtExceptionHandler (NSException *exception) - (NSString*) description { if (_e_info) - return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@ INFO:%@", + { +/* Convert stack information from an array of addresses to a stacktrace + * for display. + */ +#if defined(STACKSYMBOLS) + id o; + + o = [_e_info objectForKey: @"GSStackTraceKey"]; + if ([o isKindOfClass: [NSArray class]] == YES) + { + NSMutableDictionary *m; + + o = [[GSStackTrace alloc] initWithAddresses: o]; + if ([_e_info isKindOfClass: [NSMutableDictionary class]] == YES) + { + m = (NSMutableDictionary*)_e_info; + } + else + { + m = [_e_info mutableCopy]; + RELEASE(_e_info); + _e_info = m; + } + [m setObject: o forKey: @"GSStackTraceKey"]; + RELEASE(o); + } +#endif + return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@ INFO:%@", [super description], _e_name, _e_reason, _e_info]; + } else - return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@", + { + return [NSString stringWithFormat: @"%@ NAME:%@ REASON:%@", [super description], _e_name, _e_reason]; + } } @end diff --git a/Source/NSThread.m b/Source/NSThread.m index 2df1e9ffe..7580d7139 100644 --- a/Source/NSThread.m +++ b/Source/NSThread.m @@ -484,12 +484,9 @@ gnustep_base_thread_callback(void) + (NSArray*) callStackReturnAddresses { - /* NB. This method is actually implemented in a category in - * The NSException.m file as the stack trace code there is - * able to set up an array of stack addresses and we don't - * want to duplicate the code. - */ - return [self notImplemented: _cmd]; + NSMutableArray *stack = GSPrivateStackAddresses(); + + return stack; } /**