iOS 异常捕获学习
头文件
#import <UIKit/UIKit.h>
@interface CrashCatch : NSObject
+ (void)installUncaughtExceptionHandler;
+ (void)crashTest:(NSUInteger)index;
@end
实现文件
#import "CrashCatch.h"
#include <libkern/osatomic.h>
#include <execinfo.h>
NSString * const UEHSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
NSString * const UEHSignalKey = @"UncaughtExceptionHandlerSignalKey";
NSString * const UEHAddressesKey = @"UncaughtExceptionHandlerAddressesKey";
NSString * const UEHFileKey = @"UncaughtExceptionHandlerFileKey";
volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum = 10;
const NSInteger UncaughtExceptionHandlerSkipAddressCount = 4;
const NSInteger UncaughtExceptionHandlerReportAddressCount = 5;
void UncaughtExceptionHandler(NSException *exception);
void MySignalHandler(int signal);
static NSUncaughtExceptionHandler* g_previousUncaughtExceptionHandler;
@implementation CrashCatch
+ (void)installUncaughtExceptionHandler
{
g_previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();
NSSetUncaughtExceptionHandler (&UncaughtExceptionHandler);
signal(SIGABRT, MySignalHandler);
signal(SIGILL, MySignalHandler);
signal(SIGSEGV, MySignalHandler);
signal(SIGFPE, MySignalHandler);
signal(SIGBUS, MySignalHandler);
signal(SIGPIPE, MySignalHandler);
}
- (void)handleException:(NSException *)exception
{
NSDictionary *userInfo=[exception userInfo];
[self save:exception to:[userInfo objectForKey:UEHFileKey]];
if (g_previousUncaughtExceptionHandler != NULL) {
g_previousUncaughtExceptionHandler(exception);
}
signal(SIGABRT, SIG_DFL);
signal(SIGILL, SIG_DFL);
signal(SIGSEGV, SIG_DFL);
signal(SIGFPE, SIG_DFL);
signal(SIGBUS, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
if ([[exception name] isEqual:UEHSignalExceptionName]) {
kill(getpid(), [[[exception userInfo] objectForKey:UEHSignalKey] intValue]);
}
else {
[exception raise];
}
}
+ (NSArray *)backtrace {
void *callstack[128];
int frames = backtrace(callstack, 128);
char **strs = backtrace_symbols(callstack, frames);
int i;
NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
for (i = UncaughtExceptionHandlerSkipAddressCount;
i < UncaughtExceptionHandlerSkipAddressCount+UncaughtExceptionHandlerReportAddressCount;
i++)
{
[backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return backtrace;
}
+ (NSString *)getAppInfo
{
NSString *appInfo = [NSString stringWithFormat:
@"----------------------------------------\n"
@"Application : %@ %@(%@)\n"
@"Device Model : %@\n"
@"OS Version : %@ %@\n"
@"----------------------------------------\n",
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"],
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"],
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
[UIDevice currentDevice].model,
[UIDevice currentDevice].systemName,
[UIDevice currentDevice].systemVersion];
NSLog(@"AppInfo: %@", appInfo);
return appInfo;
}
- (void)save:(NSException *)exception to:(NSString *)file
{
NSArray *stackArray = exception.userInfo[UEHAddressesKey];
NSString *reason = [exception reason];
NSString *name = [exception name];
NSString *exceptionInfo = [NSString stringWithFormat:
@"----------------------------------------\n"
@"Exception Reason : %@\n"
@"Exception Name : %@\n"
@"Exception Stack : \n%@\n"
@"----------------------------------------\n",
reason, name, stackArray];
NSLog(@"%@", exceptionInfo);
NSLog(@">>>%@", [CrashCatch backtrace]);
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) [0] stringByAppendingPathComponent:file];
if (![[NSFileManager defaultManager] fileExistsAtPath:path]){
[[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
}
NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970];
NSString *savePath = [path stringByAppendingFormat:@"/error_%lld.log", (int64_t)timeInterval];
NSError *error = nil;
BOOL didWrite = [exceptionInfo writeToFile:savePath
atomically:YES
encoding:NSUTF8StringEncoding
error:&error];
if (didWrite) {
NSLog(@"Save sucess:%@", savePath);
}else {
NSLog(@"Save failed:%@", error);
}
NSMutableString *mailUrl = [NSMutableString string];
[mailUrl appendString:@"mailto:example@gmail.com"];
[mailUrl appendString:@"?subject=Report Crash"];
[mailUrl appendFormat:@"&body=%@", exceptionInfo];
NSString *mailPath = [mailUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:mailPath]];
}
+ (void)crashTest:(NSUInteger)index {
if (index == 0) {
[self performSelector:@selector(thisMthodDoesNotExist) withObject:nil];
}else if (index == 1) {
[[NSMutableDictionary dictionary] setObject:nil forKey:@"nil"];
}else if (index == 2) {
[[NSArray array] objectAtIndex:1];
}else if (index == 3) {
[[[CrashCatch alloc] init] performSelector:@selector(otherCrash) withObject:nil];
}
}
- (void)otherCrash {
}
@end
void UncaughtExceptionHandler (NSException *exception)
{
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
if (exceptionCount > UncaughtExceptionMaximum) { return; }
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
NSArray *callStack = exception.callStackSymbols;
[userInfo setObject:callStack forKey:UEHAddressesKey];
[userInfo setObject:@"ObjCCrash" forKey:UEHFileKey];
NSException *exp = [NSException exceptionWithName:[exception name]
reason:[exception reason]
userInfo:userInfo];
[[[CrashCatch alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject:exp waitUntilDone:YES];
}
void MySignalHandler(int signal)
{
int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
if (exceptionCount > UncaughtExceptionMaximum) { return; }
NSMutableDictionary *userInfo =
[NSMutableDictionary dictionaryWithDictionary:@{UEHSignalKey : @(signal)}];
NSArray *callStack = [CrashCatch backtrace];
[userInfo setObject:callStack forKey:UEHAddressesKey];
[userInfo setObject:@"SignalCrash" forKey:UEHFileKey];
NSString *reson = [NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.\n %@", nil),
signal, [CrashCatch getAppInfo]];
NSException *exp = [NSException exceptionWithName:UEHSignalExceptionName
reason:reson
userInfo:userInfo];
[[[CrashCatch alloc] init] performSelectorOnMainThread:@selector(handleException:) withObject:exp waitUntilDone:YES];
}