0%

调试技巧之获取所有Message send

调试技巧之获取所有Message send

在查 bug 的时候有时候会希望获取方法调用顺序,今天忽然发现了一个新大陆

在 gdb 断点调用

1
call (void)instrumentObjcMessageSends(YES)

或者在代码段中插入

1
(void)instrumentObjcMessageSends(YES);

故此总结一下其他相关的获取调用顺序大概的方案

断点人肉分析

这个就不详细解释了

打 Log

打 Log 几乎是最常用的方案了,轻量,快捷。

策略也可以分几种

  1. 在原有代码段中插入
  2. 利用断点添加 Log
  3. AOP 插入Log(用一些第三方库比如 Aspect 可以实现)

Instrument

因为 TimeProfile 是利用 dtrace 采样去实现的,所以并不能获取全量的数据,会有一定数据的丢失,不过已经能解决很多问题了

Hook Message Send

利用 fishhook 可以实现对 message send 替换。但是目前找不到对 x86 架构的支持,并且需要自己写汇编。具体使用起来也比较麻烦。

自定义 message send

  1. 保存寄存器状态、执行 start 切片代码、恢复寄存器状态
  2. 调用原始实现
  3. 保存寄存器状态、执行 end 切片代码、恢复寄存器状态

目前成熟一点的实践

  1. 用结构体抽象:类名,cmd,开始时间,调用时长
1
2
3
4
5
6
typedef struct {
void *cls;
char *cmd;
double start_time;
double duration;
}msgsend_trace_t;
  1. 因为方法的调用是先进后出的,每个线程都用一个栈保存其的调用栈顺序。每次方法调用结束的时候根据开始时候记录的信息计算出 msgsend_trace_t,并且保存在数组中
  2. 信息持久化
  3. 调用栈图形可视化

总结

  1. 几乎可以获取所有信息
  2. 整套方案需要实现的东西较多,网上目前没有完整的实现方案贡献,只有自己实现
  3. 图形可视化帮助了解调用的包含顺序
  4. 有明确的调用时间顺序,当 Instrument 没法解决的问题的时候可以尝试用这套方案