iOS Crash日志全面解析:结构、类型与分析方法

iOS Crash日志收集

上架AppStroe 被打回来了,原因是:

Your app crashed on iPad running iOS 11.3.1 connected to an IPv6 network when we tapped on profile image.

We have attached detailed crash logs to help troubleshoot this issue.

一、Crash文件结构

当程序运行Crash的时候,系统会把运行的最后时刻的运行信息记录下来,存储到一个文件中,也就是我们所说的Crash文件。iOS的Crash日志通常由以下6各部分组成。

1、Process Information(进程信息)

Incident Idnetifier

崩溃报告的唯一标识符,不同的Crash

CrashReporter Key

设备标识相对应的唯一键值(并非真正的设备的UDID,苹果为了保护用户隐私iOS6以后已经无法获取)。通常同一个设备上同一版本的App发生Crash时,该值都是一样的。

Hardware Model

代表发生Crash的设备类型,上图中的"iPad4,4"代表iPad Air

Process

代表Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的ID

Path

可执行程序在手机上的存储位置,注意路径时到/XXX,其实是作为一个Bundle的,真正的可执行文件其实是Bundle里面的XXX,感兴趣的可以自己查一下相关资料,有机会我后面也会介绍到

Identifier

你的App的Indentifier,通常为"com.xxx.yyy",xxx代表你们公司的域名,yyy代表某一个App

Version

当前App的版本号,由Info.plist中的两个字段组成, CFBundleShortVersionString and CFBundleVersion

Code Type

当前App的CPU架构

Parent Process

当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都是launchd

2、Basic Information

Date/Time

Crash发生的时间,可读的字符串

OS Version

系统版本,()内的数字代表的时Bulid号

Report Version

Crash日志的格式,目前基本上都是104,不同的version里面包含的字段可能有不同

3、Exception(非常重要)

Exception Type

异常类型

Exception Subtype:

异常子类型

Crashed Thread

发生异常的线程号

4、Thread Backtrace

发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。信息表明本次Crash出现xxxViewController的323行,出错的函数调用为orderCountLoadFailed。

5、Thread State

Crash时发生时刻,线程的状态,通常我们根据Crash栈即可获取到相关信息,这部分一般不用关心。

6、Binary Images

Crash时刻App加载的所有的库,其中第一行是Crash发生时我们App可执行文件的信息,可以看出为armv7,可执行文件的包得uuid位c0f......cd65,解析Crash的时候dsym文件的uuid必须和这个一样才能完成Crash的符号化解析。

二、常见的Crash类型

1、Watchdog timeout

Exception Code:0x8badf00d, 不太直观,可以读成"eat bad food",意思是don't block main thread

紧接着下面会有一段描述:

Application Specific Information:

com.xxx.yyy   failed to resume in time

对于此类Crash,我们应该去审视自己App初始化时做的事情是否正确,是否在主线程请求了网络,或者其他耗时的事情卡住了正常初始化流程。

通常系统允许一个App从启动到可以相应用户事件的时间最多为5S,如果超过了5S,App就会被系统终止掉。在Launch,resume,suspend,quit时都会有相应的时间要求。在Highlight Thread里面我们可以看到被终止时调用到的位置,xxxAppDelegate加上行号。

PS. 在连接Xcode调试时为了便于调试,系统会暂时禁用掉Watchdog,所以此类问题的发现需要使用正常的启动模式。

2、User force-quit

Exception Codes: 0xdeadfa11, deadfall

这个强制退出跟我们平时所说的kill掉后台任务操作还不太一样,通常在程序bug造成系统无法响应时可以采用长按电源键,当屏幕出现关机确认画面时按下Home键即可关闭当前程序。

3、Low Memory termination

跟一般的Crash结构不太一样,通常有Free pages,Wired Pages,Purgeable pages,largest process 组成,同事会列出当前时刻系统运行所有进程的信息。

