我有一个C ++应用程序(用于OS X),它将lua称为脚本语言。 我正在运行大量这些应用程序(100s),他们可以运行 很长一段时间(几天或几周)。
有时一次崩溃。当它崩溃时,它留下了一个可爱的核心文件。
我可以在gdb中打开这个核心文件,找到应用程序崩溃的位置。 我可以遍历调用堆栈并找到lua_State变量的实例。 我的问题是我想看看lua调用堆栈的样子 这次......
请记住,因为这是一个核心,所以我无权调用C函数,它排除了调试lua脚本的几种常用方法。
我希望避免通过调试挂钩添加手动跟踪,因为我担心额外的性能损失,并增加了复杂性。
如何遍历lua内部结构以获取调用堆栈信息?
答案 0 :(得分:9)
我已经创建了一个GDB脚本来处理由mac链接的网页中的内容。它不漂亮,应该适当地包装成一个功能等,但这里是好奇的。
注意:似乎网页错误的lua函数的文件名。如果字符串来自luaL_dofile()
,则文件名以@
符号开头。如果他们是从lua_dostring()
打来的。在这种情况下,$filename
变量被设置为传递给lua_dostring()
的整个字符串 - 并且用户可能只对该文件中的一行或两行上下文感兴趣。我不知道如何解决这个问题。
set $p = L->base_ci
while ($p <= L->ci )
if ( $p->func->value.gc->cl.c.isC == 1 )
printf "0x%x C FUNCTION", $p
output $p->func->value.gc->cl.c.f
printf "\n"
else
if ($p->func.tt==6)
set $proto = $p->func->value.gc->cl.l.p
set $filename = (char*)(&($proto->source->tsv) + 1)
set $lineno = $proto->lineinfo[ $p->savedpc - $proto->code -1 ]
printf "0x%x LUA FUNCTION : %d %s\n", $p, $lineno, $filename
else
printf "0x%x LUA BASE\n", $p
end
end
set $p = $p+1
end
输出如下内容:
0x1002b0 LUA BASE
0x1002c8 LUA FUNCTION : 4 @a.lua
0x1002e0 LUA FUNCTION : 3 @b.lua
0x100310 C FUNCTION(lua_CFunction) 0x1fda <crash_function(lua_State*)>
当我从这段代码调试崩溃时:
// This is a file designed to crash horribly when run.
// It should generate a core, and it should crash inside some lua functions
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include <iostream>
#include <signal.h>
int crash_function(lua_State * L)
{
raise( SIGABRT ); //This should dump core!
return 0;
}
int main()
{
lua_State * L = luaL_newstate();
lua_pushcfunction(L, crash_function);
lua_setfield(L, LUA_GLOBALSINDEX, "C");
luaopen_base(L);
if( 1 == luaL_dofile(L, "a.lua" ))
{
std::cout<<"ERROR: "<<lua_tostring(L,-1)<<std::endl;
return 1;
}
if( 1 == luaL_dofile(L, "b.lua" ))
{
std::cout<<"ERROR: "<<lua_tostring(L,-1)<<std::endl;
return 1;
}
lua_getfield(L, LUA_GLOBALSINDEX, "A");
lua_pcall(L, 0, 0, NULL);
}
使用a.lua
-- a.lua
-- just calls B, which calls C which should crash
function A()
B()
end
和b.lua
-- b.lua
function B()
C()
end
答案 1 :(得分:7)
这是Michael Anderson的GDB脚本的一个小变化:我必须使用它,因为我的脚本出现Cannot access memory at address 0x656d
错误,因为我的核心转储中L->base_ci
无效。这从顶部框架(L->ci
)开始,向下,向相反方向,避免无效的L->base_ci
指针。
set $p = L->ci
while ($p > L->base_ci )
if ( $p->func->value.gc->cl.c.isC == 1 )
printf "0x%x C FUNCTION ", $p
output $p->func->value.gc->cl.c.f
printf "\n"
else
if ($p->func.tt==6)
set $proto = $p->func->value.gc->cl.l.p
set $filename = (char*)(&($proto->source->tsv) + 1)
set $lineno = $proto->lineinfo[ $p->savedpc - $proto->code -1 ]
printf "0x%x LUA FUNCTION : %d %s\n", $p, $lineno, $filename
else
printf "0x%x LUA BASE\n", $p
end
end
set $p = $p - 1
end
答案 2 :(得分:4)
根据上述评论,我建议使用以下文章:Lua callstack with C++ debugger。它提供了关于调试Lua / C ++组合的一个很好的概述,特别是在调试核心转储时,“检查Lua数据结构”一节很有帮助。
答案 3 :(得分:4)
您可以查看我的Lua GDB helpers。它是一组宏,可以让您检查堆栈和值,甚至打印回溯。基本上,macs引用的文章包含在一个简单易用的包中。
它提供了这些宏:
luastack [L]
- 列出当前Lua C堆栈上的值。
luaprint < value > [verbose]
- 漂亮打印作为参数传递的TValue。期待指向TValue的指针。当verbose为1时,展开表,元表和用户数据环境。
luaprinttable < table >
- 漂亮打印Lua表。预计指向Table的指针。
luavalue < index > [L]
- 在索引处转储单个值。
luatraceback [L]
- 致电debug.traceback()
。虽然不确定它是否适用于核心文件...