图1:Xcode变量区显示卡顿转菊花,测试使用Xcode13.3和下文提到的复现Demo
这部分AppleTeam迟迟不优化的原因在于,Apple公司的内部项目和外部项目开发模式的巨大差异。Apple内部产品,如系统应用,系统库,会直接内嵌到iOS固件中,并直接受益于dyldsharedcache(参考WWDC-AppStartupTime:Past,Present,andFuture[1])来提升加载速度。这意味着他们通常会将一个App,拆分为一个薄的主二进制,搭载以相当多的动态链接库(DynamicFramework),以及插件(PlugIn)的模式来进行开发。举个例子,我们以iOS的消息App(MobileSMS.app)为例子,使用iOS15.4模拟器测试。可以看到其主二进制大小仅有KB(x86_64架构)。通过otool-L查询链接,可以看到总计动态链接了22个动态链接库,其中有9个是非公开的,大都是支撑消息App的功能库,这些库占据了大量存储。图2:消息App的动态链接库列表而iOS平台的第三方开发者的工程,为了追求更快的冷启动时长,由于没有了dyldsharedcache的优化(dyld3提出的启动闭包只能优化非冷启动),很多项目会使用尽量少的动态链接库。加之开源社区的CocoaPods,Carthage,SwiftPM等包管理器的盛行导致的SwiftModule爆炸增长,预二进制的Framework/XCFramework包装格式的滥用,加之闭源三方公司的SDK的集成,最终形成了一个无论是体积还是符号量都非常巨大的主二进制,以及相当长的SearchPaths。
以公司内飞书应用的内测版为例子,在使用Debug,Onone模式编译,不剥离(Strip)任何符号情况下,可以看到其主二进制大小为1.1GB,动态链接库数量为,但是仅包含Apple的系统库和Swift标准库。业务代码以静态链接库集成。
图3:公司飞书应用的动态链接库列表上述这两种不同的工程结构,带来了非常显著的调试体验的差异,并且Apple公司近年来的XcodeTeam和DebuggerTeam优化,并没有完全考虑部分第三方开发者常使用的,厚主二进制下的工程结构。
PS:理论上可以通过业务的工程结构的改造,在本地开发模式下,使用一个动态链接库包裹基础静态链接库的方式,减少主二进制大小(也会减少后续提到的DWARF搜索的耗时),但是大型项目推进工程结构的改造会是一个非常漫长的过程。图4:一种减少主二进制大小的工程结构设计
解决方案:自定义LLDB工具链经过调研,我们发现业界常见做法,无外乎这几种思路:
工程改造:缩减SwiftModule/SearchPath数量:可行,但是收益较低,且不可能无限制缩减
通过LLDB一些开关:可行,但是内网测试下依旧达不到理想的调试状态
我们致力于在字节跳动的移动端提供基础能力支持,因此提出了一套解决方案,不依赖业务工程结构的改造,而是从LLDB工具链上入手,提供定向的调试性能优化。
调研期间也确认到,借助自定义LLDB工具链,集成到XcodeIDE是完全可行的,包括iPhone模拟器、真机以及Mac应用。
图5:自定义LLDB工具链的文件结构,系列后续文章会单独讲解,这里不展开
而LLVM/LLDB本身的工具链代码,在Apple的开源范畴之内(仓库