所以对于我的 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)
但没有任何区别。
您需要使用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 补丁。
基本问题(大致)是pcall
必须展开堆栈以便到达错误处理代码。这提供了两种明显的方法来解决该问题:在展开之前创建堆栈跟踪,或者将(可能)引发错误的代码移开,这样就不必删除堆栈帧。
第一个由 处理xpcall
。这设置了一个错误处理程序,可以在堆栈仍然完好无损的情况下创建消息。(请注意,在某些情况下xpcall
不会调用处理程序,1因此它不适合清理代码!但对于堆栈跟踪,它通常足够好。)
第二个选项(始终有效2)是通过将代码移动到不同的协程来保留堆栈。代替
\nlocal ok, r1, r2, etc = pcall( f, ... )\n
Run Code Online (Sandbox Code Playgroud)\n做
\nlocal 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
如果您想要完整的堆栈跟踪,则必须收集协程内部的堆栈跟踪和协程外部的堆栈跟踪(您当前所在的位置),然后将两者合并,同时删除后者的第一行:
\nlocal full_tb = debug.traceback( co )\n .. debug.traceback( ):sub( 17 ) -- drop \'stack traceback:\' line\n
Run Code Online (Sandbox Code Playgroud)\n1未调用处理程序的一种情况是 OOM:
\ng = ("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)\n2好吧,至少只要 Lua 本身不崩溃。
\n