2011-12-16 21 views
20

Tengo una aplicación C++ (para OS X) que llama a lua como lenguaje de scripting. Estoy ejecutando un gran número de estas aplicaciones (100s) y pueden ejecutar durante un tiempo muy largo (días o semanas).¿Cómo puedo obtener el seguimiento de la pila lua desde un archivo central utilizando gdb

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 lua en esta vez ...

Tenga en cuenta que, dado que este es un núcleo, no tengo acceso a las funciones de llamada C, que descarta varias de las formas habituales de depurar scripts de lua.

Me gustaría evitar añadir rastros manuales a través de los ganchos de depuración ya que estoy preocupado por las penalizaciones adicionales de rendimiento y la complejidad añadida.

¿Cómo puedo atravesar las estructuras internas de lua para obtener información sobre la pila de llamadas?

+5

Por lo tanto, tal vez usted ha leído esto ya, pero yo no lo sabe, así que no tenga mal humor, por favor;) [Lua pila de llamadas con C++ depurador] (http://zeuxcg.org/2010/11/07/lua-callstack-with-c-debugger /) –

+0

@macs Esa es una muy buena visión general. La sección "Inspeccionar estructuras de datos Lua" es la clave. Había resuelto la mayor parte de eso, pero es bastante engorroso de usar. Probablemente buscaré escribir macros/scripts GDB para hacerlo viable. –

+0

Me complace darle una mano, también hay [StackTracePlus] (https://github.com/ignacio/StackTracePlus) pero tendrá que modificar la función de llamada C, si estoy en lo correcto. Entonces es bastante inútil en este caso particular. –

Respuesta

8

He creado 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á mal con respecto al nombre del archivo para las funciones lua. En el caso de que la cadena provenga 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 al lua_dostring(), y el usuario probablemente 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 da salida a 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 puedo depurar el choque 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 
4

Según los comentarios anteriores, recomendaría el siguiente artículo: Lua callstack with C++ debugger. Está dando una buena visión general sobre la depuración de la combinación de Lua/C++, especialmente la sección "Inspeccionar estructuras de datos de Lua" es útil, cuando se trata de la depuración de los volcados centrales.

+1

Se me concede la recompensa porque es lo que me permite llegar a mi solución (parcial). –

4

Puedes ver mi Lua GDB helpers. 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 de la corriente de la pila Lua C.

  • luaprint <value> [verbose] - Pretty-prints a 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 solo valor en un índice.

  • luatraceback [L] - Llamadas debug.traceback(). No estoy seguro si funcionará en el archivo principal ...

+0

Aparentemente, sus macros usan el acercamiento número 1, llamando a los métodos de Lua para obtener la pila. No funcionará con un volcado de memoria. –

+0

Hmmm, parece que tendré que implementar más funcionalidades en GDB puro :) –

+0

Después de analizar detenidamente las fuentes de Lua, renuncio a implementar el seguimiento de pila en GDB, porque, por ejemplo, para obtener el nombre de la función, [ tienes que inspeccionar el bytecode] (http://www.lua.org/source/5.1/ldebug.c.html#symbexec). No estoy seguro de que sea realmente posible hacerlo sin conexión. –

6

Esta es una pequeña variación de secuencia de comandos de GDB de Michael Anderson: tuve que usar esto porque me estaba poniendo Cannot access memory at address 0x656d errores con su guión, debido a L->base_ci siendo válida en mi volcado de memoria. Esto comienza desde el cuadro superior (L->ci) y baja, en la dirección opuesta, evitando el puntero L->base_ci no válido.

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 
Cuestiones relacionadas