/** NSException - Object encapsulation of a general exception handler Copyright (C) 1993, 1994, 1996, 1997, 1999 Free Software Foundation, Inc. Written by: Adam Fedor Date: Mar 1995 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111 USA. $Date: 2007-09-14 07:36:11 -0400 (Fri, 14 Sep 2007) $ $Revision: 25482 $ NOTE: This code was taken from GNUstep Base Library and sligthly modified by Ludovic Marcotte */ #include "NSException+Stacktrace.h" #include #include #include #include #include #include // // // static void _terminate() { abort(); } static void _NSFoundationUncaughtExceptionHandler (NSException *exception) { NSString *stack; fprintf(stderr, "Uncaught exception %s, reason: %s\n", [[exception name] lossyCString], [[exception reason] lossyCString]); fflush(stderr); /* NEEDED UNDER MINGW */ stack = [[[exception userInfo] objectForKey: @"GSStackTraceKey"] description]; if (stack != nil) { fprintf(stderr, "Stack\n%s\n", [stack lossyCString]); } fflush(stderr); /* NEEDED UNDER MINGW */ _terminate(); } // // // #define STACKSYMBOLS 1 @interface GSStackTrace : NSObject { NSMutableArray *frames; } + (GSStackTrace*) currentStack; - (NSString*) description; - (NSEnumerator*) enumerator; - (id) frameAt: (unsigned)index; - (unsigned) frameCount; - (NSEnumerator*) reverseEnumerator; @end #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: (int)lineNo; - (int) 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 { [_module release]; _module = nil; [_fileName release]; _fileName = nil; [_functionName release]; _functionName = nil; [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: (int)lineNo { _module = [module retain]; _address = address; _fileName = [file retain]; _functionName = [function retain]; _lineNo = lineNo; return self; } - (int) 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 { [_fileName release]; _fileName = nil; 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; } #endif /* STACKSYMBOLS */ // // // @implementation NSException (SOGoExtensions) - (void) raise { #ifndef _NATIVE_OBJC_EXCEPTIONS NSThread *thread; NSHandler *handler; #endif if ([_e_info objectForKey: @"GSStackTraceKey"] == nil) { NSMutableDictionary *m; if (_e_info == nil) { _e_info = m = [NSMutableDictionary new]; } else if ([_e_info isKindOfClass: [NSMutableDictionary class]] == YES) { m = (NSMutableDictionary*)_e_info; } else { m = [_e_info mutableCopy]; RELEASE(_e_info); _e_info = m; } [m setObject: [GSStackTrace currentStack] forKey: @"GSStackTraceKey"]; } #ifdef _NATIVE_OBJC_EXCEPTIONS @throw self; #else thread = GSCurrentThread(); handler = thread->_exception_handler; if (handler == NULL) { static int recursion = 0; /* * Set/check a counter to prevent recursive uncaught exceptions. * Allow a little recursion in case we have different handlers * being tried. */ if (recursion++ > 3) { fprintf(stderr, "recursion encountered handling uncaught exception\n"); fflush(stderr); /* NEEDED UNDER MINGW */ _terminate(); } /* * Call the uncaught exception handler (if there is one). */ if (_NSUncaughtExceptionHandler != NULL) { (*_NSUncaughtExceptionHandler)(self); } /* * The uncaught exception handler which is set has not * exited, so we call the builtin handler, (undocumented * behavior of MacOS-X). * The standard handler is guaranteed to exit/abort. */ _NSFoundationUncaughtExceptionHandler(self); } thread->_exception_handler = handler->next; handler->exception = self; longjmp(handler->jumpState, 1); #endif } @end