如何使用gdb从核心文件获取lua堆栈跟踪

我有一个 C++ 应用程序(针对 OS X),它将 lua 作为脚本语言调用。我运行了大量这样的应用程序(100 多个),它们可以运行很长时间(数天或数周)。有时其中一个会崩溃。当崩溃时,会产生一个 core 文件。我可以在 gdb 中打开此核心文件并找到应用程序崩溃的位置。我可以遍历调用堆栈并找到 lua_state 变量的实例。我的问题是,我希望在此时查看 lua 调用堆栈的情况......请记住,由于这是一个核心,我无法访问调用 C 函数的功能,这排除了几种通常调试 lua 脚本的方法。我希望避免通过调试钩子添加手动跟踪,因为我担心会加重性能损失和增加复杂性。如何遍历 lua 内部结构以获取调用堆栈信息?

原文链接 https://stackoverflow.com/questions/8528503

点赞
stackoverflow用户172695
stackoverflow用户172695

基于上面的评论,我建议阅读以下文章:使用 C++ 调试器的 Lua 调用栈。它给出了关于调试 Lua / C++ 组合的很好的概述,特别是“检查 Lua 数据结构”一节在调试核心转储时非常有用。

2011-12-21 15:36:50
stackoverflow用户221509
stackoverflow用户221509

你可以查看我的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()”。不确定它是否适用于核心文件...

2011-12-21 16:01:43
stackoverflow用户221955
stackoverflow用户221955

我已经创建了一个 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
2011-12-22 07:10:02
stackoverflow用户1793220
stackoverflow用户1793220

这是对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
2012-11-02 04:21:15