c++ - ¿Cómo puedo obtener el seguimiento de la pila lua desde un archivo central utilizando gdb?
(4)
Tengo una aplicación C ++ (para OS X) que llama a lua como un lenguaje de scripting. Estoy ejecutando una gran cantidad de estas aplicaciones (100s) y pueden funcionar durante mucho tiempo (días o semanas).
A veces uno se cuelga. Y cuando se bloquea, me deja un precioso archivo central.
Puedo abrir este archivo core en gdb y encontrar dónde falla la aplicación. Puedo recorrer la pila de llamadas y encontrar una instancia de una variable lua_State. Mi problema es que me gustaría ver cómo es la pila de llamadas de lua en este momento ...
Tenga en cuenta que, dado que se trata de un núcleo, no tengo acceso a las funciones C de llamada, lo que excluye varias de las formas habituales de depurar scripts de lua.
Me gustaría evitar agregar rastros manuales a través de los anzuelos de depuración ya que estoy preocupado por las penalizaciones de rendimiento adicionales y la complejidad añadida.
¿Cómo puedo atravesar las estructuras internas de lua para obtener información sobre la pila de llamadas?
Creé un script GDB para hacer las cosas en la página web vinculada por macs. No es hermoso, y probablemente debería estar adecuadamente envuelto en una función, etc., pero aquí está para los curiosos.
NOTA: parece que la página web está equivocada con respecto al nombre de archivo de las funciones lua. En el caso en que la cadena proviene de luaL_dofile()
el nombre del archivo comienza con un símbolo @
. Si se llaman desde lua_dostring()
. En ese caso, la variable $filename
se establece en la totalidad de la cadena que se pasa a lua_dostring()
, y es probable que el usuario solo esté interesado en una o dos líneas de contexto de ese archivo. No estaba seguro de cómo arreglarlo.
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
Esto produce algo como:
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*)>
Cuando depuro el bloqueo de este código:
// 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);
}
Con a.lua
-- a.lua
-- just calls B, which calls C which should crash
function A()
B()
end
y b.lua
-- b.lua
function B()
C()
end
En base a los comentarios anteriores, recomendaría el siguiente artículo: Lua callstack con C ++ depurador . Está dando una buena visión general sobre la depuración de la combinación Lua / C ++, especialmente la sección "Inspeccionar estructuras de datos Lua" es útil, cuando se trata de la depuración de los volcados centrales.
Esta es una pequeña variación del guión GDB de Michael Anderson: tuve que usar esto porque Cannot access memory at address 0x656d
errores de la Cannot access memory at address 0x656d
con su script, debido a que L->base_ci
no es válido en mi volcado del núcleo. Esto comienza desde el cuadro superior ( L->ci
) y baja, en la dirección opuesta, evitando el puntero inválido 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
Podrías echarle un vistazo a mis ayudantes Lua GDB . Es una colección de macros que le permite inspeccionar la pila y los valores, e incluso imprimir trazados retrospectivos. Esencialmente lo que contiene el artículo al que hacen referencia los Mac, en un paquete fácil de usar.
Proporciona estas macros:
luastack [L]
- enumera los valores en la pila Lua C actual.luaprint < value > [verbose]
- Pretty imprime un TValue pasado como argumento. Espera un puntero a un TValue. Cuando verbose es 1, expande tablas, metatablas y entornos de datos de usuario.luaprinttable < table >
- Pretty-prints a Lua Table. Espera un puntero a la Tabla.luavalue < index > [L]
- Vuelca un único valor en un índice.luatraceback [L]
- Llama adebug.traceback()
. No estoy seguro de si funcionará en el archivo principal aunque ...