所以对于我的pcall语句,我一直在做这样的事情
local status, err = pcall(fn)
if not status then
print(err)
print(debug.stacktrace())
end
这适用于一些基本的东西,但问题是debug.stacktrace()
返回CURRENT相对堆栈跟踪,而不是错误的堆栈跟踪。如果fn中的错误在堆栈中发生了10级,那么我就不知道它究竟发生在哪里,只是这个pcall块失败了。我想知道是否有办法获得pcall的堆栈跟踪而不是当前的堆栈跟踪。我试过debug.stacktrace(err)
,但没有什么区别。
答案 0 :(得分:4)
您需要使用xpcall
来提供自定义函数,以便将堆栈跟踪添加到错误消息中。 From PiL:
通常,当发生错误时,我们需要更多的调试信息 仅发生错误的位置。至少,我们想要一个 回溯,显示导致错误的完整堆栈调用。 当pcall返回其错误消息时,它会破坏堆栈的一部分 (从它到错误点的部分)。因此,如果我们 想要追溯,我们必须在pcall返回之前构建它。要做到这一点, Lua提供了xpcall函数。除了要调用的功能外, 它接收第二个参数,一个错误处理函数。的情况下 错误,Lua在堆栈展开之前调用错误处理程序,这样 它可以使用调试库来收集它想要的任何额外信息 关于错误。
您可能需要查看此patch that extends pcall
to include stacktrace。
正如评论中所建议的那样,您可以将local ok, res = xpcall(f, debug.traceback, args...)
与Lua 5.2+或LuaJIT(打开Lua 5.2兼容性)一起使用,并使用上面提到的Lua 5.1补丁。
答案 1 :(得分:4)
基本问题是(大致)pcall
必须展开堆栈才能达到错误处理代码。这提供了两种明显的方法来解决这个问题:在展开之前创建堆栈跟踪,或者将(可能的)错误抛出代码移开,因此堆栈框架不必被删除。
第一个由xpcall
处理。这将设置一个错误处理程序,可以在堆栈仍然完好时创建消息。 (请注意,在某些情况下xpcall
不会调用处理程序, 1 因此它不适用于清理代码!但对于堆栈跟踪,它通常是足够好了。)
第二个选项(这总是 2 )是通过将代码移动到不同的协程来保留堆栈。而不是
local ok, r1, r2, etc = pcall( f, ... )
DO
local co = coroutine.create( f )
local ok, r1, r2, etc = coroutine.resume( f, ... )
现在堆栈(co
)仍然保留,可由debug.traceback( co )
或其他debug
函数查询。
如果你想要完整的堆栈跟踪,那么你必须收集协同程序内部的堆栈跟踪和它外面的堆栈跟踪(你当前所在的位置),然后在删除第一行的同时将两者结合起来后者:
local full_tb = debug.traceback( co )
.. debug.traceback( ):sub( 17 ) -- drop 'stack traceback:' line
1 没有调用处理程序的一种情况是OOM:
g = ("a"):rep( 1024*1024*1024 ) -- a gigabyte of 'a's
-- fail() tries to create a 32GB string – make it larger if that doesn't OOM
fail = load( "return "..("g"):rep( 32, ".." ), "(replicator)" )
-- plain call errors without traceback
fail()
--> not enough memory
-- xpcall does not call the handler either:
xpcall( fail, function(...) print( "handler:", ... ) return ... end, "foo" )
--> false not enough memory
-- (for comparison: here, the handler is called)
xpcall( error, function(...) print( "handler:", ... ) return ... end, "foo" )
--> handler: foo
-- false foo
-- coroutine preserves the stack anyway:
do
local co = coroutine.create( fail )
print( "result:", coroutine.resume( fail ) )
print( debug.traceback( co ) .. debug.traceback( ):sub( 17 ) )
end
--> result: false not enough memory
--> stack traceback:
-- [string "(replicator)"]:1: in function 'fail'
-- stdin:4: in main chunk
-- [C]: in ?
2 好吧,至少只要Lua本身不会崩溃。