iOS-App端崩溃日志的收集+Xcode工具symbolicatecrash解析iOS Crash文件

以前对于应用层的崩溃日志,没有主动收集过,只是下过应用沙盒下的日志,还有就是集成了bugly来应对测试和线上的崩溃,最近是碰到一个需求,在sdk里面上报应用层的崩溃日志,lz就了解一下,与大家分享一下。

收集方式

Mach异常+Unix信号方式

Mach异常:Mach异常是指最底层的内核级异常,被定义在 <mach/exception_types.h>下 。
每个threadtaskhost都有一个异常端口数组,Mach的部分API暴露给了 用户态 ,用户态的开发者可以直接通过Mach API设置threadtaskhost的异常端口,来捕获Mach异常,抓取Crash事件。

UNIX信号: 所有的Mach异常都在host层被转换成相应的UNIX信号,并通过threadsignal将信号投递到出错的线程。(iOS中的 POSIX API 就是通过 Mach 之上的 BSD 层实现的。)

因此: EXC_BAD_ACCESS (SIGSEGV)表示的意思是:Mach层的EXC_BAD_ACCESS异常在host层被转换成了SIGSEGV信号,并投递到出错的线程。

Mach异常和UNIX信号都可以抓取crash事件,这两种方式哪个更好?

优选Mach异常,因为Mach异常处理会先于Unix信号处理发生,如果Mach异常的handler让程序exit了,那么Unix信号就永远不会到达这个进程了。

多个Crash日志收集服务的冲突

有时候为了防止与三方的一些日志收集服务的冲突,我们需要拿到之前的服务注册的handler,然后备份,等自己的回调函数处理完后再把之前备份的handler注册回去。

相关代码如下:

+ (void)defaultHandler {
    //保存handler
    previousHandler = NSGetUncaughtExceptionHandler();
    //1.捕获一些异常导致的崩溃
    NSSetUncaughtExceptionHandler(&UncaughtExceptionHandler);
    // 2.捕获非异常情况,通过signal传递出来的崩溃
    signal(SIGABRT, SignalHandler);
    signal(SIGILL, SignalHandler);
    signal(SIGSEGV, SignalHandler);
    signal(SIGFPE, SignalHandler);
    signal(SIGBUS, SignalHandler);
    signal(SIGPIPE, SignalHandler);
}

应用级的异常NSException

// 崩溃时的回调函数
void UncaughtExceptionHandler(NSException * exception) {
    // 获取异常的堆栈信息
    NSArray *callStack = [exception callStackSymbols];
    //获取异常的名称
    NSString *exceptionName = [exception name];
    //获取异常的原因
    NSString *excepReason = [exception reason];
    NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
    [userInfo setObject:callStack forKey:kCaughtExceptionStackInfoKey];
    NSException *customException = [NSException exceptionWithName:exceptionName reason:excepReason userInfo:userInfo];
    [CatchCrash performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES];
    //将异常塞给之前的第三方
    if (previousHandler) {
         previousHandler(exception);
    }
}

通过系统的函数我们很容易就能获取到异常的堆栈信息,原因,名称,例如我们常见的 数组越界,插入空的数据,未实现的方法等都会走这个方法,lz在下面的demo里面也写的例子演示

Unix信号

void SignalHandler(int signal)
{
    // 这种情况的崩溃信息,就另某他法来捕获吧
    NSArray *callStack = [CatchCrash backtrace];
    NSException *customException = [NSException exceptionWithName:kSignalExceptionName
                                                           reason:[NSString stringWithFormat:NSLocalizedString(@"Signal %d was raised.", nil),signal]
                                                         userInfo:@{kSignalKey:[NSNumber numberWithInt:signal]}];
    [CatchCrash performSelectorOnMainThread:@selector(handleException:) withObject:customException waitUntilDone:YES];
}

+ (NSArray *)backtrace
{
    void* callstack[128];
    int frames = backtrace(callstack, 128);
    char **strs = backtrace_symbols(callstack, frames);
    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
    for (int i = 0; i < frames; i++) {
        [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];
    }
    free(strs);
    return backtrace;
}

关于Unix信号方式方式捕获的异常,通常见于EXC_BAD_ACESS野指针错误,demo里面也有例子

关于signal信号的捕捉,在Xcode调试时,Debugger模式会先于我们的代码catch到所有的crash
想看看到此类崩溃,demo不能连着xcode,不然看不到

对于搜集到的崩溃日志的处理

