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

Mic*_*son 21 c++ lua gdb

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

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

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

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

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

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

Mic*_*son 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
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)


His*_*H M 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
Run Code Online (Sandbox Code Playgroud)


Seb*_*ler 5

基于以上评论,我推荐以下文章:Lua callstack with C++ debugger。它很好地概述了调试 Lua / C++ 组合,尤其是“检查 Lua 数据结构”部分在调试核心转储时很有帮助。