From 6ce4c9788c79b459fdf949cfd7af632769b47c8d Mon Sep 17 00:00:00 2001 From: theraven Date: Fri, 11 Sep 2009 16:14:45 +0000 Subject: [PATCH] Rewrote exception callstack generation to use the backtrace() and backtrace_symbols() code. Implemented the -callStackSymbols method from 10.5 using this. For this to be enabled, the configure script will require a small modification, which Gregory will add later. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@28662 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 10 + Headers/Foundation/NSException.h | 7 + Source/NSException.m | 698 ++++--------------------------- 3 files changed, 108 insertions(+), 607 deletions(-) diff --git a/ChangeLog b/ChangeLog index d4b0952ca..7fd69150f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2009-09-11 David Chisnall + + * Source/NSException.m: + *Headers/Foundation/NSException.h: + + Rewrote exception callstack generation to use the backtrace() and + backtrace_symbols() code. Implemented the -callStackSymbols method from + 10.5 using this. For this to be enabled, the configure script will + require a small modification, which Gregory will add later. + 2009-09-10 David Chisnall * Source/GSFFIInvocation.m: diff --git a/Headers/Foundation/NSException.h b/Headers/Foundation/NSException.h index 1b9ee7f88..ca5ac1585 100644 --- a/Headers/Foundation/NSException.h +++ b/Headers/Foundation/NSException.h @@ -123,6 +123,13 @@ extern "C" { * this value. */ - (NSArray*) callStackReturnAddresses; +/** + * Returns an array of the symbolic names of the call stack return addresses. + * Note that, on some platforms, symbols are only exported in + * position-independent code and so these may only return numeric addresses for + * code in static libraries or the main application. + */ +- (NSArray *)callStackSymbols; #endif /** diff --git a/Source/NSException.m b/Source/NSException.m index e4fc13c79..1827fe992 100644 --- a/Source/NSException.m +++ b/Source/NSException.m @@ -40,6 +40,9 @@ #import "Foundation/NSDictionary.h" #import "Foundation/NSValue.h" #include +#ifdef HAVE_BACKTRACE +#include +#endif #define _e_info (((id*)_reserved)[0]) @@ -49,612 +52,105 @@ typedef struct { @defs(NSThread) } *TInfo; /* This is the GNU name for the CTOR list */ +Class GSStackTraceClass; + @interface GSStackTrace : NSObject { - NSMutableArray *frames; + void **addresses; + NSArray *addressArray; + NSArray *symbols; + int count; } -+ (GSStackTrace*) currentStack; - -- (NSString*) description; -- (NSEnumerator*) enumerator; -- (NSMutableArray*) frames; -- (id) frameAt: (NSUInteger)index; -- (NSUInteger) frameCount; -- (id) initWithAddresses: (NSArray*)stack; -- (NSEnumerator*) reverseEnumerator; - +- (NSArray*) symbols; +- (NSArray*)addresses; +@end +@interface NSException (StackTracePrivate) +- (GSStackTrace*)_callStack; @end -#define STACKSYMBOLS 1 - -/* - * Turn off STACKSYMBOLS if we don't have bfd support for it. - */ -#if !(defined(HAVE_BFD_H) && defined(HAVE_LIBBFD) && defined(HAVE_LIBIBERTY)) -#if defined(STACKSYMBOLS) -#undef STACKSYMBOLS -#endif -#endif - -/* - * Turn off STACKSYMBOLS if we have NDEBUG defined ... if we are built - * with NDEBUG then we are probably missing stackframe information etc. - */ -#if defined(NDEBUG) -#if defined(STACKSYMBOLS) -#undef STACKSYMBOLS -#endif -#endif - - -#if defined(__MINGW32__) -#if defined(STACKSYMBOLS) -static NSString * -GSPrivateBaseAddress(void *addr, void **base) -{ - return nil; -} -#endif /* STACKSYMBOLS */ -#else /* __MINGW32__ */ - -#ifndef GNU_SOURCE -#define GNU_SOURCE -#endif -#ifndef __USE_GNU -#define __USE_GNU -#endif -#include - -#if defined(STACKSYMBOLS) -static NSString * -GSPrivateBaseAddress(void *addr, void **base) -{ -#ifdef HAVE_DLADDR - Dl_info info; - - if (!dladdr(addr, &info)) - return nil; - - *base = info.dli_fbase; - - return [NSString stringWithUTF8String: info.dli_fname]; -#else - return nil; -#endif -} -#endif /* STACKSYMBOLS */ -#endif /* __MINGW32__ */ - -#if defined(STACKSYMBOLS) - -// GSStackTrace inspired by FYStackTrace.m -// created by Wim Oudshoorn on Mon 11-Apr-2006 -// reworked by Lloyd Dupont @ NovaMind.com on 4-May-2006 - -#include - -@class GSBinaryFileInfo; - -@interface GSFunctionInfo : NSObject -{ - void *_address; - NSString *_fileName; - NSString *_functionName; - int _lineNo; - GSBinaryFileInfo *_module; -} -- (void*) address; -- (NSString *) fileName; -- (NSString *) function; -- (id) initWithModule: (GSBinaryFileInfo*)module - address: (void*)address - file: (NSString*)file - function: (NSString*)function - line: (NSInteger)lineNo; -- (NSInteger) lineNumber; -- (GSBinaryFileInfo*) module; - -@end - - -@interface GSBinaryFileInfo : NSObject -{ - NSString *_fileName; - bfd *_abfd; - asymbol **_symbols; - long _symbolCount; -} -- (NSString *) fileName; -- (GSFunctionInfo *) functionForAddress: (void*) address; -- (id) initWithBinaryFile: (NSString *)fileName; -- (id) init; // return info for the current executing process - -@end - - - -@implementation GSFunctionInfo - -- (void*) address -{ - return _address; -} - -- (oneway void) dealloc -{ - DESTROY(_module); - DESTROY(_fileName); - DESTROY(_functionName); - [super dealloc]; -} - -- (NSString *) description -{ - return [NSString stringWithFormat: @"(%@: %p) %@ %@: %d", - [_module fileName], _address, _functionName, _fileName, _lineNo]; -} - -- (NSString *) fileName -{ - return _fileName; -} - -- (NSString *) function -{ - return _functionName; -} - -- (id) init -{ - [self release]; - return nil; -} - -- (id) initWithModule: (GSBinaryFileInfo*)module - address: (void*)address - file: (NSString*)file - function: (NSString*)function - line: (NSInteger)lineNo -{ - _module = RETAIN(module); - _address = address; - _fileName = [file copy]; - _functionName = [function copy]; - _lineNo = lineNo; - - return self; -} - -- (NSInteger) lineNumber -{ - return _lineNo; -} - -- (GSBinaryFileInfo *) module -{ - return _module; -} - -@end - - - -@implementation GSBinaryFileInfo - -+ (GSBinaryFileInfo*) infoWithBinaryFile: (NSString *)fileName -{ - return [[[self alloc] initWithBinaryFile: fileName] autorelease]; -} - -+ (void) initialize -{ - static BOOL first = YES; - - if (first == NO) - { - return; - } - first = NO; - bfd_init (); -} - -- (oneway void) dealloc -{ - DESTROY(_fileName); - if (_abfd) - { - bfd_close (_abfd); - _abfd = NULL; - } - if (_symbols) - { - objc_free (_symbols); - _symbols = NULL; - } - [super dealloc]; -} - -- (NSString *) fileName -{ - return _fileName; -} - -- (id) init -{ - NSString *processName; - - processName = [[[NSProcessInfo processInfo] arguments] objectAtIndex: 0]; - return [self initWithBinaryFile: processName]; -} - -- (id) initWithBinaryFile: (NSString *)fileName -{ - int neededSpace; - - // 1st initialize the bfd - if ([fileName length] == 0) - { - //NSLog (@"GSBinaryFileInfo: No File"); - [self release]; - return nil; - } - _fileName = [fileName copy]; - _abfd = bfd_openr ([fileName cString], NULL); - if (!_abfd) - { - //NSLog (@"GSBinaryFileInfo: No Binary Info"); - [self release]; - return nil; - } - if (!bfd_check_format_matches (_abfd, bfd_object, NULL)) - { - //NSLog (@"GSBinaryFileInfo: BFD format object error"); - [self release]; - return nil; - } - - // second read the symbols from it - if (!(bfd_get_file_flags (_abfd) & HAS_SYMS)) - { - //NSLog (@"GSBinaryFileInfo: BFD does not contain any symbols"); - [self release]; - return nil; - } - - neededSpace = bfd_get_symtab_upper_bound (_abfd); - if (neededSpace < 0) - { - //NSLog (@"GSBinaryFileInfo: BFD error while deducing needed space"); - [self release]; - return nil; - } - if (neededSpace == 0) - { - //NSLog (@"GSBinaryFileInfo: BFD no space for symbols needed"); - [self release]; - return nil; - } - _symbols = objc_malloc (neededSpace); - if (!_symbols) - { - //NSLog (@"GSBinaryFileInfo: Can't allocate buffer"); - [self release]; - return nil; - } - _symbolCount = bfd_canonicalize_symtab (_abfd, _symbols); - if (_symbolCount < 0) - { - //NSLog (@"GSBinaryFileInfo: BFD error while reading symbols"); - [self release]; - return nil; - } - - return self; -} - -struct SearchAddressStruct -{ - void *theAddress; - GSBinaryFileInfo *module; - asymbol **symbols; - GSFunctionInfo *theInfo; -}; - -static void find_address (bfd *abfd, asection *section, - struct SearchAddressStruct *info) -{ - bfd_vma address; - bfd_vma vma; - unsigned size; - const char *fileName; - const char *functionName; - unsigned line = 0; - - if (info->theInfo) - { - return; - } - if (!(bfd_get_section_flags (abfd, section) & SEC_ALLOC)) - { - return; - } - - address = (bfd_vma) (intptr_t)info->theAddress; - - vma = bfd_get_section_vma (abfd, section); - -#if defined(bfd_get_section_size) - size = bfd_get_section_size (section); // recent -#else - size = bfd_section_size (abfd, section); // older version -#endif - - if (address < vma || address >= vma + size) - { - return; - } - - if (bfd_find_nearest_line (abfd, section, info->symbols, - address - vma, &fileName, &functionName, &line)) - { - GSFunctionInfo *fi; - NSString *file = nil; - NSString *func = nil; - - if (fileName != 0) - { - file = [NSString stringWithCString: fileName - encoding: [NSString defaultCStringEncoding]]; - } - if (functionName != 0) - { - func = [NSString stringWithCString: functionName - encoding: [NSString defaultCStringEncoding]]; - } - fi = [GSFunctionInfo alloc]; - fi = [fi initWithModule: info->module - address: info->theAddress - file: file - function: func - line: line]; - [fi autorelease]; - info->theInfo = fi; - } -} - -- (GSFunctionInfo *) functionForAddress: (void*) address -{ - struct SearchAddressStruct searchInfo = - { address, self, _symbols, nil }; - - bfd_map_over_sections (_abfd, - (void (*) (bfd *, asection *, void *)) find_address, &searchInfo); - return searchInfo.theInfo; -} - -@end - -static NSRecursiveLock *modLock = nil; -static NSMutableDictionary *stackModules = nil; - -// initialize stack trace info -static id -GSLoadModule(NSString *fileName) -{ - GSBinaryFileInfo *module = nil; - - [modLock lock]; - - if (stackModules == nil) - { - NSEnumerator *enumerator; - NSBundle *bundle; - - stackModules = [NSMutableDictionary new]; - - /* - * Try to ensure we have the main, base and gui library bundles. - */ - [NSBundle mainBundle]; - [NSBundle bundleForClass: [NSObject class]]; - [NSBundle bundleForClass: NSClassFromString(@"NSView")]; - - /* - * Add file info for all bundles with code. - */ - enumerator = [[NSBundle allBundles] objectEnumerator]; - while ((bundle = [enumerator nextObject]) != nil) - { - if ([bundle load] == YES) - { - GSLoadModule([bundle executablePath]); - } - } - } - - if ([fileName length] > 0) - { - module = [stackModules objectForKey: fileName]; - if (module == nil); - { - module = [GSBinaryFileInfo infoWithBinaryFile: fileName]; - if (module == nil) - { - module = (id)[NSNull null]; - } - if ([stackModules objectForKey: fileName] == nil) - { - [stackModules setObject: module forKey: fileName]; - } - else - { - module = [stackModules objectForKey: fileName]; - } - } - } - [modLock unlock]; - - if (module == (id)[NSNull null]) - { - module = nil; - } - return module; -} - -static NSArray* -GSListModules() -{ - NSArray *result; - - GSLoadModule(nil); // initialise - [modLock lock]; - result = [stackModules allValues]; - [modLock unlock]; - return result; -} - -#endif /* STACKSYMBOLS */ +#ifdef HAVE_BACKTRACE @implementation GSStackTrace : NSObject - ++ (void)load +{ + GSStackTraceClass = self; +} + (GSStackTrace*) currentStack { return [[[GSStackTrace alloc] init] autorelease]; } - +- (void)finalize +{ + free(addresses); +} - (oneway void) dealloc { - DESTROY(frames); - [super dealloc]; + free(addresses); + RELEASE(addressArray); + RELEASE(symbols); + [super dealloc]; } - (NSString*) description { - NSMutableString *result = [NSMutableString string]; - int i; - int n; - - n = [frames count]; - for (i = 0; i < n; i++) - { - id line = [frames objectAtIndex: i]; - - [result appendFormat: @"%3d: %@\n", i, line]; - } - return result; + NSMutableString *trace = [NSMutableString string]; + NSEnumerator *e = [[self symbols] objectEnumerator]; + int i; + id obj; + while ((obj = [e nextObject])) + { + [trace appendFormat: @"%d: %@", i++, obj]; + } + return trace; } -- (NSEnumerator*) enumerator +- (NSArray*) symbols { - return [frames objectEnumerator]; + if (nil == symbols) + { + char** strs = backtrace_symbols(addresses, count); + NSString **symbolArray = alloca(count * sizeof(NSString*)); + int i; + for (i=0 ; i