关于Memory warning可以参看我之前写的一篇文章IOS 内存警告 Memory warning level。

App在运行过程中,系统内存紧张时通常会先发警告,同时把后台挂起的程序终止掉,最终如果还是内存不够的话就会终止掉当前前台的进程。

当接受到内存警告的事后,我们应该释放尽可能多的内存,Crash其实也可以看做是对App的一种保护。

4、Crash due to bugs

因为程序bug导致的Crash通常千奇百怪,很难一概而论。大部分情况通过Crash日志就可以定位出问题,当然也不排除部分疑难杂症看半天都不值问题出在哪儿。这个就只能看功底了,一点点找,总是能发现蛛丝马迹。是在看不出来时还可以求助于Google大神,总有人遇到和你一样的Bug

三、常见的Exception Type & Exception Code

1、Exception Type

1)EXC_BAD_ACCESS

此类型的Excpetion是我们最长碰到的Crash,通常用于访问了不改访问的内存导致。一般EXC_BAD_ACCESS后面的"()"还会带有补充信息。

SIGSEGV: 通常由于重复释放对象导致,这种类型在切换了ARC以后应该已经很少见到了。

SIGABRT: 收到Abort信号退出,通常Foundation库中的容器为了保护状态正常会做一些检测,例如插入nil到数组中等会遇到此类错误。

SEGV:(Segmentation Violation),代表无效内存地址,比如空指针,未初始化指针,栈溢出等;

SIGBUS:总线错误,与 SIGSEGV 不同的是,SIGSEGV 访问的是无效地址,而 SIGBUS 访问的是有效地址,但总线访问异常(如地址对齐问题)

SIGILL:尝试执行非法的指令,可能不被识别或者没有权限

2) EXC_BAD_INSTRUCTION

此类异常通常由于线程执行非法指令导致

3) EXC_ARITHMETIC

除零错误会抛出此类异常

2、Exception Code

0xbaaaaaad

此种类型的log意味着该Crash log并非一个真正的Crash,它仅仅只是包含了整个系统某一时刻的运行状态。通常可以通过同时按Home键和音量键,可能由于用户不小心触发

0xbad22222

当VOIP程序在后台太过频繁的激活时,系统可能会终止此类程序

0x8badf00d

这个前面已经介绍了,程序启动或者恢复时间过长被watch dog终止

0xc00010ff

程序执行大量耗费CPU和GPU的运算,导致设备过热,触发系统过热保护被系统终止

0xdead10cc

程序退到后台时还占用系统资源,如通讯录被系统终止

0xdeadfa11

前面也提到过,程序无响应用户强制关闭

三、获取Crash的途径

1、本机

通过xCode连接测试机器,直接在Device中即可读取到该机器上发生的所有Crash log。

2、itunes connect

通过itunes connect后台获取到用户上报的Crash日志。

3、第三方的Crash收集系统

有很多优秀的第三方Crash收集系统大大的方便了我们收集Crash,甚至还带了符号化Crash日志的功能。比较常用的有Crashlytics,Flurry等。

对于iOS开发者,Keymob助手提供了集成的崩溃日志分析功能,支持实时查看和符号化解析,帮助更高效地诊断和修复问题。

三、Xcode自带工具symbolicatecrash解析iOS Crash文件

一、找到.app文件和.app.dSYM文件

  • 在桌面创建一个crash文件夹,然后Xcode->Window->Organizer找到Archives找到App->右击Show in

Finder

  • 复制.app和.app.dSYM到crash夹文件:右击.xcarchive文件->显示包内容

在dSYMs文件夹中找到.app.dSYM

在Products->Applications文件夹中找到*.app

二、找到symbolicatecrash

  • 1 find /Applications/ -name symbolicatecrash -type f

  • 稍等一会就会有路径输出,这个路径就是symbolicatecrash的路径

  • 1 /Applications//Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash

  • 用命令将symbolicatecrash拷贝到桌面的crash文件夹里面,与.app和.app.dSYM放一起(手动找到symbolicatecrash,拷贝出来也行)

  • 1 cp /Applications//Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash /Users/你的电脑名称/Desktop/crash

