调试技巧之获取所有Message send
在查 bug 的时候有时候会希望获取方法调用顺序,今天忽然发现了一个新大陆
在 gdb 断点调用
1 | call (void)instrumentObjcMessageSends(YES) |
或者在代码段中插入
1 | (void)instrumentObjcMessageSends(YES); |
故此总结一下其他相关的获取调用顺序大概的方案
断点人肉分析
这个就不详细解释了
打 Log
打 Log 几乎是最常用的方案了,轻量,快捷。
策略也可以分几种
- 在原有代码段中插入
- 利用断点添加 Log
- AOP 插入Log(用一些第三方库比如 Aspect 可以实现)
Instrument
因为 TimeProfile 是利用 dtrace 采样去实现的,所以并不能获取全量的数据,会有一定数据的丢失,不过已经能解决很多问题了
Hook Message Send
利用 fishhook 可以实现对 message send 替换。但是目前找不到对 x86 架构的支持,并且需要自己写汇编。具体使用起来也比较麻烦。
自定义 message send
- 保存寄存器状态、执行 start 切片代码、恢复寄存器状态
- 调用原始实现
- 保存寄存器状态、执行 end 切片代码、恢复寄存器状态
目前成熟一点的实践
- 用结构体抽象:类名,cmd,开始时间,调用时长
1 | typedef struct { |
- 因为方法的调用是先进后出的,每个线程都用一个栈保存其的调用栈顺序。每次方法调用结束的时候根据开始时候记录的信息计算出 msgsend_trace_t,并且保存在数组中
- 信息持久化
- 调用栈图形可视化
总结
- 几乎可以获取所有信息
- 整套方案需要实现的东西较多,网上目前没有完整的实现方案贡献,只有自己实现
- 图形可视化帮助了解调用的包含顺序
- 有明确的调用时间顺序,当 Instrument 没法解决的问题的时候可以尝试用这套方案