我們知道,如果一個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);
}
}
複製程式碼