三、执行symbolicatecrash

  • 打开终端用命令切换到桌面的crash目录下:

  • 1 cd /Users/你的电脑名称/Desktop/crash

  • 执行命令

  • 1 ./symbolicatecrash /Users/angelseahappiness/Desktop/crash/Control_2014-01-13-111838_Lynns-iPad3.crash /Users/angel/Desktop/crash/Control.app.dSYM > Control_symbol.crash

  • 这时候终端有可能会出现:

  • 1 Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 69.

  • 输入命令:

  • 1 export DEVELOPER_DIR="/Applications//Contents/Developer"

  • 再执行,这时候终端将会进行处理了

  • 将终端完成以后,在crash文件夹里面会多出一个文件Control_symbol.crash:这个就是最终的文件,可以查看bug所在的位置。

因为上面的崩溃日志看不懂查看都是十六进制的地址不知道是哪里出了问题

分析需要用到在上传应用时的.app 和。dSYM 文件。切记每次发送新版本都要保留这两个文件,不然没有办法解析Crash Log的,可以先把这两个文件拷贝到桌面的某一个文件夹中,然后把.crash的文件也拷贝的同一个文件夹下。

这一解析过程需要使用Symbolicatecrash来完成,首先要找到Symbolicatecrash文件

Symbolicatecrash文件独立于Xcode,可以拷到刚才放crash log的文件夹中使用,在开始解析之前需要先进行一些校验:

  1. 查看xx.app文件的uuid的方法,在命令行中输入:

这时候发现之前的一些行首为项目名 后面的地址变成的方面调用+行号。

解析ios Crash Log(根据地址解析内容)

此外,还可以在上面三个uuid对应的情况下解析某一个地址的内容

  1. 第二种相对复杂一点,涉及到地址的偏移,使用首先查看起始地址,即使每次iOS app启动都会加载(main module)主模块在不同的内存地址,但是dSYM文件假设你的main module加载在地址0x1000(大多数情况是这个,也有0x4000的)。

获取此地址的方法:

看到vmaddr显示为0x00004000。

然后查看  "TestTransform 0x00057132 0x4f000 + 33074" 使用 起始地址+地址偏移量如:

0x4000 + 33074 = 0xc132 (注意:前面为前面为16进制后面为10进制相加,要都转换成10进制相加,把得出的结果转换成16进制)

就可以使用 "0xc132" 地址查看内容了

最后,如果想要方便一点的方法比如:使用Keymob助手可以简化崩溃日志的分析流程,它支持自动符号化和过滤功能,让开发者更专注于问题定位。

相关推荐
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于Spring Boot的社区养老服务管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
nbsaas-boot2 小时前
slice / map 在 Go GC 与内存碎片上的真实成本
开发语言·后端·golang
数据小馒头2 小时前
拒绝循环写库:MySQL 批量插入、Upsert 与跨表更新的高效写法
后端
子洋2 小时前
基于远程开发的大型前端项目实践
运维·前端·后端
sheji34162 小时前
【开题答辩全过程】以 基于spring boot的停车管理系统为例,包含答辩的问题和答案
java·spring boot·后端
源代码•宸3 小时前
Leetcode—1266. 访问所有点的最小时间【简单】
开发语言·后端·算法·leetcode·职场和发展·golang
中年程序员一枚3 小时前
多数据源的springboot进行动态连接方案
java·spring boot·后端
w***76553 小时前
SpringBoot集成MQTT客户端
java·spring boot·后端
HABuo3 小时前
【Linux进程(五)】进程地址空间深入剖析-->虚拟地址、物理地址、逻辑地址的区分
linux·运维·服务器·c语言·c++·后端·centos