I have a C++ application (for OS X) that calls lua as a scripting language. I\'m running a large number of these applications (100s) and they can run for a very long time (days
You could check out my Lua GDB helpers. It is a collection of macros that let you inspect the stack and values, and even print backtrace. Essentially what the article referenced by macs contains, in a simple-to-use package.
It provides these macros:
luastack [L]
- lists the values on the current Lua C stack.
luaprint < value > [verbose]
- Pretty-prints a TValue passed as argument. Expects a pointer to a TValue. When verbose is 1, expands tables, metatables and userdata environments.
luaprinttable < table >
- Pretty-prints a Lua Table. Expects a pointer to Table.
luavalue < index > [L]
- Dumps a single value at an index.
luatraceback [L]
- Calls debug.traceback()
. Not sure if it will work on core file though...
Based on the comments above, I'd recommend the following article: Lua callstack with C++ debugger. It's giving a good overview about debugging the Lua / C++ combination, especially the section "Inspect Lua data structures" is helpful, when it comes to debugging of core dumps.
This is a small variation to Michael Anderson's GDB script: I had to use this because I was getting Cannot access memory at address 0x656d
errors with his script, due to L->base_ci
being invalid in my core dump. This starts from the top frame (L->ci
) and goes down, in the opposite direction, avoiding the invalid L->base_ci
pointer.
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
I've created a GDB script to do the stuff in the web page linked to by macs. Its not beautiful, and should probably be properly wrapped into a function etc, but here it is for the curious.
NOTE: It seems that the web page is wrong about the filename for lua functions. In the case where the string comes from luaL_dofile()
the filename starts with a @
symbol. If they're called from lua_dostring()
. In that case the $filename
variable is set to the whole of the string passed to lua_dostring()
- and the user is probably only interested in one or two lines of context from that file. I wasn't sure how to fix that up.
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
This outputs something like:
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*)>
When I debug the crash from this code:
// 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);
}
With a.lua
-- a.lua
-- just calls B, which calls C which should crash
function A()
B()
end
and b.lua
-- b.lua
function B()
C()
end