ExceptionUtils.m
author Jens Alfke <jens@mooseyard.com>
Sun Jul 13 10:45:42 2008 -0700 (2008-07-13)
changeset 16 ce9c83f7ec14
parent 11 e5976864dfe9
child 23 a910102a1c9d
permissions -rw-r--r--
* Minor fixes to glitches detected by the clang static analyzer.
* Added MYAnimatingSplitView class.
     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 (MooseyardUtil)
    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: 2 limit: 15];
    74     if (!addresses)
    75         return nil;
    76     
    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 *file = popen( [cmd UTF8String], "r" );
    85     if( ! file )
    86         return nil;
    87     
    88     NSMutableData *output = [NSMutableData data];
    89     char buffer[512];
    90     size_t length;
    91     while ((length = fread( buffer, 1, sizeof( buffer ), file ) ))
    92         [output appendBytes: buffer length: length];
    93     pclose( file );
    94     NSString *outStr = [[[NSString alloc] initWithData: output encoding: NSUTF8StringEncoding] autorelease];
    95     
    96     NSMutableString *result = [NSMutableString string];
    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             continue;
   103         if( result.length )
   104             [result appendString: @"\n"];
   105         [result appendString: @"\t"];
   106         [result appendString: line];
   107         // Don't show the "__start" frame below "main":
   108         if( [line hasPrefix: @"main "] )
   109             break;
   110     }
   111     return result;
   112 }
   113 
   114 @end
   115 
   116 
   117 
   118 
   119 #ifdef NSAppKitVersionNumber10_4 // only enable this in a project that uses AppKit
   120 
   121 @implementation MYExceptionReportingApplication
   122 
   123 
   124 static void report( NSException *x ) {
   125     [NSApp reportException: x];
   126 }
   127 
   128 - (id) init
   129 {
   130     self = [super init];
   131     if (self != nil) {
   132         MYSetExceptionReporter(&report);
   133     }
   134     return self;
   135 }
   136 
   137 
   138 - (void)reportException:(NSException *)x
   139 {
   140     [super reportException: x];
   141     [self performSelector: @selector(_showExceptionAlert:) withObject: x afterDelay: 0.0];
   142     MYSetExceptionReporter(NULL);     // ignore further exceptions till alert is dismissed
   143 }
   144 
   145 - (void) _showExceptionAlert: (NSException*)x
   146 {
   147     NSString *stack = [x my_callStack] ?:@"";
   148     int r = NSRunCriticalAlertPanel( @"Internal Error!",
   149                             [NSString stringWithFormat: @"Uncaught exception: %@\n%@\n\n%@\n\n"
   150                              "Please report this bug (you can copy & paste the text).",
   151                              [x name], [x reason], stack],
   152                             @"Continue",@"Quit",nil);
   153     if( r == NSAlertAlternateReturn )
   154         exit(1);
   155     MYSetExceptionReporter(&report);
   156 }
   157 
   158 @end
   159 
   160 #endif
   161 
   162 
   163 
   164 BOOL IsGDBAttached( void )
   165 {
   166     // From: <http://lists.apple.com/archives/Xcode-users/2004/Feb/msg00241.html>
   167     int mib[4];
   168     size_t bufSize = 0;
   169     struct kinfo_proc kp;
   170     
   171     mib[0] = CTL_KERN;
   172     mib[1] = KERN_PROC;
   173     mib[2] = KERN_PROC_PID;
   174     mib[3] = getpid();
   175     
   176     bufSize = sizeof (kp);
   177     if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
   178         Warn(@"Error %i calling sysctl",errno);
   179         return NO;
   180     }
   181     return (kp.kp_proc.p_flag & P_TRACED) != 0;
   182 }
   183 
   184 
   185 
   186 /*
   187  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   188  
   189  Redistribution and use in source and binary forms, with or without modification, are permitted
   190  provided that the following conditions are met:
   191  
   192  * Redistributions of source code must retain the above copyright notice, this list of conditions
   193  and the following disclaimer.
   194  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   195  and the following disclaimer in the documentation and/or other materials provided with the
   196  distribution.
   197  
   198  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   199  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   200  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   201  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   202  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   203   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   204  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   205  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   206  */