ExceptionUtils.m
author Jens Alfke <jens@mooseyard.com>
Tue May 20 17:40:28 2008 -0700 (2008-05-20)
changeset 10 82a37ccf6b8c
child 11 e5976864dfe9
permissions -rw-r--r--
Split ExceptionUtils out of Test.
     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 #import <unistd.h>
    12 
    13 
    14 #ifndef Warn
    15 #define Warn NSLog
    16 #endif
    17 
    18 
    19 static void (*sExceptionReporter)(NSException*);
    20 
    21 void MYSetExceptionReporter( void (*reporter)(NSException*) )
    22 {
    23     sExceptionReporter = reporter;
    24 }
    25 
    26 void MYReportException( NSException *x, NSString *where, ... )
    27 {
    28     va_list args;
    29     va_start(args,where);
    30     where = [[NSString alloc] initWithFormat: where arguments: args];
    31     va_end(args);
    32     if( sExceptionReporter ) {
    33         Warn(@"Exception caught in %@:\n\t%@",where,x);
    34         sExceptionReporter(x);
    35     }else
    36         Warn(@"Exception caught in %@:\n\t%@\n%@",where,x,x.my_callStack);
    37     [where release];
    38 }
    39 
    40 
    41 @implementation NSException (MooseyardUtil)
    42 
    43 
    44 - (NSArray*) my_callStackReturnAddresses
    45 {
    46     // On 10.5 or later, can get the backtrace:
    47     if( [self respondsToSelector: @selector(callStackReturnAddresses)] )
    48         return [self valueForKey: @"callStackReturnAddresses"];
    49     else
    50         return nil;
    51 }
    52 
    53 - (NSArray*) my_callStackReturnAddressesSkipping: (unsigned)skip limit: (unsigned)limit
    54 {
    55     NSArray *addresses = [self my_callStackReturnAddresses];
    56     if( addresses ) {
    57         unsigned n = [addresses count];
    58         skip = MIN(skip,n);
    59         limit = MIN(limit,n-skip);
    60         addresses = [addresses subarrayWithRange: NSMakeRange(skip,limit)];
    61     }
    62     return addresses;
    63 }
    64 
    65 
    66 - (NSString*) my_callStack
    67 {
    68     NSArray *addresses = [self my_callStackReturnAddressesSkipping: 2 limit: 15];
    69     if (!addresses)
    70         return nil;
    71     
    72     // We pipe the hex return addresses through the 'atos' tool to get symbolic names:
    73     // Adapted from <http://paste.lisp.org/display/47196>:
    74     NSMutableString *cmd = [NSMutableString stringWithFormat: @"/usr/bin/atos -p %d", getpid()];
    75     NSValue *addr;
    76     foreach(addr,addresses) {
    77         [cmd appendFormat: @" %p", [addr pointerValue]];
    78     }
    79     FILE *file = popen( [cmd UTF8String], "r" );
    80     if( ! file )
    81         return nil;
    82     
    83     NSMutableData *output = [NSMutableData data];
    84     char buffer[512];
    85     size_t length;
    86     while ((length = fread( buffer, 1, sizeof( buffer ), file ) ))
    87         [output appendBytes: buffer length: length];
    88     pclose( file );
    89     NSString *outStr = [[[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding] autorelease];
    90     
    91     NSMutableString *result = [NSMutableString string];
    92     NSString *line;
    93     foreach( line, [outStr componentsSeparatedByString: @"\n"] ) {
    94         // Skip  frames that are part of the exception/assertion handling itself:
    95         if( [line hasPrefix: @"-[NSAssertionHandler"] || [line hasPrefix: @"+[NSException"] 
    96                 || [line hasPrefix: @"-[NSException"] || [line hasPrefix: @"_AssertFailed"] )
    97             continue;
    98         if( result.length )
    99             [result appendString: @"\n"];
   100         [result appendString: @"\t"];
   101         [result appendString: line];
   102         // Don't show the "__start" frame below "main":
   103         if( [line hasPrefix: @"main "] )
   104             break;
   105     }
   106     return result;
   107 }
   108 
   109 @end
   110 
   111 
   112 
   113 
   114 @implementation MYExceptionReportingApplication
   115 
   116 
   117 static void report( NSException *x ) {
   118     [NSApp reportException: x];
   119 }
   120 
   121 - (id) init
   122 {
   123     self = [super init];
   124     if (self != nil) {
   125         MYSetExceptionReporter(&report);
   126     }
   127     return self;
   128 }
   129 
   130 
   131 - (void)reportException:(NSException *)x
   132 {
   133     [super reportException: x];
   134     [self performSelector: @selector(_showExceptionAlert:) withObject: x afterDelay: 0.0];
   135     MYSetExceptionReporter(NULL);     // ignore further exceptions till alert is dismissed
   136 }
   137 
   138 - (void) _showExceptionAlert: (NSException*)x
   139 {
   140     NSString *stack = [x my_callStack] ?:@"";
   141     int r = NSRunCriticalAlertPanel( @"Cloudy Internal Error!",
   142                             [NSString stringWithFormat: @"Uncaught exception: %@\n%@\n\n%@\n\n"
   143                              "Please report this bug to jens@mooseyard.com (you can copy & paste the text).",
   144                              [x name], [x reason], stack],
   145                             @"Sorry",@"Quit",nil);
   146     if( r == NSAlertAlternateReturn )
   147         exit(1);
   148     MYSetExceptionReporter(&report);
   149 }
   150 
   151 @end
   152 
   153 
   154 /*
   155  Copyright (c) 2008, Jens Alfke. All rights reserved.
   156  
   157  Redistribution and use in source and binary forms, with or without modification, are permitted
   158  provided that the following conditions are met:
   159  
   160  * Redistributions of source code must retain the above copyright notice, this list of conditions
   161  and the following disclaimer.
   162  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   163  and the following disclaimer in the documentation and/or other materials provided with the
   164  distribution.
   165  
   166  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   167  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   168  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   169  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   170  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   171   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   172  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   173  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   174  */