如何使用gdb从核心文件获取lua堆栈跟踪
我有一个 C++ 应用程序(针对 OS X),它将 lua 作为脚本语言调用。我运行了大量这样的应用程序(100 多个),它们可以运行很长时间(数天或数周)。有时其中一个会崩溃。当崩溃时,会产生一个 core 文件。我可以在 gdb 中打开此核心文件并找到应用程序崩溃的位置。我可以遍历调用堆栈并找到 lua_state 变量的实例。我的问题是,我希望在此时查看 lua 调用堆栈的情况......请记住,由于这是一个核心,我无法访问调用 C 函数的功能,这排除了几种通常调试 lua 脚本的方法。我希望避免通过调试钩子添加手动跟踪,因为我担心会加重性能损失和增加复杂性。如何遍历 lua 内部结构以获取调用堆栈信息?
原文链接 https://stackoverflow.com/questions/8528503
你可以查看我的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()”。不确定它是否适用于核心文件...
我已经创建了一个 GDB 脚本,用于执行 macs 所链接的网页中的操作。这个脚本并不完美,可能需要将其适当地包装成一个函数等,但是可以在感兴趣的人查看。
注意: 该网页似乎在 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
这是对Michael Anderson的GDB脚本的小变化:我不得不使用它,因为他的脚本中出现了“Cannot access memory at address 0x656d”错误,由于我的core dump中的“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
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
- 如何编写 Lua 模式将字符串(嵌套数组)转换为真正的数组?
基于上面的评论,我建议阅读以下文章:使用 C++ 调试器的 Lua 调用栈。它给出了关于调试 Lua / C++ 组合的很好的概述,特别是“检查 Lua 数据结构”一节在调试核心转储时非常有用。