Как я могу получить трассировку стека lua из основного файла с помощью gdb
У меня есть приложение на С++ (для OS X), которое вызывает lua как язык сценариев.
Я запускаю большое количество этих приложений (100), и они могут запускаться
в течение очень долгого времени (дней или недель).
Иногда происходит сбой. И когда он падает, он оставляет мне прекрасный основной файл.
Я могу открыть этот основной файл в gdb и найти, где приложение сбой.
Я могу пройти стек вызовов и найти экземпляр переменной lua_State.
Моя проблема в том, что я хотел бы посмотреть, как выглядит стек вызовов lua на
на этот раз...
Имейте в виду, что, поскольку это ядро, у меня нет доступа к вызовам функций C, что исключает некоторые из обычных способов отладки сценариев lua.
Мне нравится избегать добавления ручных трасс через отладочные крючки, так как меня беспокоят дополнительные штрафы за производительность и сложность.
Как я могу пересечь внутренние структуры lua, чтобы получить информацию о стеке вызовов?
Ответы
Ответ 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()
. Не уверен, что он будет работать в основном файле, хотя...