iOS Linux下的Crash解析服务

  1. 什么是dSYM
  2. 符号化方式
  3. crash 日志的信息解析
  4. 解析服务的性能优化
  5. 解析扩展

什么是dSYM

一个目录,其中包含了一个格式为DWARF的文件。

DWARF: 它是可执行程序与源代码关系的一个紧凑的表示,以一个对于调试器的处理合理有效的方式。

大多数现代编程语言是块结构的:每个实体(例如,一个类定义或一个函数)被包含在另一个实体中。在一个C程序里,每个文件可能包含多个数据定义、多个变量定义,及多个函数。DWARF遵循这个模型,它也是块结构的。在DWARF里基本的描述项是调试信息项(DebuggingInformation Entry——DIE)。一个DIE有一个标签,它指明了这个DIE描述什么及一个填入了细节并进一步描述该项的属性列表。一个DIE(除了最顶层的)被一个父DIE包含(或者说拥有),并可能有兄弟DIE或子DIE。属性可能包含各种值:常量(比如一个函数名),变量(比如一个函数的起始地址),或对另一个DIE的引用(比如一个函数的返回值类型)。

组成 DWARF 数据的不同 DWARF 部分如下:

数据列 信息说明
.debug_loc 在 DW_AT_location 属性中使用的位置列表
.debug_macinfo 宏信息
.debug_pubnames 全局对象和函数的查找表
.debug_pubtypes 全球类型的查找表
.debug_ranges 在 DW_AT_ranges 属性中使用的地址范围
.debug_str 在 .debug_info 中使用的字符串表
.debug_types 类型描述

下面的列表显示了在调试应用程序时应该关注的标记

数据列 信息说明
DW_TAG_class_type 表示类名称和类型信息
DW_TAG_structure_type 表示结构名称和类型信息
DW_TAG_union_type 表示联合名称和类型信息
DW_TAG_enumeration_type 表示联合名称和类型信息
DW_TAG_typedef 表示 typedef 名称和类型信息
DW_TAG_array_type 表示数组名称和类型信息
DW_TAG_subrange_type 表示数组大小信息
DW_TAG_inheritance 表示继承的类名称和类型信息
DW_TAG_member 表示类的成员
DW_TAG_subprogram 表示函数名称信息
DW_TAG_formal_parameter 表示函数参数的信息
DW_AT_name 表示名称字符串
DW_AT_type 表示类型信息
DW_AT_artificial 在创建时由编译程序设置
DW_AT_sibling 表示兄弟位置信息
DW_AT_data_member_location 表示位置信息
DW_AT_virtuality 在虚拟时设置

iOS 中.app和.dSYM成对出现,并且 者有相同的UUID,以标识是 同 次编译的产物。dwarfdump --uuid dsym得到的是MainImage的uuid

符号化方式

  1. Xcode 自带的符号化
  2. symbolicate脚本 symbolicatecrash app.crash app.dSYM
  3. atos 命令符号化 atos -o dSYM路径 -l 模块load地址 -arch cpu指令集种类 调用方法的地址
  4. 自己实现 addr to {file,name,line}的工具 atosl

前三种都面临依赖macos环境和性能较弱的问题,无法支撑上亿用户的crash解析服务。(从服务器费用和并发量)所以只能借助dwarf的相关知识,自己在linux实现一套解析服务集群部署。

crash 日志的信息解析

我们采用的是和apple一样的crash日志格式,其中有一些信息需要通过正则提取出来,进行符号化解析,比如:

调用栈
0 CoreFoundation 0x0000000182947164 0x182804000 + 1323364 (<redacted> + 1323364)

Image
0x1004e8000 - 0x1054b7fff +NewsInHouse arm64 <b13673136b3a36778718d8ce5ae5313e> /var/containers/Bundle/Application/D1CFB9B4-FE73-4823-A387-41FD99167E78/NewsInHouse.app/NewsInHouse

理解了这些,就可以自己重写系统的symbolicatecrash, 加上缓存后效率大增。

解析服务的性能优化

  1. 缓存(提前缓存,过程中缓存)
  2. 堆机器(linux可以自己实现一个atos)

我在linux上自己实现了一个atos, 性能大概是原生的atos的10倍。了解Dwarf的结构其实就很容易实现出来。在atos的外部再搭建redis缓存已经解析过得符号,更进一步提升了解析效率。

现在我们解析crash.log的响应时间(包括生成结果)控制在100ms以内。

解析扩展

可能存在ANR,OOM, Crash等各种各样的日志,所以我们需要在端上统一日志格式,在服务端统一解析。

script>