我想有效地获取当前的函数调用堆栈,是否有api或sth来执行此操作?
我尝试过这样的方法,但速度太慢了。
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
编辑:
真正的问题是我正在编写一个分析器工具,并在调用和返回事件中使用debug.sethook
做某事。但是在lua5.1或lua-jit情况下,当尾部返回发生时,我得到两个调用事件,只有一个这样的返回事件:
call ------ the 1st call event
call
return
所以我对这个问题的解决方案是获取当前事件的调用堆栈深度,当返回事件深度小于第一个调用事件的深度时,我知道它的尾部返回,然后我可以正确处理它。
但我发现GetStackDepth()
自我花费了很多时间(太慢),这会影响我的探查器结果。
我无法改变lua版本。
答案 0 :(得分:0)
对于尾部调用,您将获得另一个事件(或在激活记录中获得istailcall)。我必须承认我正在使用C API来进行性能分析代码,但是将其转换为本地lua并不会太棘手。尽管如果您遇到性能问题,请注意Roberto Ierusalimschy在 Lua编程,第3版,第24章“调试库”中写道:
出于性能原因,这些基元的正式接口是通过C API。
我有一个主要的C样式调试钩子,可将调用转发给事件处理程序:
void debugHook(lua_State * const ls, lua_Debug * const ar)
{
ASSERT(ls != nullptr);
ASSERT(ar != nullptr);
CallGraphTracker & dbg = getCallGraphTracker();
switch(ar->event)
{
case LUA_HOOKCALL:
dbg.handleEventCall(ls, ar);
break;
case LUA_HOOKTAILCALL:
dbg.handleEventTailCall(ls, ar);
break;
case LUA_HOOKRET:
dbg.handleEventReturn(ls, ar);
break;
default:
SHOULD_NEVER_BE_REACHED();
}
}
就我而言,事件处理程序基于lua函数指针构建调用图; CallGraphTracker具有Node
(一个根节点,另外的根节点被添加为子节点)并跟踪curNode
。 (实际上,我也构建了函数信息,但是为了简化起见,我删除了该代码。显然,节点也可以存储各种其他信息。)
如果相同功能指针(Node::addCall
),行(luaPtr
)和“ tailcall-ness”(ar->currentline
或{的子节点相同,则函数true
仅增加一个数字{1}}已存在,否则将创建新的false
。在这两种情况下,都将返回相关的子节点并将其用作新的currentNode。
Node
您会注意到,在void CallGraphTracker::handleEventCall(lua_State * ls, lua_Debug * ar)
{
// Get function info and transform into simple function pointer.
lua_getinfo(ls, "f", ar);
ASSERT(lua_isfunction(ls, -1));
void const * luaPtr = lua_topointer(ls, -1);
ASSERT(luaPtr != nullptr);
// Add call.
lua_getstack(ls, 1, ar);
lua_getinfo(ls, "l", ar);
curNode = curNode->addCall(luaPtr, ar->currentline, false);
}
void CallGraphTracker::handleEventTailCall(lua_State * ls, lua_Debug * ar)
{
// Get function info and transform into simple function pointer.
lua_getinfo(ls, "nSf", ar);
ASSERT(lua_isfunction(ls, -1));
void const * luaPtr = lua_topointer(ls, -1);
ASSERT(luaPtr != nullptr);
// Add tail call.
lua_getstack(ls, 1, ar);
lua_getinfo(ls, "l", ar);
curNode = curNode->addCall(luaPtr, ar->currentline, true);
}
void CallGraphTracker::handleEventReturn(lua_State *, lua_Debug *)
{
while(curNode->isTailCall())
{
ASSERT(curNode->getCallerNode() != nullptr);
curNode = curNode->getCallerNode();
}
ASSERT(curNode->getCallerNode() != nullptr);
curNode = curNode->getCallerNode();
}
中,我先遍历尾调用,然后执行“适当”返回。如果表单的尾调用递归,则不能假设固定数量的尾调用(严格来说)
handleEventReturn
不幸的是,尽管这可以正确处理尾部调用,但我目前的问题是在错误处理下,调用和返回之间仍然存在不匹配,因此我希望能够直接向lua询问调用堆栈的深度,而不是必须进行缓慢的“反复试验”。
我当前的计划(我尚未实施)是跟踪我预期的通话深度,然后获取该级别的信息。如果那是我所期望的,那就太好了,不需要进一步的测试,不需要花费适中的费用,否则我会沿着堆栈向上(或向下走?),直到到达存储的调用图信息适合lua返回的信息为止。堆积深度。