iOS崩潰采集如何兼容多個異称渚簦回調(diào)

我們都知道iOS的崩潰信息收集,市面上有很多的三方sdk可供選擇伴逸,而系統(tǒng)對于崩潰的處理handler提供了一個方法去設(shè)置缠沈,于是就有疑問,假如項目想接入多個sdk去采集崩潰信息错蝴,會不會只有一個生效了洲愤?如何讓不同的sdk設(shè)置的handler都執(zhí)行了

1. 崩潰的收集

1.1 設(shè)置多個異常處理handler看看效果

如下所示,我們調(diào)用系統(tǒng)提供的異常處理設(shè)置方法顷锰,我們先后設(shè)置2個handler柬赐,當(dāng)發(fā)生崩潰的時候查看異常情況的調(diào)用情況

NSSetUncaughtExceptionHandler(&HCUncaughtExceptionHandles);
NSSetUncaughtExceptionHandler(&HCAnotherUncaughtExceptionHandles);

// 測試異常代碼
- (void)testException {
    NSArray *array = [NSArray arrayWithObjects:@"1", @"2", nil];
    __unused NSString *testString = array[2];
}
圖片.png

結(jié)果發(fā)現(xiàn)后設(shè)置的handler生效了,第一個設(shè)置的沒有調(diào)用官紫,這就是被覆蓋了肛宋;假如我們集成了多個收集崩潰的sdk州藕,那么不就只有一個生效了?酝陈?

假如sdk都不做任何處理直接設(shè)置handler的話那么就會有這個問題床玻,我們看看集成Bugly然后設(shè)置自己定義的handler看看表現(xiàn)如何

1.2 集成bugly,再設(shè)置自定義的異常處理handler看看效果

 NSSetUncaughtExceptionHandler(&HCUncaughtExceptionHandles);
 //NSSetUncaughtExceptionHandler(&HCAnotherUncaughtExceptionHandles);
 [Bugly startWithAppId:@"這里替換appid"];
圖片.png

可以看到當(dāng)異常觸發(fā)的時候Bugly的和我們自己設(shè)置的handler都觸發(fā)了后添,看來Bugly還是很友好的做了這種兼容笨枯,優(yōu)秀的sdk就該如此設(shè)計

2. 怎么做到多個Handler的采集

由于系統(tǒng)提供的NSSetUncaughtExceptionHandler的設(shè)置方法,內(nèi)部只會保存一個handler函數(shù)遇西,所以會存在不做處理的情況下覆蓋掉之前設(shè)置的

2.1 分析源碼

從源碼中也可以得到驗證

  1. _objc_init中進行異常初始化馅精,設(shè)置為_objc_terminate函數(shù)

    exception_init();
    void exception_init(void)
    {
        old_terminate = std::set_terminate(&_objc_terminate);
    }
    
    
    
  2. _objc_terminate函數(shù)內(nèi)部會對OC的異常調(diào)用foundation的uncaught_handler(如果設(shè)置了的話)

    static void _objc_terminate(void)
    {
        if (PrintExceptions) {
            _objc_inform("EXCEPTIONS: terminating");
        }
        
        if (! __cxa_current_exception_type()) {
            // No current exception.
            (*old_terminate)();
        }
        else {
            // There is a current exception. Check if it's an objc exception.
            @try {
                __cxa_rethrow();
            } @catch (id e) {
                // It's an objc object. Call Foundation's handler, if any.
                (*uncaught_handler)((id)e);
                (*old_terminate)();
            } @catch (...) {
                // It's not an objc object. Continue to C++ terminate.
                (*old_terminate)();
            }
        }
    }
    
  1. objc-exception中提供了讀寫uncaught_handler的方法
static objc_uncaught_exception_handler uncaught_handler = _objc_default_uncaught_exception_handler;
objc_uncaught_exception_handler
objc_setUncaughtExceptionHandler(objc_uncaught_exception_handler fn)
{
    objc_uncaught_exception_handler result = uncaught_handler;
    uncaught_handler = fn;
    return result;
}

可以看到uncaught_handler就是一個函數(shù)指針,當(dāng)設(shè)置多次就會覆蓋前面的設(shè)置粱檀;假如系統(tǒng)設(shè)置這個handler是一個指針集合洲敢,那么設(shè)置多個就存儲多個,調(diào)用的時候去集合中取出回調(diào)調(diào)用茄蚯,理論上是可行的压彭。

現(xiàn)在系統(tǒng)設(shè)計就是一個回調(diào)函數(shù),那么我們?nèi)绾巫龅蕉鄠€回調(diào)都會被調(diào)用了渗常,上面看Bugly是可以做到的壮不,而且看調(diào)用堆棧有個g_BLYPreviousUncaughtExceptionHandler函數(shù),猜測Bugly是用這個函數(shù)存儲了別人設(shè)置的handler

2.2 Bugly+設(shè)置多個自定義handler查看效果

設(shè)置多個handlers

[Bugly startWithAppId:@"這里替換appid"];
NSSetUncaughtExceptionHandler(&HCUncaughtExceptionHandles);
NSSetUncaughtExceptionHandler(&HCAnotherUncaughtExceptionHandles);
圖片.png

可以看到我們自定義設(shè)置了2個皱碘,結(jié)果后設(shè)置的執(zhí)行了询一,沒有2個都執(zhí)行;大膽猜測Bugly是用一個函數(shù)指針保存了設(shè)置的handler癌椿,在異常處理的時候調(diào)用這個保存的handler健蕊,而且也是只保存最近設(shè)置的那個;這里還有優(yōu)化的空間啊

