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