iOS App启动优化(五):收集符号 && 生成 Order File

4,987 阅读3分钟

iOS App启动优化(一):检测启动时间

iOS App启动优化(二):物理内存和虚拟内存

iOS App启动优化(三):二进制重排

iOS App启动优化(四):编译期插桩 && 获取方法符号

iOS App启动优化(五):收集符号 && 生成 Order File

iOS App启动优化(六):实用党直接看这里

前言

没看过前四篇的胖友们不用担心,在这里直接贴上 demo 的当前主要代码

主要做了以下几件事情:

  • 声明了一个block、一个c函数func()、一个oc函数-(void)test、一个Swift编写的含有类方法swiftTest的类
  • touchesBegan:withEvent: 方法中调用了-(void)test-(void)test里面把上述方法嵌套调用,最终会打印字符串test
  • 添加编译期插桩的相关方法 __sanitizer_cov_trace_pc_guard_init__sanitizer_cov_trace_pc_guard
  • 通过编译期插桩的相关方法获取到方法符号等信息,详见代码

收集符号

Focus 1 多线程

启动的相关方法可能在不同的线程执行,如果我们用一个数组直接收集这些符号,会出现线程问题。

听到多线程问题立马想到锁,但是这里因为锁耗费性能比较多所以不推荐使用。建议使用原子队列解决这个问题。

原子队列是栈结构,通过 队列结构 + 原子性 保证顺序。

导入头文件

#import <libkern/OSAtomic.h>

启动的时候方法执行,__sanitizer_cov_trace_pc_guard 获取到的 PC 会作为结构体 Node 的成员变量以链表的形式存储下来。

当我们点击屏幕时,touchesBegan:withEvent: 里面会倒序取出节点,通过节点内的成员变量 pc 生成 Dl_info,再从 info 中读取方法符号。通过去重判断后插入到方法符号数组arr的头部,使最终记录的方法执行顺序是正序。

执行一下代码看看

这时候尴尬了,一直在-[ViewController touchesBegan:withEvent:]里死循环。

Focus 2 处理While循环

断点看汇编

__sanitizer_cov_trace_pc_guard 竟然在反复横跳?

文档里说 __sanitizer_cov_trace_pc_guard 会在每个边缘级别插入,那么每执行一次 while 循环应该算是一次边界!

解决方案: 修改为只 hook 函数, Target -> Build Setting -> Custom Complier Flags -> Other C Flags 修改为 -fsanitize-coverage=func,trace-pc-guard

再次运行代码

这个时候,我们已经获取到了正确的方法符号。

Focus 3 处理load函数

在当前类添加 load 方法后执行看输出,发现 load 并没有被打印。

load 方法调用时插入的 __sanitizer_cov_trace_pc_guard 参数 guard 为0,默认的函数实现会直接return,导致无法捕获到 load

屏蔽掉 __sanitizer_cov_trace_pc_guard 中的 if (!*guard) return; 即可

成功收集到了启动相关的所有方法符号。

Focus 3 缺失的符号处理

我们看到输出的函数符号里面缺少了之前声明的函数(testfuncswiftTest)和 block,这是因为我没有去调用他们。

只有被调用的函数才会被__sanitizer_cov_trace_pc_guard捕获

viewDidLoad 里面调用一下

Focus 4 处理c函数block的符号

生成 Order File 前还需要对 c函数block 做特殊处理。

objc-750order file 看到这两种符号都是以下划线 "_" 开头的。而我们获取到的是没有下划线的,所以要拼接上去。

生成 Order File

生成 Order File 就是把上面加工好的方法符号集合拼接成字符串并写入文件。

记得移除掉点击触发的touchesBegan:withEvent:的符号。

看一下我们的 Order File

完美~ 只要把 Order File 放入工程目录并设置好路径,就完完整整的实现了启动优化~

完结撒花~ 🎉🎉🎉🎉🎉🎉


不爱原理,直接开干的胖友们

看优化效果对比的胖友们

请移步 iOS App启动优化(六):实用党直接看这里