ExceptionUtils.m
author Jens Alfke <jens@mooseyard.com>
Wed Sep 02 08:41:25 2009 -0700 (2009-09-02)
changeset 35 5cab3034d3a1
parent 23 a910102a1c9d
permissions -rw-r--r--
10.6 compatibility: Fix some new compiler warnings, and work around apparent regressions in NSTask and -stringByStandardizingPath.
     1 //
     2 //  ExceptionUtils.m
     3 //  MYUtilities
     4 //
     5 //  Created by Jens Alfke on 1/5/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //  See BSD license at bottom of file.
     8 //
     9 
    10 #import "ExceptionUtils.h"
    11 
    12 #import "Logging.h"
    13 #import "Test.h"
    14 
    15 #include <sys/sysctl.h>
    16 #include <unistd.h>
    17 
    18 
    19 #ifndef Warn
    20 #define Warn NSLog
    21 #endif
    22 
    23 
    24 static void (*sExceptionReporter)(NSException*);
    25 
    26 void MYSetExceptionReporter( void (*reporter)(NSException*) )
    27 {
    28     sExceptionReporter = reporter;
    29 }
    30 
    31 void MYReportException( NSException *x, NSString *where, ... )
    32 {
    33     va_list args;
    34     va_start(args,where);
    35     where = [[NSString alloc] initWithFormat: where arguments: args];
    36     va_end(args);
    37     Warn(@"Exception caught in %@:\n\t%@\n%@",where,x,x.my_callStack);
    38     if( sExceptionReporter )
    39         sExceptionReporter(x);
    40     [where release];
    41 }
    42 
    43 
    44 @implementation NSException (MYUtilities)
    45 
    46 
    47 - (NSArray*) my_callStackReturnAddresses
    48 {
    49     // On 10.5 or later, can get the backtrace:
    50     if( [self respondsToSelector: @selector(callStackReturnAddresses)] )
    51         return [self valueForKey: @"callStackReturnAddresses"];
    52     else
    53         return nil;
    54 }
    55 
    56 - (NSArray*) my_callStackReturnAddressesSkipping: (unsigned)skip limit: (unsigned)limit
    57 {
    58     NSArray *addresses = [self my_callStackReturnAddresses];
    59     if( addresses ) {
    60         unsigned n = [addresses count];
    61         skip = MIN(skip,n);
    62         limit = MIN(limit,n-skip);
    63         addresses = [addresses subarrayWithRange: NSMakeRange(skip,limit)];
    64     }
    65     return addresses;
    66 }
    67 
    68 
    69 - (NSString*) my_callStack
    70 {
    71     NSArray *addresses = [self my_callStackReturnAddressesSkipping: 1 limit: 15];
    72     if (!addresses)
    73         return nil;
    74     
    75     FILE *file = NULL;
    76 #if !TARGET_OS_IPHONE
    77     // We pipe the hex return addresses through the 'atos' tool to get symbolic names:
    78     // Adapted from <http://paste.lisp.org/display/47196>:
    79     NSMutableString *cmd = [NSMutableString stringWithFormat: @"/usr/bin/atos -p %d", getpid()];
    80     NSValue *addr;
    81     foreach(addr,addresses) {
    82         [cmd appendFormat: @" %p", [addr pointerValue]];
    83     }
    84     file = popen( [cmd UTF8String], "r" );
    85 #endif
    86     
    87     NSMutableString *result = [NSMutableString string];
    88     if( file ) {
    89         NSMutableData *output = [NSMutableData data];
    90         char buffer[512];
    91         size_t length;
    92         while ((length = fread( buffer, 1, sizeof( buffer ), file ) ))
    93             [output appendBytes: buffer length: length];
    94         pclose( file );
    95         NSString *outStr = [[[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding] autorelease];
    96         
    97         NSString *line;
    98         foreach( line, [outStr componentsSeparatedByString: @"\n"] ) {
    99             // Skip  frames that are part of the exception/assertion handling itself:
   100             if( [line hasPrefix: @"-[NSAssertionHandler"] || [line hasPrefix: @"+[NSException"] 
   101                     || [line hasPrefix: @"-[NSException"] || [line hasPrefix: @"_AssertFailed"]
   102                     || [line hasPrefix: @"objc_"] )
   103                 continue;
   104             if( result.length )
   105                 [result appendString: @"\n"];
   106             [result appendString: @"\t"];
   107             [result appendString: line];
   108             // Don't show the "__start" frame below "main":
   109             if( [line hasPrefix: @"main "] )
   110                 break;
   111         }
   112     } else {
   113         NSValue *addr;
   114         foreach(addr,addresses) {
   115             if( result.length )
   116                 [result appendString: @" <- "];
   117             [result appendFormat: @"%p", [addr pointerValue]];
   118         }
   119     }
   120     return result;
   121 }
   122 
   123 @end
   124 
   125 
   126 
   127 
   128 #ifdef NSAppKitVersionNumber10_4 // only enable this in a project that uses AppKit
   129 
   130 @implementation MYExceptionReportingApplication
   131 
   132 
   133 static void report( NSException *x ) {
   134     [NSApp reportException: x];
   135 }
   136 
   137 - (id) init
   138 {
   139     self = [super init];
   140     if (self != nil) {
   141         MYSetExceptionReporter(&report);
   142     }
   143     return self;
   144 }
   145 
   146 
   147 - (void)reportException:(NSException *)x
   148 {
   149     [super reportException: x];
   150     [self performSelector: @selector(_showExceptionAlert:) withObject: x afterDelay: 0.0];
   151     MYSetExceptionReporter(NULL);     // ignore further exceptions till alert is dismissed
   152 }
   153 
   154 - (void) _showExceptionAlert: (NSException*)x
   155 {
   156     NSString *stack = [x my_callStack] ?:@"";
   157     int r = NSRunCriticalAlertPanel( @"Internal Error!",
   158                             [NSString stringWithFormat: @"Uncaught exception: %@\n%@\n\n%@\n\n"
   159                              "Please report this bug (you can copy & paste the text).",
   160                              [x name], [x reason], stack],
   161                             @"Continue",@"Quit",nil);
   162     if( r == NSAlertAlternateReturn )
   163         exit(1);
   164     MYSetExceptionReporter(&report);
   165 }
   166 
   167 @end
   168 
   169 #endif
   170 
   171 
   172 
   173 BOOL IsGDBAttached( void )
   174 {
   175     // From: <http://lists.apple.com/archives/Xcode-users/2004/Feb/msg00241.html>
   176     int mib[4];
   177     size_t bufSize = 0;
   178     struct kinfo_proc kp;
   179     
   180     mib[0] = CTL_KERN;
   181     mib[1] = KERN_PROC;
   182     mib[2] = KERN_PROC_PID;
   183     mib[3] = getpid();
   184     
   185     bufSize = sizeof (kp);
   186     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
   187         Warn(@"Error %i calling sysctl",errno);
   188         return NO;
   189     }
   190     return (kp.kp_proc.p_flag & P_TRACED) != 0;
   191 }
   192 
   193 
   194 
   195 /*
   196  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   197  
   198  Redistribution and use in source and binary forms, with or without modification, are permitted
   199  provided that the following conditions are met:
   200  
   201  * Redistributions of source code must retain the above copyright notice, this list of conditions
   202  and the following disclaimer.
   203  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   204  and the following disclaimer in the documentation and/or other materials provided with the
   205  distribution.
   206  
   207  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   208  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   209  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   210  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   211  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   212   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   213  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   214  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   215  */