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

时间:2011-12-16 00:35:11

标签: c++ lua gdb

我有一个C ++应用程序(用于OS X),它将lua称为脚本语言。 我正在运行大量这些应用程序(100s),他们可以运行 很长一段时间(几天或几周)。

有时一次崩溃。当它崩溃时,它留下了一个可爱的核心文件。

我可以在gdb中打开这个核心文件,找到应用程序崩溃的位置。 我可以遍历调用堆栈并找到lua_State变量的实例。 我的问题是我想看看lua调用堆栈的样子 这次......

请记住,因为这是一个核心,所以我无权调用C函数,它排除了调试lua脚本的几种常用方法。

我希望避免通过调试挂钩添加手动跟踪,因为我担心额外的性能损失,并增加了复杂性。

如何遍历lua内部结构以获取调用堆栈信息?

4 个答案:

答案 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()。虽然不确定它是否适用于核心文件...