jens@10: // jens@10: // ExceptionUtils.m jens@10: // MYUtilities jens@10: // jens@10: // Created by Jens Alfke on 1/5/08. jens@10: // Copyright 2008 Jens Alfke. All rights reserved. jens@10: // See BSD license at bottom of file. jens@10: // jens@10: jens@10: #import "ExceptionUtils.h" jens@11: jens@11: #import "Logging.h" jens@11: #import "Test.h" jens@11: jens@11: #include jens@11: #include jens@10: jens@10: jens@10: #ifndef Warn jens@10: #define Warn NSLog jens@10: #endif jens@10: jens@10: jens@10: static void (*sExceptionReporter)(NSException*); jens@10: jens@10: void MYSetExceptionReporter( void (*reporter)(NSException*) ) jens@10: { jens@10: sExceptionReporter = reporter; jens@10: } jens@10: jens@10: void MYReportException( NSException *x, NSString *where, ... ) jens@10: { jens@10: va_list args; jens@10: va_start(args,where); jens@10: where = [[NSString alloc] initWithFormat: where arguments: args]; jens@10: va_end(args); jens@10: if( sExceptionReporter ) { jens@10: Warn(@"Exception caught in %@:\n\t%@",where,x); jens@10: sExceptionReporter(x); jens@10: }else jens@10: Warn(@"Exception caught in %@:\n\t%@\n%@",where,x,x.my_callStack); jens@10: [where release]; jens@10: } jens@10: jens@10: jens@10: @implementation NSException (MooseyardUtil) jens@10: jens@10: jens@10: - (NSArray*) my_callStackReturnAddresses jens@10: { jens@10: // On 10.5 or later, can get the backtrace: jens@10: if( [self respondsToSelector: @selector(callStackReturnAddresses)] ) jens@10: return [self valueForKey: @"callStackReturnAddresses"]; jens@10: else jens@10: return nil; jens@10: } jens@10: jens@10: - (NSArray*) my_callStackReturnAddressesSkipping: (unsigned)skip limit: (unsigned)limit jens@10: { jens@10: NSArray *addresses = [self my_callStackReturnAddresses]; jens@10: if( addresses ) { jens@10: unsigned n = [addresses count]; jens@10: skip = MIN(skip,n); jens@10: limit = MIN(limit,n-skip); jens@10: addresses = [addresses subarrayWithRange: NSMakeRange(skip,limit)]; jens@10: } jens@10: return addresses; jens@10: } jens@10: jens@10: jens@10: - (NSString*) my_callStack jens@10: { jens@10: NSArray *addresses = [self my_callStackReturnAddressesSkipping: 2 limit: 15]; jens@10: if (!addresses) jens@10: return nil; jens@10: jens@10: // We pipe the hex return addresses through the 'atos' tool to get symbolic names: jens@10: // Adapted from : jens@10: NSMutableString *cmd = [NSMutableString stringWithFormat: @"/usr/bin/atos -p %d", getpid()]; jens@10: NSValue *addr; jens@10: foreach(addr,addresses) { jens@10: [cmd appendFormat: @" %p", [addr pointerValue]]; jens@10: } jens@10: FILE *file = popen( [cmd UTF8String], "r" ); jens@10: if( ! file ) jens@10: return nil; jens@10: jens@10: NSMutableData *output = [NSMutableData data]; jens@10: char buffer[512]; jens@10: size_t length; jens@10: while ((length = fread( buffer, 1, sizeof( buffer ), file ) )) jens@10: [output appendBytes: buffer length: length]; jens@10: pclose( file ); jens@10: NSString *outStr = [[[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding] autorelease]; jens@10: jens@10: NSMutableString *result = [NSMutableString string]; jens@10: NSString *line; jens@10: foreach( line, [outStr componentsSeparatedByString: @"\n"] ) { jens@10: // Skip frames that are part of the exception/assertion handling itself: jens@10: if( [line hasPrefix: @"-[NSAssertionHandler"] || [line hasPrefix: @"+[NSException"] jens@10: || [line hasPrefix: @"-[NSException"] || [line hasPrefix: @"_AssertFailed"] ) jens@10: continue; jens@10: if( result.length ) jens@10: [result appendString: @"\n"]; jens@10: [result appendString: @"\t"]; jens@10: [result appendString: line]; jens@10: // Don't show the "__start" frame below "main": jens@10: if( [line hasPrefix: @"main "] ) jens@10: break; jens@10: } jens@10: return result; jens@10: } jens@10: jens@10: @end jens@10: jens@10: jens@10: jens@10: jens@11: #ifdef NSAppKitVersionNumber10_4 // only enable this in a project that uses AppKit jens@11: jens@10: @implementation MYExceptionReportingApplication jens@10: jens@10: jens@10: static void report( NSException *x ) { jens@10: [NSApp reportException: x]; jens@10: } jens@10: jens@10: - (id) init jens@10: { jens@10: self = [super init]; jens@10: if (self != nil) { jens@10: MYSetExceptionReporter(&report); jens@10: } jens@10: return self; jens@10: } jens@10: jens@10: jens@10: - (void)reportException:(NSException *)x jens@10: { jens@10: [super reportException: x]; jens@10: [self performSelector: @selector(_showExceptionAlert:) withObject: x afterDelay: 0.0]; jens@10: MYSetExceptionReporter(NULL); // ignore further exceptions till alert is dismissed jens@10: } jens@10: jens@10: - (void) _showExceptionAlert: (NSException*)x jens@10: { jens@10: NSString *stack = [x my_callStack] ?:@""; jens@11: int r = NSRunCriticalAlertPanel( @"Internal Error!", jens@10: [NSString stringWithFormat: @"Uncaught exception: %@\n%@\n\n%@\n\n" jens@11: "Please report this bug (you can copy & paste the text).", jens@10: [x name], [x reason], stack], jens@11: @"Continue",@"Quit",nil); jens@10: if( r == NSAlertAlternateReturn ) jens@10: exit(1); jens@10: MYSetExceptionReporter(&report); jens@10: } jens@10: jens@10: @end jens@10: jens@11: #endif jens@11: jens@11: jens@11: jens@11: BOOL IsGDBAttached( void ) jens@11: { jens@11: // From: jens@11: int mib[4]; jens@11: size_t bufSize = 0; jens@11: int local_error = 0; jens@11: struct kinfo_proc kp; jens@11: jens@11: mib[0] = CTL_KERN; jens@11: mib[1] = KERN_PROC; jens@11: mib[2] = KERN_PROC_PID; jens@11: mib[3] = getpid(); jens@11: jens@11: bufSize = sizeof (kp); jens@11: if ((local_error = sysctl(mib, 4, &kp, &bufSize, NULL, 0)) < 0) { jens@11: Warn(@"Failure calling sysctl"); jens@11: return NO; jens@11: } jens@11: return (kp.kp_proc.p_flag & P_TRACED) != 0; jens@11: } jens@11: jens@11: jens@10: jens@10: /* jens@11: Copyright (c) 2008, Jens Alfke . All rights reserved. jens@10: jens@10: Redistribution and use in source and binary forms, with or without modification, are permitted jens@10: provided that the following conditions are met: jens@10: jens@10: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@10: and the following disclaimer. jens@10: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions jens@10: and the following disclaimer in the documentation and/or other materials provided with the jens@10: distribution. jens@10: jens@10: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@10: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@10: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@10: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@10: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@10: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@10: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@10: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@10: */