之前搞过一下 systemtap(systemtap 折腾笔记 ),可惜很快琐事缠身,没有继续搞下去。最近偷空搞了一下,有点意思。

章大大的思路,是用 perl 生成 systemtap 脚本,从 /proc/$pid/maps 里面扫出 liblua.so 的地址。然后 hook 这个地址里,以 luaL_* 和 lua_* 开头的 lua 虚拟机 C 函数。hook 住的函数里,都会有个参数 L,这个 L 表示 lua 的虚拟机。接下来就是按 lua 的 C 代码撸一遍,看看怎么从 L 的内存结构里,dump 出整个 lua 的运行栈。相当于在 systemtap 里,重新实现一遍官方的 debug.traceback。

换到 skynet 里实现,有几个不同点。首先,skynet 是把 lua 静态编译进去的,所以无法找到 liblua.so。其实这样问题更简单了,不需要扫 so 的地址,只要 hook 住 skynet 里的相关函数,一样可以拿到 L 的地址。其次,是 skynet 用的是魔改版的 lua,所以 proto 的位置与官方版有点不同,而且现在用的是 lua53 了,跟 openresty 的 lua51 比还是有差异的。我实现了一版给 skynet 用的 lua-bt,参见 skynet_systemtap_set 仓库里的 mini_lua_bt.stp

做出来的效果就是上图这样。因为没有用户登录,所以只有一个 exchange 服务在跑。这个服务是用于和平台通讯的,所以会定时处理一些消息。

目前还有点小问题,有时候同一个栈位置会打印两次,还没找到原因。另一个是目前打印的是文件名和行数,没有函数名直观。但是 lua 的函数其实只是一个指针,可以给这个指针绑定不同的名字,都是同一个函数,而且代码里还有大量的匿名函数,所以不好搞出来函数名。另外,从 skynet 的结构来说,其实可以找到更接近业务层的点。skynet 底层是 socket-server,然后用 sn-lua 跑 lua 服务,再用 lua-skynet.c 为 lua 服务提供 skynet 的接口。hook 住 lua-skynet 的 lcallback,应该就能看到 skynet 调 lua 业务的东西了。不过这个还没搞好

接下来再慢慢补充一下其他类型的脚本。。