美文网首页
ExceptionHandler / signal 覆盖问题

ExceptionHandler / signal 覆盖问题

作者: 大成小栈 | 来源:发表于2021-05-31 23:06 被阅读0次

在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

相关文章

网友评论

      本文标题:ExceptionHandler / signal 覆盖问题

      本文链接:https://www.haomeiwen.com/subject/gnyisltx.html