iOS 相容多個有crash 收集機制的SDK

Auditore發表於2018-01-03

我們知道,如果一個APP內有多個第三方crash 收集SDK,那麼很可能他們之間會有衝突,如果APP自身也有crash 收集機制,那麼如何做相容呢?

1.UncaughtException 如果同時有多方通過NSSetUncaughtExceptionHandler註冊異常處理程式,和平的作法是:後註冊者通過NSGetUncaughtExceptionHandler將先前別人註冊的handler取出並備份,在自己handler處理完後自覺把別人的handler註冊回去,規規矩矩的傳遞。不傳遞強行覆蓋的後果是,在其之前註冊過的日誌收集服務寫出的Crash日誌就會因為取不到NSException而丟失Last Exception Backtrace等資訊。(P.S. iOS系統自帶的Crash Reporter不受影響) 在開發測試階段,可以利用 fishhook 框架去hookNSSetUncaughtExceptionHandler方法,這樣就可以清晰的看到handler的傳遞流程斷在哪裡,快速定位汙染環境者。不推薦利用偵錯程式新增符號斷點來檢查,原因是一些Crash收集框架在除錯狀態下是不工作的。 示例:

static NSUncaughtExceptionHandler *previousUncaughtExceptionHandler;
+ (void)installExceptionHandler {   
 //將之前的註冊的handler儲存起來    previousUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();    NSSetUncaughtExceptionHandler(&LDAPMUncaughtExceptionHandler);
}
static void LDAPMUncaughtExceptionHandler(NSException *exception) {    
// 獲取堆疊,收集堆疊    
// 這裡做我們自己對exception的處理   
 // ······   
 // 處理完以後,呼叫前者的handler處理exception,讓他們也有資料,如果不做處理,之前的註冊的都將無法獲取到exception   
 if (previousUncaughtExceptionHandler) {            
      previousUncaughtExceptionHandler(exception);   
    }
}
複製程式碼

在開發測試階段,可以利用 fishhook 框架去hook NSSetUncaughtExceptionHandler方法,這樣就可以清晰的看到handler的傳遞流程斷在哪裡,快速定位汙染環境者。不推薦利用偵錯程式新增符號斷點來檢查,原因是一些Crash收集框架在除錯狀態下是不工作的。檢測程式碼如下,

static NSUncaughtExceptionHandler *g_vaildUncaughtExceptionHandler;​static void(*ori_NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler * );
​void my_NSSetUncaughtExceptionHandler( NSUncaughtExceptionHandler * handler)​{​    
g_vaildUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();​   
 if (g_vaildUncaughtExceptionHandler != NULL) {​        
 NSLog(@"UncaughtExceptionHandler=%p",g_vaildUncaughtExceptionHandler);   
} ​    
 ori_NSSetUncaughtExceptionHandler(handler);​    
 NSLog(@"%@",[NSThread callStackSymbols]);​   
 g_vaildUncaughtExceptionHandler = NSGetUncaughtExceptionHandler();​ 
 NSLog(@"UncaughtExceptionHandler=%p",g_vaildUncaughtExceptionHandler);
​}
複製程式碼

2.Signal handler

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);    
//先儲存之前舊的signal的handler    
if (old_action.sa_flags & SA_SIGINFO) {        
previousSignalHandler = old_action.sa_sigaction;   
}    
//註冊我們自己的handler    
LDAPMSignalRegister(SIGABRT);    
// .......​
}
//註冊signal handlerstatic 
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);   
}
}
複製程式碼

相關文章