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