101

手淘架构组最新实践:iOS 基于静态库插桩的二进制重排启动优化

 4 years ago
source link: https://www.infoq.cn/article/ot9IwCf7fkjEOEqZlWGL
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

背景

近期抖音和 Facebook 分享了自己通过二进制重排优化启动时间的方案,手淘 iOS 架构团队也对二进制重排进行了研究,由于手淘工程模块已经二进制化,因此实现了一套基于静态库插桩的重排方案。

APP 启动 和 PageFault

当我们向操作系统申请内存时,操作系统并不是直接分配给我们物理内存,而是只标记当前进程拥有该段内存,当真正使用这段内存时才会分配。这种延迟分配物理内存的方式就通过 page fault 机制来实现的。当我们访问一个内存地址时,如果该地址非法,或者我们对其没有访问权限,或者该地址对应的物理内存还未分配, cpu 都会生成一个 page fault ,进而执行操作系统的 page fault handler 。如果是因为还未分配物理内存,操作系统会立即分配物理内存给当前进程,然后重试产生这个 page fault 的内存访问指令。

ZfQryey.png!web

App 在启动时,需要执行各种函数,我们需要读取 TEXT 段代码到物理内存中,这个过程会发生缺⻚中断,由于启动时所需要执行的代码分布在 TEXT 段的各个部分,会读取很多⻚面,导致启动时 Page Fault 数量非常多。与直接访问物理内存不同, page fault 过程大部分是由软件完成的,消耗时间比较久,所以是影响启动性能的一个关键指标。

例如下图中,手淘启动时首先的调用的几个方法 会分布在虚拟内存的各个⻚面中, 执行这些方法时,需要从读取到物理内容中,就会产生多次 page fault 。

如果能将启动阶段需要的读取代码集中排布,将这些方法全都放到相邻的区域中,我们读取这些方法可能就只需要极少的 page fault 次数。可以减少不必要的 page fault 时间。达到优化启动时间的效果。

重排前后的函数在页面的布局对比:

zUjAbeE.png!web

重排方案

如何获取方法的执行顺序

为了生成 order_file , 我们需要确定应用启动时方法的执行顺序。之前抖音和 facebook 都分享过自己的方案,在实际操作的过程中,我们发现抖音和 facebook 的方案并不适用于手淘。

抖音通过静态扫描和运行时 Trace 等方法确定 order_file,该方案无法覆盖 initialize、block 和 C++ 通过寄存器的间接函数调用静态扫描不出来调用。

facebook 分享过通过 llvm 插桩的确定 order_file 的方案,需要使用源码重新打包。由于手淘几乎全是已经编译好的二进制模块,在手淘使用该方案不现实。

只能想其他办法…

手淘之前已经做过 pod 预编译,我和师兄念纪想到了是否可以通过在汇编层面对 pod 编译后的静态库进行插桩。在启动时,插桩后的方法都会调用记录方法,从而获得启动方法的执行顺序。在参考了离青对汇编插桩的研究后,确定了静态库插桩的实现方案。

静态库插桩

我们编译过的静态库由 .o 文件组成,我们可以对 .o 中的函数代码进行修改,在每个函数的开头插入调用我们指定记录函数的指令。

举个例子:

插入前 -[MyApp window]: 的汇编代码

复制代码

-[MyApp window]:
0000000000002d88adrpx8,#0x
0000000000002d8cldrswx8, [x8,#0xf18]
; 0x2f18@PAGEOFF, _OBJC_IVAR_$_MyApp._window
0000000000002d90ldrx0, [x0,x8]
0000000000002d94ret

插入后的 汇编代码,可以看到 增加了跳转到 _record_method 的指令,并且补上了 prologue 和 epilogue 。

复制代码

-[MyApp window]:
0000000000002ebc stpx29,x30, [sp, #-0x10]!
0000000000002ec0movx29, sp
0000000000002ec4bl _record_method
0000000000002ec8ldpx29,x30, [sp],#0x
0000000000002ecc adrpx8,#0x
0000000000002ed0ldrswx8, [x8,#0xc0]
0000000000002ed4ldrx0, [x0,x8]
0000000000002ed8ret

生成 order file

linkmap 记录了连接过程中的相关信息。其中包含链接用到的 symbol 相关的信息。通过 pc address 减去 slide 得到的地址,我们可以在 linkmap 中找到对应的 symbol .

复制代码

address=pc- slide. // 因为 ASLR, APP 可执行文件随机载入的原因,需要处理一下偏移
量。

我们需要将之前记录的地址转换成对应的符号,为了真实还原线上的执行环境,我们只是在 app 中简单地的记录了 pc 地址 和 Image 的偏移量。通过解析 linkmap ,获取函数的地址区间, 得到距离 address 最近的 symbol ,生成 order_file 。

linkmap 文件:

复制代码

# Symbols:
# Address Size File Name
0x1000016300x00000039[2]-[ViewControllerviewDidLoad]
0x1000016700x00000092[3]_main
0x1000017100x00000080[4]-[AppDelegateapplication:didFinishLaunchingWithOptions:]
0x1000017900x00000040[4]-[AppDelegateapplicationWillResignActive:]
0x1000017D00x00000040[4]-[AppDelegateapplicationDidEnterBackground:]
0x1000018100x00000040[4]-[AppDelegateapplicationWillEnterForeground:]
0x1000018500x00000040[4]-[AppDelegateapplicationDidBecomeActive:]
0x1000018900x00000040[4]-[AppDelegateapplicationWillTerminate:]

更改符号的排列顺序

默认情况下, ld 链接器会按照链接的顺序将各个 .o 文件的数据重新布局生成可执行文件。ld 链接器提供 -order-file 选项操控数据排列的顺序。在 Xcode 中可以通过 Order File 选项指定符号排序文件。

复制代码

//Order file 内容例子:
+[xxxxx1 load]
+[xxxxx2 swizzleResumeAndSuspendMethodForClass:]
+[xxxxx3 load]
+[xxxxx4 initialize]___
+[xxxxx5 initialize]_block_invoke
+[xxxxx6 initialize]___
+[xxxxx7 initialize]_block_invoke
...

优化效果

通过精准的启动函数重排,最后重排效果还是很可观的,在 iPhone6 上优化了 400ms 的启动时间。

参考

感谢抖音团队和 Facebook 团队提供优化新思路

抖音研发实践:基于二进制文件重排的解决方案 APP 启动速度提升超 15%

Improving iOS Startup Performance with Binary Layout Optimizations

https://atscaleconference.com/videos/performance-scale-improving-ios-startup-performance-with-binary-layout-optimizations/

Linux 下 Page Fault 的处理流程 https://cloud.tencent.com/developer/article/1459526

本文转载自公众号淘系技术(ID:AlibabaMTT)。

原文链接:

https://mp.weixin.qq.com/s?__biz=MzAxNDEwNjk5OQ==&mid=2650405083&idx=1&sn=f52e8dcf03faafcf910d58add9904550&chksm=839530c3b4e2b9d5cab0e84da1a0d6c210ed7fb2fc4a66d01213fdfb2fcffbbe332c87b8eb76&scene=27#wechat_redirect


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK