在iOS开发过程中,当主项目、及其集成的SDK都使用了NSSetUncaughtExceptionHandler崩溃日志收集时,多次设置ExceptionHandler会导致前面设置的NSUncaughtExceptionHandler失效。同样,signal也会被SDK截获,并使宿主无法获取到signal的返回值。
1. 关于ExceptionHandler冲突
解决方法如下:
static NSUncaughtExceptionHandler *_handler;
// 保存前一个处理异常的handler
_handler = NSGetUncaughtExceptionHandler();
// 设置自己的handler
NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
//当自己在UncaughtExceptionHandler方法中处理完后,将exception传给其他的handler
_handler(exception);
2. 关于SignalHandler冲突
解决方法如下:
typedef void (*SignalHandler)(int signo, siginfo_t *info, void *context);
static SignalHandler previousSignalHandler = NULL;
+ (void)installSignalHandler {
struct sigaction old_action;
sigaction(SIGABRT, NULL, &old_action);
if (old_action.sa_flags & SA_SIGINFO) {
previousSignalHandler = old_action.sa_sigaction;
}
LDAPMSignalRegister(SIGABRT);
// .......
}
static void LDAPMSignalRegister(int signal) {
struct sigaction action;
action.sa_sigaction = LDAPMSignalHandler;
action.sa_flags = SA_NODEFER | SA_SIGINFO;
sigemptyset(&action.sa_mask);
sigaction(signal, &action, 0);
}
static void LDAPMSignalHandler(int signal, siginfo_t* info, void* context) {
// 获取堆栈,收集堆栈
........
LDAPMClearSignalRigister();
// 处理前者注册的 handler
if (previousSignalHandler) {
previousSignalHandler(signal, info, context);
}
}
3. 将两个Handler的处理融合
将以上两个冲突的解决过程融合一下,得到以下的处理过程。其中对于崩溃栈信息的处理过程往上有很多,请自行查阅。
#import "CrashManager.h"
#import "execinfo.h"
static NSString *maSignal = @"signal";
static NSString *maCallStackSymbols = @"callStackSymbols";
static NSString *maSignalExceptionName = @"signalExceptionName";
static NSUncaughtExceptionHandler *_preCrashHandler = NULL;
typedef void SignalHandler(int signo, siginfo_t *info, void *context);
static void (*_preSignalHandlerArr[9])(int signo, siginfo_t *info, void *context);
static NSArray *_signalArr = nil;
@implementation MACrashManager
#pragma mark - Public functions
+ (void)registerCrashHandler {
_signalArr = @[@(SIGHUP), @(SIGINT), @(SIGQUIT), @(SIGABRT), @(SIGILL), @(SIGSEGV), @(SIGFPE), @(SIGBUS), @(SIGPIPE)];
[MACrashManager registerExceptionHandler];
[MACrashManager registerSignalHandlerArr];
}
+ (void)unregisterCrashHandler {
[MACrashManager unregisterCrashHandlerWithException:nil];
}
+ (void)resumeCrashHandler {
if (_preCrashHandler) {
NSSetUncaughtExceptionHandler(_preCrashHandler);
}
for (int index = 0; index<_signalArr.count; index++) {
SignalHandler *preHandler = _preSignalHandlerArr[index];
if (preHandler) {
int sigVal = [[_signalArr objectAtIndex:index] intValue];
MASignalHandlerRegister(sigVal, preHandler);
}
}
}
#pragma mark - Private functions
+ (void)registerExceptionHandler {
MAExceptionHandlerRegister();
}
+ (void)registerSignalHandlerArr {
for (int index = 0; index<_signalArr.count; index++) {
struct sigaction pre_action;
sigaction(SIGABRT, NULL, &pre_action);
if (pre_action.sa_flags & SA_SIGINFO) {
SignalHandler *preSignalHandler = pre_action.sa_sigaction;
_preSignalHandlerArr[index] = preSignalHandler;
}
int sigVal = [[_signalArr objectAtIndex:index] intValue];
MASignalHandlerRegister(sigVal, &MASignalHandler);
}
}
+ (void)unregisterCrashHandlerWithException:(NSException *)exception {
NSSetUncaughtExceptionHandler(NULL);
MAClearSignalRigister();
if (exception) {
if ([exception.name isEqualToString:maSignalExceptionName]) {
kill(getpid(), [exception.userInfo[maSignal] intValue]);
} else {
[exception raise];
}
}
}
+ (void)handleException:(NSException *)exception {
#ifdef DEBUG
[MACrashManager debugShowException:exception];
#else
[MACrashManager uploadException:exception];
#endif
[MACrashManager unregisterCrashHandlerWithException:exception];
}
+ (NSArray *)signalBackTraceSymbols {
void *callStack[128];// 堆栈方法数组
int frames = backtrace(callStack, 128);// 获取错误堆栈方法指针数组,返回数目
char **strs = backtrace_symbols(callStack, frames);// 符号化
NSMutableArray *backTraceSymbols = [NSMutableArray arrayWithCapacity:frames];
for (int i = 0; i < frames; i++) {
[backTraceSymbols addObject:[NSString stringWithUTF8String:strs[i]]];
}
free(strs);
return backTraceSymbols;
}
+ (void)uploadException:(NSException *)exception {
// .....
}
#pragma mark - C functions
void MAExceptionHandler(NSException *exception) {
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:exception.userInfo];
[userInfo setObject:exception.callStackSymbols forKey:maCallStackSymbols];
exception = [NSException exceptionWithName:exception.name reason:exception.reason userInfo:userInfo];
[MACrashManager handleException:exception];
_preCrashHandler(exception);
}
void MAExceptionHandlerRegister() {
_preCrashHandler = NSGetUncaughtExceptionHandler();
NSSetUncaughtExceptionHandler(&MAExceptionHandler);
}
void handleSignalStackSymbols(int signal) {
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:@(signal) forKey:maSignal];
NSArray *callStackSymbols = [MACrashManager signalBackTraceSymbols];
[userInfo setObject:callStackSymbols forKey:maCallStackSymbols];
NSString *reason = [NSString stringWithFormat:@"Signal %d was raised.", signal];
NSException *exception = [NSException exceptionWithName:maSignalExceptionName reason:reason userInfo:userInfo];
[MACrashManager handleException:exception];
}
void MASignalHandler(int signal, siginfo_t* info, void* context) {
handleSignalStackSymbols(signal);
MAClearSignalRigister();
for (int index = 0; index<_signalArr.count; index++) {
SignalHandler *preHandler = _preSignalHandlerArr[index];
if (preHandler) {
preHandler(signal, info, context);
}
}
}
void MASignalHandlerRegister(int signal, SignalHandler *sigHandler) {
struct sigaction action;
action.sa_sigaction = sigHandler;
action.sa_flags = SA_NODEFER | SA_SIGINFO;
sigemptyset(&action.sa_mask);
sigaction(signal, &action, 0);
}
void MAClearSignalRigister() {
for (int index = 0; index<_signalArr.count; index++) {
int sigVal = [[_signalArr objectAtIndex:index] intValue];
signal(sigVal, SIG_DFL);
}
}
#pragma mark - Debug functions
#ifdef DEBUG
+ (void)debugShowException:(NSException *)exception {
MAApp *app = [MAAppManager latestOpenedMiniApp];
MAQdasManager *qdasManager = app.qdasManager;
[qdasManager eventClassErrorWithStackTrace:exception.userInfo[maCallStackSymbols]];
NSString *message = [NSString stringWithFormat:@"\n程序崩溃,已自动复制错误信息到剪切板。\n\nname:%@\nreason:%@\ncall stack:%@\n", exception.name, exception.reason, exception.userInfo[maCallStackSymbols]];
[self debugShowMessage:message];
[UIPasteboard generalPasteboard].string = message;
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
while (YES) {
for (NSString *mode in (__bridge NSArray *)allModes) {
CFRunLoopRunInMode((CFStringRef)mode, 0.01, false);
}
}
// CFRelease(allModes);
}
+ (void)debugShowMessage:(NSString *)message {
void (^showbug)() = ^() {
UIWindow *window = [UIApplication sharedApplication].delegate.window;
UITextView *textView = [[UITextView alloc] initWithFrame:window.bounds];
textView.contentInset = UIEdgeInsetsMake(.0, 15.0, .0, 15.0);
textView.backgroundColor = [UIColor whiteColor];
textView.font = [UIFont systemFontOfSize:14.0];
textView.textColor = [UIColor blackColor];
textView.text = message;
textView.editable = NO;
[window addSubview:textView];
};
if ([NSThread isMainThread]) {
showbug();
return;
}
dispatch_async(dispatch_get_main_queue(), showbug);
}
#endif
@end
参考文章:
https://www.jianshu.com/p/29051908c74b
https://www.jianshu.com/p/953f0961157a
网友评论