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