LuaProfiler for Slua in unity Profiler

Author Avatar
go3k 4月 20, 2018

Unity Profiler是非常方便的性能分析工具,但是默认只能显示有限的C#部分接口的调用情况。实际项目有大量lua代码,如果不能显示lua代码的执行情况,那分析会显得非常有限。

Unity提供了自定义Profiler Sampler的API:

Profiler.BeginSample(string samplename);
Profiler.EndSample();

我们只需要成对的调用,即可在Profiler中显示名为samplename的调用信息。那么我们只需要把这两个接口导出到lua,并在lua代码中进行调用就可以分析lua代码了。

可是手动增加分析代码有缺点,代码量大,修改那么多的函数会比较累。函数有多个return分支时可能出错。很难覆盖全面,可能忽略了真正的性能瓶颈。

debug.sethook

如何自动对所有函数调用增加分析代码?主要用到两个接口:

  1. debug.sethook可以为lua函数调用设置一个钩子函数,每当函数调用、返回时会触发钩子函数。
  2. debug.getinfo可以获取函数调用栈上的函数信息。

更多的函数细节查看官方文档

理论上我们只需要在钩子函数的call事件时获取函数信息调用BeginSamplereturn事件时调用EndSample就大功告成了。

但是有这样一个问题,在lua 5.1或者是lua-jit情况下,callreturn事件不是一一对应的,return少了很多,造成无法正确分析。原因是这两中情况下,当有尾调用发生时,主函数就不会有return事件了,有些lua版本会有tail return事件,但是lua-jit下因为优化的原因都是两个call一个return

尾调用时,主调函数的站信息被直接替换成尾调用了。

解决办法就是用一个栈s,记录函数调用时的调用栈深度,在call时push当前栈深度;在return时,把s上调用深度小于等于当前深度的调用都pop掉,这样push和pop就是一一对应的了。

另外,没有找到直接返回调用栈深度的接口,通过下面的方式计算。

local function GetStackDepth()
local depth = 0
while true do
    if not debug.getinfo(3 + depth) then
        break
    end
    depth = depth + 1
end
return depth
end