Подтвердить что ты не робот

Как я могу получить трассировку стека lua из основного файла с помощью gdb

У меня есть приложение на С++ (для OS X), которое вызывает lua как язык сценариев. Я запускаю большое количество этих приложений (100), и они могут запускаться в течение очень долгого времени (дней или недель).

Иногда происходит сбой. И когда он падает, он оставляет мне прекрасный основной файл.

Я могу открыть этот основной файл в gdb и найти, где приложение сбой. Я могу пройти стек вызовов и найти экземпляр переменной lua_State. Моя проблема в том, что я хотел бы посмотреть, как выглядит стек вызовов lua на на этот раз...

Имейте в виду, что, поскольку это ядро, у меня нет доступа к вызовам функций C, что исключает некоторые из обычных способов отладки сценариев lua.

Мне нравится избегать добавления ручных трасс через отладочные крючки, так как меня беспокоят дополнительные штрафы за производительность и сложность.

Как я могу пересечь внутренние структуры lua, чтобы получить информацию о стеке вызовов?

4b9b3361

Ответ 1

Я создал GDB script, чтобы сделать материал на веб-странице, связанную с 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

Ответ 2

Это небольшая вариация для Майкла Андерсона GDB script: мне пришлось использовать это, потому что я получал ошибки Cannot access memory at address 0x656d с его script, из-за того, что 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

Ответ 3

Основываясь на комментариях выше, я бы рекомендовал следующую статью: Lua callstack с отладчиком С++. Это дает хороший обзор об отладке комбинации Lua/С++, особенно раздел "Inspect Lua data structure", когда дело доходит до отладки исходных дампов.

Ответ 4

Вы можете проверить мои помощники Lua GDB. Это набор макросов, которые позволяют вам проверять стек и значения и даже распечатывать обратную трассировку. По сути, статья, на которую ссылаются маки, содержит простой пакет.

Он предоставляет эти макросы:

  • luastack [L] - перечисляет значения в текущем стеке Lua C.

  • luaprint < value > [verbose] - Довольно печатает переданный в качестве аргумента TValue. Ожидает указатель на TValue. Когда verbose равно 1, расширяется таблица, metatables и среды пользовательских данных.

  • luaprinttable < table > - Довольно печатает таблицу Lua. Ожидает указатель на таблицу.

  • luavalue < index > [L] - Сбрасывает одно значение по индексу.

  • luatraceback [L] - Вызов debug.traceback(). Не уверен, что он будет работать в основном файле, хотя...