从容面对app崩溃-iOS篇

前言

有代码的地方,就可能会有崩溃。定位崩溃问题的方法:最直接的,通过重现的步骤去调适;但是,有时候测试无法确定崩溃的步骤,或者崩溃是线上用户发生的,这些情况下,我们仍然可以通过崩溃日志,知道崩溃的原因和崩溃的代码行,从而修复问题。

- 什么是崩溃?

Application crashes: An application typically crashes when it performs an operation which is not allowed by the operating system.

Server crashes, Operating system crashes, …

- 定位崩溃原因的方法有哪些?

线索1:重现步骤

线索2:崩溃日志(知道崩溃的原因、崩溃的代码行等信息)

那么,本文就来说说崩溃日志。


获取日志的几种方法

在iOS系统中,当应用崩溃的时候,系统会生成一个崩溃日志,保存在设备上。

1.直接在手机上查看

在手机端设置里查看,如图是iOS10的界面:

2.手机连PC用iTunes导出

iTunes同步资料库,然后去PC对应目录下,找文件

Mac OS X:   ~/Library/Logs/CrashReporter/MobileDevice/

Windows XP:   C://Documents and Settings/Application Data/Apple Computer/Logs/Crash/Reporter/MobileDevice

Windows Vista or 7:    C://Users/AppData/Roaming/Apple Computer/Logs/Crash/Reporter/MobileDevice

3.手机连XCode导出

Simulator-Hardware-Device-Manage Devices

4.XCode中,对提交商店的应用,查看Crashes

xcode->Window->Organizer->Crashes


5.用iTunes Connect(提交商店的应用)

