作者:李卓立 仲凯宁
背景介绍
在《字节跳动 DanceCC 工具链系列之Swift 调试性能的优化方案》[1]一文中,我们介绍了如何使用自定义的工具链,来针对性优化调试器的性能,解决大型Swift项目的调试痛点。
在经过内部项目的接入以及一段时间的试用之后,为了精确测量经过优化后的LLDB调试Xcode项目效率提升效果,衡量项目收益,需要开发一套能够同时获取Xcode官方工具链与DanceCC工具链调试耗时的耗时监控方案。
一般来说,LLDB内置的工作耗时,可以通过输入log timers dump
来获取粗略的累计耗时,但是这个耗时只包括了源代码中插入了LLDB_SCOPED_TIMER()
宏的函数,并不代表完整的真实耗时。并且这个耗时统计需要用户手动触发,如果要单独获取某次操作的耗时还需要先进行reset操作清空之前的耗时记录;对于我们目前的需求而言不够精确也不够自动。
因此DanceCC提出了一套专门的方案。方案原理基于LLDB Plugin[2],利用Fishhook[3],从LLDB的Script Bridge API[4]层面拦截Xcode对LLDB调用,以此来进行耗时监控统计。
注:LLDB论坛也有贡献者,讨论另一套内置的LLDB metries方案[5],但是目标侧重点和我们略有不同,并且截至发稿日未有完整的结论,因此仅在引用链接提及供读者延伸阅读。
方案原理
LLDB Plugin
Apple在其LLDB和早期Xcode集成中,为了不侵入一些容易改动的上层逻辑,引入了LLDB Plugin的设计和支持。
每个Plugin是一个动态链接库,需要实现特定的C++/C入口函数,由LLDB主进程在运行时通过dladdr
找到函数入口并加载进内存。目前有两种Plugin的接口形式(网上常见第一种)
- 新Plugin接口:
namespace lldb {
bool PluginInitialize(SBDebugger debugger);
}
这种Plugin,需要用户在脚本中手动按需加载,并常驻在内存中:
plugin load /path/to/plugin.dylib
- 老Plugin接口:
extern "C" bool LLDBPluginInitialize(void);
extern "C" void LLDBPluginTerminate(void);
将编译的动态库放入以下两个目录,即可自动被加载,无法手动控制时机,在当前调试Session结束时卸载:
/path/to/LLDB.framework/Resources/Plugins
~/Library/Application Support/LLDB/PlugIns