最近 子熏同学 搞定了新的抓取火焰图的方法,他觉得改动有点多,不方便开源出来,不过我觉得思路很好,写篇文章记录一下

skynet 自带的魔改版 Lua,即使 Lua 代码在死循环,也能通过 gm 指令让 vm 停下来,看看出事的地方是哪里。这是通过引入 lua_checksig_ 来实现的。在 lvm.c 里,重点的几个 OPCODE: OP_JMP, OP_CALL, OP_TAILCALL, OP_FORLOOP 都会调用 lua_checksig 进行检查,如果设置了开关,就会通过 lua_error 中止运行。新的火焰图也是用类似方法实现,当目标服务检测到开关打开,就会将当前栈帧到 base_ci 的部分抓取下来,序列化成文本,放进一个全局指针里。然后另一个服务就会去读这个指针,将数据序列化到文件里,后续用 flamegraph.pl 进行火焰图的渲染

这里目标服务和检测服务之间没有用平常的消息机制进行通讯,因为死循环的时候已经不会再消费消息了。取而代之的是直接改对应 skynet_context(目标服务)的状态开关,当服务运行到检查点,就会自然将自己的栈抓下来,并将状态改成已抓取。待检测服务读取完后,将状态改成未抓取,至此完成一次栈帧捕获。检测服务可以自定义抓取的频率,定期修改目标服务的状态开关即可。这两个服务的通讯有点 golang 的 channel 的意思,相当于一个大小为 1 的 channel,消费者(检测服务)未消费,则生产者(目标服务)处于阻塞状态,不会继续抓取

本来想集成到线上项目里的,可是最近新项目进度比较忙,一直没时间搞。。。子熏的测试是只有 2% 左右的性能损失,非常高效。这种方法不需要以前 systemtap 那种切到内核空间里去,应该是会高效很多的