# iOS Crashes 分析

3831603266272_.pic.jpg

iOS 崩溃原因

  1. CPU resource Limit CPU 占用率过高
  2. 触发watch dog 主线程超过20s未响应
  3. Memory limit exceeded内存超出上限
  4. Memory pressure exit : JatSam
  5. background task timeout

崩溃原因解析

  1. CPU resource limit
    应用程序处理任务时间过长,系统可能会终止程序执行,建议使用 iOS 13 BGProcessingTask

  2. watch dog
    出现场景: 应用启动或者重新进入前台
    可能原因:

  • dead lock
  • 无限循环
  • 主线程无法完成的任务
    注意:处于调试下是不会出现 Watchdog 的
  1. Memory limit exceeded
  2. JatSam

在 Linux 系统中,交换空间(swap space)可以用来存放内存中不常用的临时数据。而 iOS 系统因为闪存容量和读写寿命的原因,并没有引入交换空间,取而代之的是 Compressed memory 技术,即当内存紧张时,压缩一些内存内容,并在需要时解压。但副作用是会造成较高的 CPU 占用甚至卡顿,手机耗电量也会随之增加。

为了解决上面的问题,苹果设计了 Jetsam 机制。其工作方式是当内存不足时,系统会通知前台应用去释放内存(通过 applicationDidReceiveMemoryWarning 方法和 UIApplicationDidReceiveMemoryWarningNotification 通知),如果内存压力依然存在,将会终止一些后台应用。最终内存还不够的话,就会终止当前应用(FOOM),并且上报日志。

iOS dsym和link-map文件分析

/Users/sunxx/Library/Developer/Xcode/Archives/2020-10-20/xxxPM.xcarchive/dSYMs/AFNetworking.framework.dSYM/Contents/Resources/DWARF/AFNetworking

使用atos解析crash 文件

atos -arch arm64 -o /Users/sun/Library/Developer/Xcode/Archives/xx.xcarchive/dSYMs/AFNetworking.framework.dSYM/Contents/Resources/DWARF/AFNetworking -l 0x1000e4000 0x00000001000effdc

-[xxxxviewController runContenxt:]

  • -arch:指令集,现在release的都包含arm64指令集架构。可以通过atool -hv appname查看相关的指令集;
  • -o :目标文件:可执行文件。
  • -l : load address,及发生crash对应的镜像起始地址;后面一个地址是symblicate address,及符号地址。

使用linkmap 查看二进制文件信息

理解Link Map File,可以帮助我们:

  • 理解链接过程
  • 理解Crash时,通过Symbols还原出源码的机制
  • 理解内存分段、分区
  • 分析可执行文件中哪个类或库占用比较大,进行安装包瘦身
生成link-map 文件

Xcode 开启编译选项:设置 Write Link Map fileYES

截屏2020-10-21 上午9.51.46.png

查看路径:~/Library/Developer/Xcode/DerivedData/XXX-

  • __text表示编译后的程序执行语句
  • __data表示已初始化的全局变量和局部静态变量
  • __bss表示未初始化的全局变量和局部静态变量
  • __cstring表示代码里的字符串常量

其中一些常用的信息如下:

