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