如果应用提交到App Store了,还可以从iTunes Connect(https://itunesconnect.apple.com/)上获取到用户的崩溃日志.。通过应用提交者的apple id登录,管理提交的应用,应用细节中有一个崩溃报告

5.app植入第三方sdk,配合第三方平台查看

很多第三方平台,(比如蒲公英、fabric、网易云捕...)提供sdk和管理后台。

只需要使用它们的sdk,并添加几行代码,app运行时,sdk会将应用Crash数据自动上传到后台,后台会进行相应的统计和分析。并给出产品各个版本的崩溃数,崩溃率,影响用户数,崩溃的机型、系统 等。

6.使用苹果的api,自己实现抓取崩溃信息,提交到自己写的后台

和5是一样的,有这些现成的情况下,不是很必要自己重新再写一个,但是可以简单了解一下其中的关键原理。

1.iOS SDK中提供了一个现成的函数NSSetUncaughtExceptionHandler 用来做异常处理。


void HandleException(NSException*exception)   //捕捉到异常时执行的方法

{

        //异常的堆栈信息

        NSArray*stackArray = [exception callStackSymbols];

        //出现异常的原因

        NSString*reason = [exception reason];

        //异常名称

        NSString*name = [exception name];

        NSString*exceptionInfo = [NSStringstringWithFormat:@"Exception reason:%@\nException name:%@\nException stack:%@",name, reason, stackArray];

        NSLog(@"%@", exceptionInfo);

        [UncaughtExceptionHandler saveCrash:exceptionInfo]; //自己实现一个记录下这些信息的方法,作者是实现了一个写文本文件的方法

}

void InstallUncaughtExceptionHandler(void)    //注册一些抓取函数,在APPDelegate中didFinishLaunchingWithOptions调用

{

        NSSetUncaughtExceptionHandler(&HandleException);

}


2.但NSSetUncaughtExceptionHandler功能非常有限,而引起崩溃的原因如:内存访问错误,重复释放等错误并不能被抓到,而这种错误它抛出的是Signal,因此做一个Signal处理可以抓到这一类的崩溃。


void SignalExceptionHandler(intsignal) //抓取到Signal异常时,执行的方法

{

         NSMutableString*mstr = [[NSMutableString alloc] init];

         [mstr appendString:@"Stack:\n"];

         void* callstack[128];

         int i, frames =backtrace(callstack,128);

         char** strs =backtrace_symbols(callstack, frames);

         for(i =0; i<frames;++i){

                  [mstr appendFormat:@"%s\n", strs[i]];

          }

          [SignalHandler saveCrash:mstr]; //同上,自己实现一个记录下这些信息的方法,作者是实现了一个写文本文件的方法

}

void InstallSignalHandler(void) //注册抓取函数和抓取的Signal类型,在APPDelegate中didFinishLaunchingWithOptions调用

{

          signal(SIGHUP,SignalExceptionHandler);

          signal(SIGINT,SignalExceptionHandler);

          signal(SIGQUIT,SignalExceptionHandler);

          signal(SIGABRT,SignalExceptionHandler);

          signal(SIGILL,SignalExceptionHandler);

          signal(SIGSEGV,SignalExceptionHandler);

          signal(SIGFPE,SignalExceptionHandler);

          signal(SIGBUS,SignalExceptionHandler);

          signal(SIGPIPE,SignalExceptionHandler);

}

3.写完上面两个类,在APPDelegate中didFinishLaunchingWithOptions调用


- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {

         InstallSignalHandler();

         InstallUncaughtExceptionHandler();

         return YES;

}

4.可以写一个demo,按钮函数包含一些崩溃的代码,然后就会发现抓取的崩溃的函数,被执行了,崩溃信息成功输出到了指定的文本文件中。

- (IBAction)buttonClick:(UIButton*)sender {    //1.信号量

         int list[2]={1,2};

         int*p = list;

         free(p);   

         p[1] =5;

}

- (IBAction)buttonOCException:(UIButton*)sender{        //2.ios崩溃

          NSArray*array=@[@"tom",@"xxx",@"ooo"];

          [array objectAtIndex:5];

}

5.得到了关键的堆栈信息,和崩溃原因


到这里,已经知道了,怎么获得到这些信息。下面继续讲一下,怎么根据这些信息,定位到是哪里崩溃的,以及其中可能遇到的一些问题。

如何读懂、解析日志

1.崩溃日志提供的信息


从手机直接捞出来的日志,很有可能长这样,一推十六进制的,看不出有效信息。那么问题来了,怎么样转换为看得懂的信息呢?

2.认识符号文件

符号文件是保存 16 进制函数地址映射信息的中转文件,符号集中存储着文件名、方法名、行号的信息.

符号文件和崩溃日志,具有一一对应的关系。

如何确定符合文件A和崩溃日志B是一一对应的?

在崩溃日志中会记录一个UUID;用一些工具(比如dwarfdump),可以查询符号文件的UUID;如果是一致的,那就说明这两个文件的一对对应的符号文件和崩溃日志,可以用这个符号文件解析这个崩溃日志。

然后,用dwarfdump、atos等工具,可以将崩溃日志,通过对应的符号文件,将十六进制数通过查符号表,转化为工程中的方法名,代码行号。

比如:

崩溃日志中有一段:


然后,用dwarfdump命令

命令:dwarfdump --uuid Your.app.dSYM


对比两个uuid一致,就对上了。

最后,用symbolicatecrash工具,解析崩溃日志

命令:

cd /Users/yourMac/Desktop/崩溃文件夹

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

就得到了崩溃日志。


3.解析的原理

(1)-原始的崩溃文件:

原始的崩溃文件其中一行,拿出来讲解

3 UncaughtExceptionDemo0x00dabe0 0x00d300e + 31698

序号  进程名                  崩溃时的堆栈地址    运行时起始地址 + 偏移量

应用崩溃发生时的堆栈地址为0x00dabe0,该进程的运行时起始地址是0x00d300e,崩溃处距离进程起始地址的偏移量为十进制的31698(对应十六进制为0x0007bd2)。

三者对应关系: 0x00dabe0 =0x00d300e+ 0x0007bd2

(崩溃时的堆栈地址= 运行时起始地址 + 偏移量)

接着,去符号表中查询

(2)-获取符号表起始地址

命令:$otool -l Your.app.dSYM/Contents/Resources/DWARF/Your

结果


其中vmaddr = 0x00004000 就是符号表中的起始地址。

符号表崩溃地址 = 符号表起始地址 + 偏移量

= 0x4000 + 0x7bd2

= 0xbbd2

得到符号表中的崩溃地址

(3)-提取.debug_info

命令:$ dwarfdump -e --debug-info YourPath/YourApp.dSYM/Contents/Resources/DWARF > info-e.txt

会生成一个txt,里面包含很多个如下的内容,文件内容举例:


可以知道ViewController::buttonOCException方法的地址范围:0x0000bb76-0x0000bc00。

(4)提取.debug_line

命令:$ dwarfdump -e --debug-line YourPath/YourApp.dSYM/Contents/Resources/DWARF > line-e.txt

会生成一个txt,里面包含很多个如下的内容,文件内容举例:


这里代表的意思是,每一行代码,对应的地址、偏移量。

(5)-结论,得到关键信息:

解析后的崩溃日志为

3UncaughtExceptionDemo-[ViewController buttonOCException:] (ViewController.m:36)

崩溃所在源码文件:/Users/huangchong/iOSCrashUncaught-master/UncaughtExceptionDemo/ViewController.m

发生崩溃的方法:-[ViewController :buttonOCException]

发生崩溃的方法在源文件中的行号:32

崩溃发生的代码行,在源文件中得行号:36


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

推荐阅读更多精彩内容

  • 写在前面:本文会在最开头将苹果官方的文档Understanding and Analyzing Applicati...
    小小外星人阅读 25,588评论 23 130
  • 前言 iOS崩溃是让iOS开发人员比较头痛的事情,app崩溃了,说明代码写的有问题,这时如何快速定位到崩溃的地方很...
    齐滇大圣阅读 65,224评论 29 443
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 前言 崩溃是让发人员比较头痛的事情,app崩溃了,说明代码写的有问题,这时如何快速定位到崩溃的地方很重要。调试阶段...
    進无尽阅读 1,965评论 0 9
  • 该文章属于刘小壮原创,转载请注明:刘小壮[https://www.jianshu.com/u/2de707c93d...
    刘小壮阅读 37,502评论 45 122