使用NSSetUncaughtExceptionHandler函数捕获
#include <signal.h>
#include <execinfo.h>
void handleExceptions(NSException *exception) {
NSLog(@"*****************************************************************");
NSLog(@"exception 0000000000000 = %@",exception);
NSLog(@"*****************************************************************");
NSLog(@"callStackSymbols 11111111111111 = %@",[exception callStackSymbols]);
NSLog(@"*****************************************************************");
}
void signalHandler(int sig) {
//最好不要写,可能会打印太多内容
NSLog(@"*****************************************************************");
NSLog(@"signal 22222222222 = %d", sig);
NSLog(@"*****************************************************************");
}
- (void)initHandler {
struct sigaction newSignalAction;
memset(&newSignalAction, 0,sizeof(newSignalAction));
newSignalAction.sa_handler = &signalHandler;
sigaction(SIGABRT, &newSignalAction, NULL);
sigaction(SIGILL, &newSignalAction, NULL);
sigaction(SIGSEGV, &newSignalAction, NULL);
sigaction(SIGFPE, &newSignalAction, NULL);
sigaction(SIGBUS, &newSignalAction, NULL);
sigaction(SIGPIPE, &newSignalAction, NULL);
//异常时调用的函数
NSSetUncaughtExceptionHandler(&handleExceptions);
}
-
memset(&newSignalAction, 0,sizeof(newSignalAction));
给newSignalAction 结构体设置初始值 -
sigaction(SIGABRT, &newSignalAction, NULL);
sigaction(SIGILL, &newSignalAction, NULL);
sigaction(SIGSEGV, &newSignalAction, NULL);
sigaction(SIGFPE, &newSignalAction, NULL);
sigaction(SIGBUS, &newSignalAction, NULL);
sigaction(SIGPIPE, &newSignalAction, NULL);
标记需要捕获的crash类型
- 异常时调用的函数
NSSetUncaughtExceptionHandler(&handleExceptions);
NSSetUncaughtExceptionHandler 底层调用逻辑梳理
NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler *handler)
{
_NSUncaughtExceptionHandler = handler;
}
-
传入的 handler 被NSException类对象持有
-
接着callUncaughtHandler会调用_NSUncaughtExceptionHandler
static void
callUncaughtHandler(id value)
{
if (_NSUncaughtExceptionHandler != NULL)
{
(*_NSUncaughtExceptionHandler)(value);
}
_NSFoundationUncaughtExceptionHandler(value);
}
实现逻辑窥探
且NSException 初始化时也会注册调用对应的方法
+ (void) initialize
{
if (self == [NSException class])
{
#if defined(_NATIVE_OBJC_EXCEPTIONS)
# ifdef HAVE_SET_UNCAUGHT_EXCEPTION_HANDLER
objc_setUncaughtExceptionHandler(callUncaughtHandler);
# elif defined(HAVE_UNEXPECTED)
_objc_unexpected_exception = callUncaughtHandler;
# elif defined(HAVE_SET_UNEXPECTED)
objc_set_unexpected(callUncaughtHandler);
# endif
#endif
}
}
调用 [NSException raise: NSGenericException format: @"Terminate"];
NSException *obj;
NSMutableArray *testObjs = [[NSMutableArray alloc] init];
NSAutoreleasePool *arp = [NSAutoreleasePool new];
test_alloc_only(@"NSException");
obj = [NSException exceptionWithName: NSGenericException
reason: nil
userInfo: nil];
PASS((obj != nil), "can create an exception");
PASS(([[obj name] isEqualToString: NSGenericException]), "name works");
obj = [NSException exceptionWithName: NSGenericException
reason: nil
userInfo: nil];
[testObjs addObject: obj];
test_NSObject(@"NSException", testObjs);
NS_DURING
[MyClass testAbc];
NS_HANDLER
{
NSArray *addresses = [localException callStackReturnAddresses];
NSArray *a = [localException callStackSymbols];
NSEnumerator *e = [a objectEnumerator];
NSString *s = nil;
PASS([addresses count] > 0, "call stack addresses is not empty");
PASS([addresses count] == [a count], "addresses and symbols match");
NSLog(@"Got %@", a);
while ((s = [e nextObject]) != nil)
if ([s rangeOfString: @"testAbc"].length > 0)
break;
testHopeful = YES;
PASS(s != nil, "working callStackSymbols ... if this has failed it is probably due to a lack of support for objective-c method names (local symbols) in the backtrace_symbols() function of your libc. If so, you might lobby your operating system provider for a fix.");
testHopeful = NO;
}
NS_ENDHANDLER
PASS(NSGetUncaughtExceptionHandler() == 0, "default handler is null");
NSSetUncaughtExceptionHandler(handler);
PASS(NSGetUncaughtExceptionHandler() == handler, "setting handler works");
fprintf(stderr, "We expect a single FAIL without any explanation as\n"
"the test is terminated by an uncaught exception ...\n");
[NSException raise: NSGenericException format: @"Terminate"];
PASS(NO, "shouldn't get here ... exception should have terminated process");
[arp release]; arp = nil;
接着会来到调用:
-
(void) raise: (NSString)name
format: (NSString)format,... -
(void) raise: (NSString)name
format: (NSString)format
arguments: (va_list)argList
+ (void) raise: (NSString*)name
format: (NSString*)format,...
{
va_list args;
va_start(args, format);
[self raise: name format: format arguments: args];
// This probably doesn't matter, but va_end won't get called
va_end(args);
while (1); // does not return
}
+ (void) raise: (NSString*)name
format: (NSString*)format
arguments: (va_list)argList
{
NSString *reason;
NSException *except;
reason = [NSString stringWithFormat: format arguments: argList];
except = [self exceptionWithName: name reason: reason userInfo: nil];
[except raise];
while (1); // does not return
}
其中:[except raise]; 里面会调用 callUncaughtHandler(self);
- (void) raise
{
if (_reserved == 0)
{
_reserved = NSZoneCalloc([self zone], 2, sizeof(id));
}
if (nil == _e_stack)
{
// Only set the stack when first raised
_e_stack = [GSStackTrace new];
[_e_stack trace];
}
#if defined(_NATIVE_OBJC_EXCEPTIONS)
@throw self;
#else
{
NSThread *thread;
NSHandler *handler;
thread = GSCurrentThread();
handler = thread->_exception_handler;
if (NULL == handler)
{
static int recursion = 0;
/*
* Set/check a counter to prevent recursive uncaught exceptions.
* Allow a little recursion in case we have different handlers
* being tried.
*/
if (recursion++ > 3)
{
fprintf(stderr,
"recursion encountered handling uncaught exception\n");
fflush(stderr); /* NEEDED UNDER MINGW */
_terminate();
}
/*
* Call the uncaught exception handler (if there is one).
* The calls the built-in default handler to terminate the program!
*/
callUncaughtHandler(self);
}
else
{
thread->_exception_handler = handler->next;
handler->exception = self;
longjmp(handler->jumpState, 1);
}
}
#endif
while (1); // does not return
}
最后:callUncaughtHandler 会调用会上层函数的回调监听: NSSetUncaughtExceptionHandler(&handleExceptions);
那么问题来了:如何触发异常捕获调用呢?
查看NSObject 对象的底层实现,会有一些不合法的判断,在不合法的地方调用
+ (void) raise: (NSString*)name
format: (NSString*)format,...
以数组NSArray为例:
if (anObject == nil)
[NSException raise: NSInvalidArgumentException
format: @"Attempt to add nil to an array"];
- (NSArray*) arrayByAddingObject: (id)anObject
{
id na;
NSUInteger c = [self count];
if (anObject == nil)
[NSException raise: NSInvalidArgumentException
format: @"Attempt to add nil to an array"];
if (c == 0)
{
na = [[GSArrayClass allocWithZone: NSDefaultMallocZone()]
initWithObjects: &anObject count: 1];
}
else
{
GS_BEGINIDBUF(objects, c+1);
[self getObjects: objects];
objects[c] = anObject;
na = [[GSArrayClass allocWithZone: NSDefaultMallocZone()]
initWithObjects: objects count: c+1];
GS_ENDIDBUF();
}
return AUTORELEASE(na);
}
符号表的调用
获取符号表 由 GSStackTrace 类对象调用symbols 获得
- (NSArray*) symbols
{
if (nil == symbols && numReturns > FrameOffset)
{
NSInteger count = numReturns - FrameOffset;
NSUInteger i;
#if defined(USE_BFD)
void **ptrs = (void**)&returns[FrameOffset];
NSMutableArray *a;
a = [[NSMutableArray alloc] initWithCapacity: count];
for (i = 0; i < count; i++)
{
GSFunctionInfo *aFrame = nil;
void *address = (void*)*ptrs++;
void *base;
NSString *modulePath;
GSBinaryFileInfo *bfi;
modulePath = GSPrivateBaseAddress(address, &base);
if (modulePath != nil && (bfi = GSLoadModule(modulePath)) != nil)
{
aFrame = [bfi functionForAddress: (void*)(address - base)];
if (aFrame == nil)
{
/* We know we have the right module but function lookup
* failed ... perhaps we need to use the absolute
* address rather than offest by 'base' in this case.
*/
aFrame = [bfi functionForAddress: address];
}
}
else
{
NSArray *modules;
int j;
int m;
modules = GSListModules();
m = [modules count];
for (j = 0; j < m; j++)
{
bfi = [modules objectAtIndex: j];
if ((id)bfi != (id)[NSNull null])
{
aFrame = [bfi functionForAddress: address];
if (aFrame != nil)
{
break;
}
}
}
}
// not found (?!), add an 'unknown' function
if (aFrame == nil)
{
aFrame = [GSFunctionInfo alloc];
[aFrame initWithModule: nil
address: address
file: nil
function: nil
line: 0];
[aFrame autorelease];
}
[a addObject: [aFrame description]];
}
symbols = [a copy];
[a release];
#elif defined(_WIN32)
void **ptrs = (void**)&returns[FrameOffset];
SYMBOL_INFO *symbol;
NSString *syms[MAXFRAMES];
symbol = (SYMBOL_INFO *)calloc(sizeof(SYMBOL_INFO)
+ 1024 * sizeof(char), 1);
symbol->MaxNameLen = 1024;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
(void)pthread_mutex_lock(&traceLock);
for (i = 0; i < count; i++)
{
NSUInteger addr = (NSUInteger)*ptrs++;
if ((fromSym)(hProcess, (DWORD64)addr, 0, symbol))
{
syms[i] = [NSString stringWithFormat:
@"%s - %p", symbol->Name, addr];
}
else
{
syms[i] = [NSString stringWithFormat:
@"unknown - %p", symbol->Name, addr];
}
}
(void)pthread_mutex_unlock(&traceLock);
free(symbol);
symbols = [[NSArray alloc] initWithObjects: syms count: count];
#elif defined(HAVE_BACKTRACE)
void **ptrs = (void**)&returns[FrameOffset];
char **strs;
NSString **symbolArray;
strs = backtrace_symbols(ptrs, count);
symbolArray = alloca(count * sizeof(NSString*));
for (i = 0; i < count; i++)
{
symbolArray[i] = [NSString stringWithUTF8String: strs[i]];
}
symbols = [[NSArray alloc] initWithObjects: symbolArray count: count];
free(strs);
#elif defined(WITH_UNWIND)
void **ptrs = (void**)&returns[FrameOffset];
NSString **symbolArray;
symbolArray = alloca(count * sizeof(NSString*));
for (i = 0; i < count; i++)
{
const void *addr = ptrs[i];
Dl_info info;
if (dladdr(addr, &info)) {
const char *libname = "unknown";
if (info.dli_fname) {
// strip library path
char *delim = strrchr(info.dli_fname, '/');
libname = delim ? delim + 1 : info.dli_fname;
}
if (info.dli_sname) {
symbolArray[i] = [NSString stringWithFormat:
@"%lu: %p %s %s + %d", (unsigned long)i, addr, libname,
info.dli_sname, (int)(addr - info.dli_saddr)];
} else {
symbolArray[i] = [NSString stringWithFormat:
@"%lu: %p %s unknown", (unsigned long)i, addr, libname];
}
} else {
symbolArray[i] = [NSString stringWithFormat:
@"%lu: %p unknown", (unsigned long)i, addr];
}
}
symbols = [[NSArray alloc] initWithObjects: symbolArray count: count];
#else
NSMutableArray *a;
symbols = a = [[self addresses] mutableCopy];
for (i = 0; i < count; i++)
{
NSString *s;
s = [[NSString alloc] initWithFormat: @"%p: symbol not available",
[[a objectAtIndex: i] pointerValue]];
[a replaceObjectAtIndex: i withObject: s];
RELEASE(s);
}
#endif
}
return symbols;
}
网友评论