获取 lua pcall 中错误的真实堆栈跟踪

Arh*_*owk 5 lua

所以对于我的 pcall 语句,我一直在做这样的事情

local status, err = pcall(fn)
if not status then
     print(err)
     print(debug.stacktrace())
end
Run Code Online (Sandbox Code Playgroud)

这适用于一些基本的东西,但问题是debug.stacktrace()返回当前相对堆栈跟踪,而不是错误的堆栈跟踪。如果 fn 中的错误发生在堆栈向下 10 层,那么我不知道它究竟发生在哪里,只是这个 pcall 块失败了。我想知道是否有办法获取 pcall 的堆栈跟踪而不是当前的堆栈跟踪。我试过了,debug.stacktrace(err)但没有任何区别。

Pau*_*nko 7

您需要使用xpcall提供一个自定义函数,该函数会将堆栈跟踪添加到错误消息中。从皮尔

通常,当发生错误时,我们需要更多的调试信息,而不仅仅是错误发生的位置。至少,我们需要一个回溯,显示导致错误的完整调用堆栈。当 pcall 返回它的错误消息时,它会破坏堆栈的一部分(从它到错误点的部分)。因此,如果我们想要回溯,我们必须在 pcall 返回之前构建它。为此,Lua 提供了 xpcall 函数。除了要调用的函数之外,它还接收第二个参数,即错误处理函数。如果出现错误,Lua 在堆栈展开之前调用该错误处理程序,以便它可以使用调试库来收集它想要的关于错误的任何额外信息。

您可能需要检查这个扩展pcall到包含 stacktrace 的补丁

正如评论中所建议的,您可以使用local ok, res = xpcall(f, debug.traceback, args...)Lua 5.2+ 或 LuaJIT(打开 Lua 5.2 兼容性)并使用上面提到的 Lua 5.1 补丁。

  • 快速示例:`local ok, res = xpcall(f, debug.traceback, args...)`。请注意,只有在 5.2 和 LuaJIT 开启了 5.2 兼容性后才能传递额外的参数。 (2认同)
  • 对于 lua 5.1: ```lua local ok, res = xpcall(function() return f(arg1, arg2) end, debug.traceback) ``` (2认同)

nob*_*ody 6

基本问题(大致)是pcall必须展开堆栈以便到达错误处理代码。这提供了两种明显的方法来解决该问题:在展开之前创建堆栈跟踪,或者将(可能)引发错误的代码移开,这样就不必删除堆栈帧。

\n

第一个由 处理xpcall。这设置了一个错误处理程序,可以在堆栈仍然完好无损的情况下创建消息。(请注意,在某些情况下xpcall不会调用处理程序,1因此它不适合清理代码!但对于堆栈跟踪,它通常足够好。)

\n

第二个选项(始终有效2)是通过将代码移动到不同的协程来保留堆栈。代替

\n
local ok, r1, r2, etc = pcall( f, ... )\n
Run Code Online (Sandbox Code Playgroud)\n

\n
local co = coroutine.create( f )\nlocal ok, r1, r2, etc = coroutine.resume( co, ... )\n
Run Code Online (Sandbox Code Playgroud)\n

现在堆栈(in )仍然被保留并且可以被或其他函数co查询。debug.traceback( co )debug

\n

如果您想要完整的堆栈跟踪,则必须收集协程内部的堆栈跟踪和协程外部的堆栈跟踪(您当前所在的位置),然后将两者合并,同时删除后者的第一行:

\n
local full_tb = debug.traceback( co )\n             .. debug.traceback( ):sub( 17 ) -- drop \'stack traceback:\' line\n
Run Code Online (Sandbox Code Playgroud)\n
\n

1未调用处理程序的一种情况是 OOM:

\n
g = ("a"):rep( 1024*1024*1024 ) -- a gigabyte of \'a\'s\n-- fail() tries to create a 32GB string \xe2\x80\x93 make it larger if that doesn\'t OOM\nfail = load( "return "..("g"):rep( 32, ".." ), "(replicator)" )\n\n-- plain call errors without traceback\nfail()\n--> not enough memory\n\n-- xpcall does not call the handler either:\nxpcall( fail, function(...) print( "handler:", ... ) return ... end, "foo" )\n--> false   not enough memory\n\n-- (for comparison: here, the handler is called)\nxpcall( error, function(...) print( "handler:", ... ) return ... end, "foo" )\n--> handler: foo\n--  false   foo\n\n-- coroutine preserves the stack anyway:\ndo\n   local co = coroutine.create( fail )\n   print( "result:", coroutine.resume( fail ) )\n   print( debug.traceback( co ) .. debug.traceback( ):sub( 17 ) )\nend\n--> result: false   not enough memory\n--> stack traceback:\n--    [string "(replicator)"]:1: in function \'fail\'\n--    stdin:4: in main chunk\n--    [C]: in ?\n
Run Code Online (Sandbox Code Playgroud)\n

2好吧,至少只要 Lua 本身不崩溃。

\n