2.3 自己實現(xiàn)該功能

系統(tǒng)動態(tài)庫的一些C函數(shù)踢俄,它在運行時進行符號的綁定確定函數(shù)的調(diào)用地址缩功,那么我們就可以用fishhook來hook系統(tǒng)的函數(shù)

2.3.1 hook系統(tǒng)設(shè)置handler的函數(shù)
  1. 定義數(shù)據(jù)結(jié)構(gòu)存儲handler指針
@interface HCHandler : NSObject {
    @package
    NSUncaughtExceptionHandler *handler;
}

@end

@implementation HCHandler
  
- (BOOL)isEqual:(HCHandler *)object {
    if (self == object) {
        return YES;
    }
    
    return (self->handler) == (object->handler);
}

- (NSUInteger)hash {
    //NSLog(@"%ld", (NSUInteger)(self->handler));
    return (NSUInteger)(self->handler);
}

@end
  1. hook系統(tǒng)的NSSetUncaughtExceptionHandler函數(shù)
static void (*SystemNSSetUncaughtExceptionHandler)(NSUncaughtExceptionHandler * _Nullable handler);
static NSHashTable *_handlers;

+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _handlers = [NSHashTable weakObjectsHashTable];
        struct rebinding rebindingHandler = {};
        rebindingHandler.name = "NSSetUncaughtExceptionHandler";
        rebindingHandler.replacement = (void *)MineSetUncaughtExceptionHandler;
        rebindingHandler.replaced = (void **)&SystemNSSetUncaughtExceptionHandler;
        struct rebinding rebindings[] = {rebindingHandler};
        rebind_symbols(rebindings, 1);
    });
}

void MineSetUncaughtExceptionHandler(NSUncaughtExceptionHandler * _Nullable handler) {
    if (handler == HCUncaughtExceptionHandles) { // 如果handler是我們自己的handler就不加入到hashmap中
        SystemNSSetUncaughtExceptionHandler(&HCUncaughtExceptionHandles);
    } else {
        HCHandler *handlerObj = [HCHandler new];
           handlerObj->handler = handler;
           if (![_handlers containsObject:handlerObj]) {
               [_handlers addObject:handlerObj];
           }
    }
}
2.3.2 存儲別人設(shè)置的handler函數(shù)指針

這里用了個全局的hashmap去存儲的,當(dāng)設(shè)置異常handler的時候可以看到執(zhí)行了我們hook的方法MineSetUncaughtExceptionHandler都办,我們在這里進行handler的存儲

測試代碼:這里我們測試自己定義的2個handler

//[Bugly startWithAppId:@"這里替換為appId"];
NSSetUncaughtExceptionHandler(&HCUncaughtExceptionHandles);
NSSetUncaughtExceptionHandler(&HCAnotherUncaughtExceptionHandles);

Debug查看如下圖嫡锌,我們將設(shè)置的handler存儲起來了,并將系統(tǒng)的異常的回調(diào)handler設(shè)置為HCUncaughtExceptionHandles

圖片.png
2.3.3 異沉斩ぃ回調(diào)的時候世舰,執(zhí)行存儲的異常handlers

我們在hook系統(tǒng)的設(shè)置handler的方法的時候,設(shè)置最終的回調(diào)處理為HCUncaughtExceptionHandles槽卫,那么我們就在該回調(diào)函數(shù)中處理其他注冊的handler的觸發(fā)邏輯

代碼如下:

oid HCUncaughtExceptionHandles(NSException *exception) {
    NSArray<HCHandler *> *handlers = _handlers.allObjects;
    [handlers enumerateObjectsUsingBlock:^(HCHandler * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj->handler) {
            obj->handler(exception);
        }
    }];
    // 自己的異常處理邏輯
}

異常發(fā)生查看效果

圖片.png

至此已經(jīng)實現(xiàn)了我們開始的需求如何同時存在多個異常處理的handler

同理signal的異常也可以通過hook signal函數(shù)去設(shè)計實現(xiàn)跟压;這里就舉個例子針對Exception的回調(diào)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市歼培,隨后出現(xiàn)的幾起案子震蒋,更是在濱河造成了極大的恐慌茸塞,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件查剖,死亡現(xiàn)場離奇詭異钾虐,居然都是意外死亡,警方通過查閱死者的電腦和手機笋庄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門效扫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人直砂,你說我怎么就攤上這事菌仁。” “怎么了静暂?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵济丘,是天一觀的道長。 經(jīng)常有香客問我洽蛀,道長摹迷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任郊供,我火速辦了婚禮峡碉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘驮审。我一直安慰自己异赫,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布头岔。 她就那樣靜靜地躺著,像睡著了一般鼠证。 火紅的嫁衣襯著肌膚如雪峡竣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天量九,我揣著相機與錄音适掰,去河邊找鬼。 笑死荠列,一個胖子當(dāng)著我的面吹牛类浪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肌似,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼费就,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了川队?” 一聲冷哼從身側(cè)響起力细,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤睬澡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后眠蚂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煞聪,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年逝慧,在試婚紗的時候發(fā)現(xiàn)自己被綠了昔脯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡笛臣,死狀恐怖云稚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情捐祠,我是刑警寧澤碱鳞,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站踱蛀,受9級特大地震影響窿给,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜率拒,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一崩泡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猬膨,春花似錦角撞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至沛申,卻和暖如春劣领,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背铁材。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工尖淘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人著觉。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓村生,卻偏偏與公主長得像,于是被迫代替她去往敵國和親饼丘。 傳聞我的和親對象是個殘疾皇子趁桃,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容