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