軟件測試之SDK開發(ios)——signal捕獲

signal是一種軟中斷信號,提供異步事件處理機制。signal是進程間相互傳遞信息的一種粗糙方法,使用場景如下:

  • 進程終止
  • 終端交互
  • 編程錯誤或硬件錯誤相關,系統遇到不可恢復的錯誤時觸發崩潰機制讓程序退出,比如:除0,內存寫入錯誤等。

這裏我們主要考慮系統遇到不可恢復的錯誤即Crash時,信號相關的應用。此類致命signal有很多,簡單列舉如下:

SIGABRT–程序中止命令中止信號
SIGALRM–程序超時信號
SIGFPE–程序浮點異常信號
SIGILL–程序非法指令信號
SIGHUP–程序終端中止信號
SIGINT–程序鍵盤中斷信號
SIGKILL–程序結束接收中止信號
SIGTERM–程序kill中止信號
SIGSTOP–程序鍵盤中止信號
SIGSEGV–程序無效內存中止信號
SIGBUS–程序內存字節未對齊中止信號
SIGPIPE–程序Socket發送失敗中止信號

signal的捕獲可以使用signal函數,將signal拋給處理函數進行處理,通過signal 的name 和線程的callStackSymbols等信息即可定位該signal發生的場景。如下所示:

void SignalHandler(int sig)
{
    // See https://stackoverflow.com/questions/40631334/how-to-intercept-exc-bad-instruction-when-unwrapping-nil.
    NSString *name = @"Unknown signal";
    switch (sig) {
        case SIGHUP:{
            name = @"SIGHUP";
        }
            break;
        case SIGINT:{
            name = @"SIGINT";
        }
            break;
        case SIGQUIT:{
            name = @"SIGQUIT";
        }
            break;
        case SIGILL:{
            name = @"SIGILL";
        }
            break;
        case SIGTRAP:{
            name = @"SIGTRAP";
        }
            break;
        case SIGABRT:{
            name = @"SIGABRT";
        }
            break;
#ifdef SIGPOLL
        case SIGPOLL:{
            name = @"SIGPOLL";
        }
            break;
#endif
        case SIGEMT:{
            name = @"SIGEMT";
        }
            break;
        case SIGFPE:{
            name = @"SIGFPE";
        }
            break;
        case SIGKILL:{
            name = @"SIGKILL";
        }
            break;
        case SIGBUS:{
            name = @"SIGBUS";
        }
            break;
        case SIGSEGV:{
            name = @"SIGSEGV";
        }
            break;
        case SIGSYS:{
            name = @"SIGSYS";
        }
            break;
        case SIGPIPE:{
            name = @"SIGPIPE";
        }
            break;
        case SIGALRM:{
            name = @"SIGALRM";
        }
            break;
        case SIGTERM:{
            name = @"SIGTERM";
        }
            break;
        case SIGURG:{
            name = @"SIGURG";
        }
            break;
        case SIGSTOP:{
            name = @"SIGSTOP";
        }
            break;
        case SIGTSTP:{
            name = @"SIGTSTP";
        }
            break;
        case SIGCONT:{
            name = @"SIGCONT";
        }
            break;
        case SIGCHLD:{
            name = @"SIGCHLD";
        }
            break;
        case SIGTTIN:{
            name = @"SIGTTIN";
        }
            break;
        case SIGTTOU:{
            name = @"SIGTTOU";
        }
            break;
#ifdef SIGIO
        case SIGIO:{
            name = @"SIGIO";
        }
            break;
#endif
        case SIGXCPU:{
            name = @"SIGXCPU";
        }
            break;
        case SIGXFSZ:{
            name = @"SIGXFSZ";
        }
            break;
        case SIGVTALRM:{
            name = @"SIGVTALRM";
        }
            break;
        case SIGPROF:{
            name = @"SIGPROF";
        }
            break;
#ifdef SIGWINCH
        case SIGWINCH:{
            name = @"SIGWINCH";
        }
            break;
#endif
#ifdef SIGINFO
        case SIGINFO:{
            name = @"SIGINFO";
        }
            break;
#endif
        case SIGUSR1:{
            name = @"SIGUSR1";
        }
            break;
        case SIGUSR2:{
            name = @"SIGUSR2";
        }
            break;
        default:{}
            break;
    }

    NSArray *callStackSymbols = [NSThread callStackSymbols];
    NSString *date = [LLTool stringFromDate:[NSDate date]];
    NSDictionary *appInfos = [LLRoute dynamicAppInfos];
    LLCrashSignalModel *signalModel = [[LLCrashSignalModel alloc] initWithName:name stackSymbols:callStackSymbols date:date userIdentity:[LLConfig sharedConfig].userIdentity appInfos:appInfos];
    if ([LLCrashHelper sharedHelper].crashModel) {
        [[LLCrashHelper sharedHelper].crashModel updateAppInfos:[LLRoute appInfos]];
        [[LLCrashHelper sharedHelper].crashModel appendSignalModel:signalModel];
        [[LLStorageManager sharedManager] updateModel:[LLCrashHelper sharedHelper].crashModel complete:^(BOOL result) {
            NSLog(@"Save signal model success");
        } synchronous:YES];
    } else {
        LLCrashModel *model = [[LLCrashModel alloc] initWithName:signalModel.name reason:@"Catch Signal" userInfo:nil stackSymbols:callStackSymbols date:date userIdentity:[LLConfig sharedConfig].userIdentity appInfos:[LLRoute appInfos] launchDate:[NSObject LL_launchDate]];
        [model appendSignalModel:signalModel];
        [LLCrashHelper sharedHelper].crashModel = model;
        [[LLStorageManager sharedManager] saveModel:model complete:^(BOOL result) {
            NSLog(@"Save signal model success");
        } synchronous:YES];
    }
    
    //將crash的有用信息轉換成字典
    NSDictionary *crashInfo = [NSDictionary dictionaryWithObjectsAndKeys:name, @"name",
                               @"Catch Signal",@"reason",
                               callStackSymbols,@"stack",nil] ;
    
    [[LLDebugTool sharedTool] uploadBugWithDict:crashInfo exceptionType:CRASH files:nil takeScreenshot:NO complete:^(BOOL result,NSString* zipPath) {
        if(result){
            NSLog(@"上傳bug成功");
            [[NSFileManager defaultManager] removeItemAtPath:zipPath error:nil];
        };
        
    } synchronous:YES] ;
}

參考文章:
1、http://www.cocoachina.com/articles/22765
2、https://www.jianshu.com/p/1b804426d212
3、http://www.iosxxx.com/blog/2015-08-29-iosyi-chang-bu-huo.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章