//  __TEXT段中的一些Section
1. __text: 代码节,存放机器编译后的代码
2. __stubs: 用于辅助做动态链接代码(dyld).
3. __stub_helper:用于辅助做动态链接(dyld).
4. __objc_methname:objc的方法名称
5. __cstring:代码运行中包含的字符串常量,比如代码中定义`#define kGeTuiPushAESKey        @"DWE2#@e2!"`,那DWE2#@e2!会存在这个区里。
6. __objc_classname:objc类名
7. __objc_methtype:objc方法类型
8. __ustring:
9. __gcc_except_tab:
10. __const:存储const修饰的常量
11. __dof_RACSignal:
12. __dof_RACCompou:
13. __unwind_info:
// __DATA段中的一些Section
1. __got:存储引用符号的实际地址,类似于动态符号表
2. __la_symbol_ptr:lazy symbol pointers。懒加载的函数指针地址。和__stubs和stub_helper配合使用。具体原理暂留。
3. __mod_init_func:模块初始化的方法。
4. __const:存储constant常量的数据。比如使用extern导出的const修饰的常量。
5. __cfstring:使用Core Foundation字符串
6. __objc_classlist:objc类列表,保存类信息,映射了__objc_data的地址
7. __objc_nlclslist:Objective-C 的 +load 函数列表,比 __mod_init_func 更早执行。
8. __objc_catlist: categories
9. __objc_nlcatlist:Objective-C 的categories的 +load函数列表。
10. __objc_protolist:objc协议列表
11. __objc_imageinfo:objc镜像信息
12. __objc_const:objc常量。保存objc_classdata结构体数据。用于映射类相关数据的地址,比如类名,方法名等。
13. __objc_selrefs:引用到的objc方法
14. __objc_protorefs:引用到的objc协议
15. __objc_classrefs:引用到的objc类
16. __objc_superrefs:objc超类引用
17. __objc_ivar:objc ivar指针,存储属性。
18. __objc_data:objc的数据。用于保存类需要的数据。最主要的内容是映射__objc_const地址,用于找到类的相关数据。
19. __data:暂时没理解,从日志看存放了协议和一些固定了地址(已经初始化)的静态量。
20. __bss:存储未初始化的静态量。比如:`static NSThread *_networkRequestThread = nil;`其中这里面的size表示应用运行占用的内存,不是实际的占用空间。所以计算大小的时候应该去掉这部分数据。
21. __common:存储导出的全局的数据。类似于static,但是没有用static修饰。比如KSCrash里面`NSDictionary* g_registerOrders;`, g_registerOrders就存储在__common里面
0x101AE43A0 0x00000018  [699] __OBJC_$_PROTOCOL_REFS_PLPlayerDelegate
0x101AE43B8 0x00000128  [699] __OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_PLPlayerDelegate
0x101AE44E0 0x00000060  [699] __OBJC_$_PROTOCOL_METHOD_TYPES_PLPlayerDelegate

再看一段编译的代码

0x10181373D 0x0000008D  [699] literal string: ^{AudioBufferList=I[1{AudioBuffer=II^v}]}88@0:8@16^{AudioBufferList=I[1{AudioBuffer=II^v}]}24{AudioStreamBasicDescription=dIIIIIIII}32q72q80
0x1018137CA 0x00000011  [699] literal string: v32@0:8@16i24i28
0x1018137DB 0x00000015  [699] literal string: v24@0:8@"PLPlayer"16
0x1018137F0 0x00000018  [699] literal string: v32@0:8@"PLPlayer"16q24
0x101813808 0x00000021  [699] literal string: v32@0:8@"PLPlayer"16@"NSError"24
0x101813829 0x0000001F  [699] literal string: v48@0:8@"PLPlayer"16{?=qiIq}24
0x101813848 0x0000002E  [699] literal string: v48@0:8@"PLPlayer"16^{__CVBuffer=}24q32i40i44
0x101813876 0x00000097  [699] literal string: ^{AudioBufferList=I[1{AudioBuffer=II^v}]}88@0:8@"PLPlayer"16^{AudioBufferList=I[1{AudioBuffer=II^v}]}24{AudioStreamBasicDescription=dIIIIIIII}32q72q80
0x10181390D 0x00000020  [699] literal string: v32@0:8@"PLPlayer"16@"NSData"24
0x10181392D 0x00000023  [699] literal string: v40@0:8@"PLPlayer"16@"NSData"24q32
0x101813950 0x0000001B  [699] literal string: v32@0:8@"PLPlayer"16i24i28
0x10181396B 0x00000018  [699] literal string: v28@0:8@"PLPlayer"16B24
0x101813983 0x00000011  [699] literal string: v36@0:8@16i24@28
0x101813994 0x0000001C  [699] literal string: v24@0:8@"SuperPlayerView"16
0x1018139B0 0x0000002C  [699] literal string: v36@0:8@"SuperPlayerView"16i24@"NSString"28
0x1018139DC 0x0000000C  [699] literal string: @"PLPlayer"

由此可以推断,Xcode 在链接动态库的时候只是在使用动态库文件的位置添加了一个描述符而已

lipo 使用

  1. 查看库信息lipo -info xxx.framework/xxx
  2. 合并静态库
    lipo -create xxx1.a xxx2.a -output xxxnew
lipo -create xxx1.framework/xxx1  xxx2.framework/xxx2 -output xxxnew.framework/xxxnew
  1. 移除指定架构
lipo 静态库路径 -remove 架构名 -output 存放路径
  1. 静态库拆分
lipo 静态库源路径 -thin 架构名 -output 存放路径

你可能感兴趣的:(# iOS Crashes 分析)