+ (void)handleException:(NSException *)exception
{
    NSString *exceptionInfo = [NSString stringWithFormat:@"========异常错误报告========:\n%@\n%@\n%@",[exception name],[exception reason],[[exception userInfo] objectForKey:kCaughtExceptionStackInfoKey]];
    NSString * path = [applicationDocumentsDirectory() stringByAppendingPathComponent:@"Exception.txt"];
    // 将一个txt文件写入沙盒
    [exceptionInfo writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

Mach异常+Unix信号方式产生的崩溃,lz放到了沙盒下面,在下一次程序启动的时候就上传,上传完成之后就移除沙盒下面的文件

参看链接
demo传送门

本地日志解析

在本地新建一个项目,写一个简单的崩溃程序

NSString *str = nil;
NSDictionary *dict = @{@"test":str};

然后用Xcode打包,把生成的ipa上传到蒲公英,然后下载到手机,点开运行一下,桌面创建一个文件夹crash

  • 获取crash文件
    Xcode->Window->Devices and Simulators->View Device Logs->右键导出crash文件。
  • 获取dSYM文件
    Window->Organizer->.xcarchive->右键显示包内容->dSYMs文件->xxx.app.dSYM
  • symbolicatecrash工具
    通过find找到symbolicatecrash工具的路径
find /Applications/Xcode.app -name symbolicatecrash -type f

前往文件夹->粘贴路径->symbolicatecrash工具
把crash文件+dSYM文件+symbolicatecrash工具copy到刚才创建的crash文件
cd到crash文件,执行命令

./symbolicatecrash ./*.crash ./*.app.dSYM > symbol.crash

如果出现 Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 69
执行命令

export DEVELOPER_DIR=/Applications/XCode.app/Contents/Developer

然后在执行

./symbolicatecrash ./*.crash ./*.app.dSYM > symbol.crash

可以看到本地生成的日志分析文件symbol.crash文件

Application Specific Information:
abort() called

Last Exception Backtrace:
0   CoreFoundation                  0x1a067d27c __exceptionPreprocess + 228
1   libobjc.A.dylib                 0x19f8579f8 objc_exception_throw + 55
2   CoreFoundation                  0x1a05f6ce8 _CFThrowFormattedException + 111
3   CoreFoundation                  0x1a057e9a8 -[__NSPlaceholderDictionary initWithObjects:forKeys:count:] + 351
4   CoreFoundation                  0x1a056f584 +[NSDictionary dictionaryWithObjects:forKeys:count:] + 63
5   testaaa                         0x102a5a548 _hidden#0_ + 25928 (__hidden#4_:22)
6   UIKitCore                       0x1cca0dfc8 -[UIViewController loadViewIfRequired] + 1011
7   UIKitCore                       0x1cca0e3cc -[UIViewController view] + 27
8   UIKitCore                       0x1ccfecba4 -[UIWindow addRootViewControllerViewIfPossible] + 135
9   UIKitCore                       0x1ccfed14c -[UIWindow _setHidden:forced:] + 271
10  UIKitCore                       0x1ccffda28 -[UIWindow makeKeyAndVisible] + 47
11  UIKitCore                       0x1ccfb0648 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3531
12  UIKitCore                       0x1ccfb5d20 -[UIApplication _runWithMainScene:transitionContext:completion:] + 1539
13  UIKitCore                       0x1cc8792dc __111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke + 775
14  UIKitCore                       0x1cc881874 +[_UICanvas _enqueuePostSettingUpdateTransactionBlock:] + 159
15  UIKitCore                       0x1cc878f60 -[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:] + 235
16  UIKitCore                       0x1cc879850 -[__UICanvasLifecycleMonitor_Compatability activateEventsOnly:withContext:completion:] + 1063
17  UIKitCore                       0x1cc877b9c __82-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:]_block_invoke + 743
18  UIKitCore                       0x1cc877864 -[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:] + 427
19  UIKitCore                       0x1cc87c3a4 __125-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:]_block_invoke + 219
20  UIKitCore                       0x1cc87d188 _performActionsWithDelayForTransitionContext + 111
21  UIKitCore                       0x1cc87c25c -[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:] + 243
22  UIKitCore                       0x1cc880f5c -[_UICanvas scene:didUpdateWithDiff:transitionContext:completion:] + 359
23  UIKitCore                       0x1ccfb4328 -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 539
24  UIKitCore                       0x1ccbb0ba8 -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 359
25  FrontBoardServices              0x1a2ff89fc -[FBSSceneImpl _didCreateWithTransitionContext:completion:] + 439
26  FrontBoardServices              0x1a300240c __56-[FBSWorkspace client:handleCreateScene:withCompletion:]_block_invoke_2 + 255
27  FrontBoardServices              0x1a3001c14 __40-[FBSWorkspace _performDelegateCallOut:]_block_invoke + 63
28  libdispatch.dylib               0x1a00bd7d4 _dispatch_client_callout + 15
29  libdispatch.dylib               0x1a00625d8 _dispatch_block_invoke_direct$VARIANT$mp + 223
30  FrontBoardServices              0x1a3033040 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 39
31  FrontBoardServices              0x1a3032cdc -[FBSSerialQueue _performNext] + 407
32  FrontBoardServices              0x1a3033294 -[FBSSerialQueue _performNextFromRunLoopSource] + 51
33  CoreFoundation                  0x1a060f018 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 23
34  CoreFoundation                  0x1a060ef98 __CFRunLoopDoSource0 + 87
35  CoreFoundation                  0x1a060e880 __CFRunLoopDoSources0 + 175
36  CoreFoundation                  0x1a06097bc __CFRunLoopRun + 1003
37  CoreFoundation                  0x1a06090b0 CFRunLoopRunSpecific + 435
38  GraphicsServices                0x1a280979c GSEventRunModal + 103
39  UIKitCore                       0x1ccfb7978 UIApplicationMain + 211
40  testaaa                         0x102a5a5d4 main + 26068 (__hidden#7_:14)
41  libdyld.dylib                   0x1a00ce8e0 start + 3
[__NSPlaceholderDictionary initWithObjects:forKeys:count:] + 351
[NSDictionary dictionaryWithObjects:forKeys:count:] + 63

从上面的日志解析文件可以很明显的看出是字典初始化插入数据为空,但是没有定位到具体的代码,这一点比较难受

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容