Print stacktrace from C code with embedded lua
Asked Answered
A

4

20

If I understand this correctly, Lua by default will call the debug library "debug.traceback" when an error occurs.

However, when embedding Lua into C code like done in the example here: Simple Lua API Example

We only have available the error message on the top of the stack.

i.e.

if (status) {
    /* If something went wrong, error message is at the top of */
    /* the stack */
    fprintf(stderr, "Couldn't load file: %s\n", lua_tostring(L, -1));

    /* I want to print a stacktrace here. How do I do that? */
    exit(1);
}

How do I print the stack trace from C after the initial error?

Anderton answered 4/9, 2012 at 3:33 Comment(1)
In Lua 5.2 you can use luaL_traceback.Vollmer
G
20

Lua by default will call the debug library "debug.traceback" when an error occurs.

No, it won't. The Lua runtime (lua.exe) will do that, but the Lua library will not do that on its own. If you want a call-stack with your Lua errors, then you need to generate one.

The Lua runtime does this by using lua_pcall's error function. The stack has not been unwound when the error function is called, so you can get a stack trace there. The error function the runtime uses is this one:

static int traceback (lua_State *L) {
  if (!lua_isstring(L, 1))  /* 'message' not a string? */
    return 1;  /* keep it intact */
  lua_getfield(L, LUA_GLOBALSINDEX, "debug");
  if (!lua_istable(L, -1)) {
    lua_pop(L, 1);
    return 1;
  }
  lua_getfield(L, -1, "traceback");
  if (!lua_isfunction(L, -1)) {
    lua_pop(L, 2);
    return 1;
  }
  lua_pushvalue(L, 1);  /* pass error message */
  lua_pushinteger(L, 2);  /* skip this function and traceback */
  lua_call(L, 2, 1);  /* call debug.traceback */
  return 1;
}
Greenway answered 4/9, 2012 at 3:47 Comment(2)
Thanks, not really a complete answer. But I looked up lua.c and that gives the whole picture of how to actually use the traceback function.Anderton
Extending this to walk the C stack would require a lot of platform-specific details, but can certainly be done. It would obviously work best if symbols are available for the executable. This question provides a bunch of approaches to doing the C side. Stringifying the result and tagging it on to the result of debug.traceback is then a straightforward problem.Thrall
L
11

Working off Nicol’s answer above here is a working example:

static int traceback(lua_State *L) {
    lua_getfield(L, LUA_GLOBALSINDEX, "debug");
    lua_getfield(L, -1, "traceback");
    lua_pushvalue(L, 1);
    lua_pushinteger(L, 2);
    lua_call(L, 2, 1);
    fprintf(stderr, "%s\n", lua_tostring(L, -1));
    return 1;
}

int main(int argc, char **argv) {
    lua_State *L = lua_open();
    luaL_openlibs(L);    
    lua_pushcfunction(L, traceback);
    int rv = luaL_loadfile(L, "src/main.lua");
    if (rv) {
        fprintf(stderr, "%s\n", lua_tostring(L, -1));
        return rv;
    } else {
        return lua_pcall(L, 0, 0, lua_gettop(L) - 1);
    }
}
Larner answered 1/5, 2013 at 17:41 Comment(2)
Good answer, Note newer lua versions has lua_getglobal so need to use lua_getglobal(L, "debug");Carpal
If this function prints me "null" in back, what am I doing wrong?Quinquennial
R
9

I met the some question as you do,and I found this way work:

luaL_traceback(L, L, NULL, 1);
printf("%s\n", lua_tostring(L, -1));

SinceluaL_tracebackis exactlydebug.traceback() using to print the stack,so I think this may be a proper way,and you can read the API manual aboutluaL_traceback or just read the source code of Lua to figure out the what the params means.

Reeding answered 16/9, 2015 at 9:10 Comment(1)
It seems that this function does not exists in Lua 5.1, which is the version I must use. Too bad. I also don't have debug library loaded to my Lua state running on embedded device.Erdah
C
2

mxcl's code has some problem:

static int traceback(lua_State *L) {
    lua_getfield(L, LUA_GLOBALSINDEX, "debug");
    lua_getfield(L, -1, "traceback");
    //---------------------------
    lua_pop(L,-2); //to popup the 'debug'
    //---------------------------
    lua_pushvalue(L, 1);
    lua_pushinteger(L, 2);
    lua_call(L, 2, 1);
    fprintf(stderr, "%s\n", lua_tostring(L, -1));
    return 1;
}
Clementina answered 13/4, 2014 at 18:48 Comment(1)
You still wrong, lua_pop Pops n elements from the stack. You should use lua_removePartisan

© 2022 - 2024 — McMap. All rights reserved.