ExceptionUtils.m
author Jens Alfke <jens@mooseyard.com>
Sat May 24 13:24:33 2008 -0700 (2008-05-24)
changeset 11 e5976864dfe9
parent 10 82a37ccf6b8c
child 16 ce9c83f7ec14
permissions -rw-r--r--
* Added BSD license to more .m files.
* Updated some copyright notices.
* Added some #imports to make the MYNetwork classes build without using the MYUtilities.pch prefix header.
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
    int local_error = 0;
jens@11
   170
    struct kinfo_proc kp;
jens@11
   171
    
jens@11
   172
    mib[0] = CTL_KERN;
jens@11
   173
    mib[1] = KERN_PROC;
jens@11
   174
    mib[2] = KERN_PROC_PID;
jens@11
   175
    mib[3] = getpid();
jens@11
   176
    
jens@11
   177
    bufSize = sizeof (kp);
jens@11
   178
    if ((local_error = sysctl(mib, 4, &kp, &bufSize, NULL, 0)) < 0) {
jens@11
   179
        Warn(@"Failure calling sysctl");
jens@11
   180
        return NO;
jens@11
   181
    }
jens@11
   182
    return (kp.kp_proc.p_flag & P_TRACED) != 0;
jens@11
   183
}
jens@11
   184
jens@11
   185
jens@10
   186
jens@10
   187
/*
jens@11
   188
 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
jens@10
   189
 
jens@10
   190
 Redistribution and use in source and binary forms, with or without modification, are permitted
jens@10
   191
 provided that the following conditions are met:
jens@10
   192
 
jens@10
   193
 * Redistributions of source code must retain the above copyright notice, this list of conditions
jens@10
   194
 and the following disclaimer.
jens@10
   195
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
jens@10
   196
 and the following disclaimer in the documentation and/or other materials provided with the
jens@10
   197
 distribution.
jens@10
   198
 
jens@10
   199
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
jens@10
   200
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
jens@10
   201
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
jens@10
   202
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
jens@10
   203
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
jens@10
   204
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
jens@10
   205
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
jens@10
   206
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
jens@10
   207
 */