我有一个C++应用程序(用于OS X),它将lua称为脚本语言.我正在运行大量这些应用程序(100s),它们可以运行很长时间(几天或几周).
有时会崩溃.当它崩溃时,它留下了一个可爱的核心文件.
我可以在gdb中打开这个核心文件,找到应用程序崩溃的位置.我可以遍历调用堆栈并找到lua_State变量的实例.我的问题是,我想看看这个时候lua调用堆栈的样子......
请记住,由于这是一个核心,我无权调用C函数,它排除了几种调试lua脚本的常用方法.
我想避免通过调试钩子添加手动跟踪,因为我担心额外的性能损失,并增加了复杂性.
如何遍历lua内部结构以获取调用堆栈信息?
我已经创建了一个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
Run Code Online (Sandbox Code Playgroud)
这输出如下:
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*)>
Run Code Online (Sandbox Code Playgroud)
当我从这段代码调试崩溃时:
// 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);
}
Run Code Online (Sandbox Code Playgroud)
同 a.lua
-- a.lua
-- just calls B, which calls C which should crash
function A()
B()
end
Run Code Online (Sandbox Code Playgroud)
和b.lua
-- b.lua
function B()
C()
end
Run Code Online (Sandbox Code Playgroud)
这是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
Run Code Online (Sandbox Code Playgroud)
基于以上评论,我推荐以下文章:Lua callstack with C++ debugger。它很好地概述了调试 Lua / C++ 组合,尤其是“检查 Lua 数据结构”部分在调试核心转储时很有帮助。