#include #include static void mark_object(lua_State *L, lua_State *dL, const void * parent, const char * desc); #if LUA_VERSION_NUM == 501 static void luaL_checkversion(lua_State *L) { if (lua_pushthread(L) == 0) { luaL_error(L, "Must require in main thread"); } lua_setfield(L, LUA_REGISTRYINDEX, "mainthread"); } static void lua_rawsetp(lua_State *L, int idx, const void *p) { if (idx < 0) { idx += lua_gettop(L) + 1; } lua_pushlightuserdata(L, (void *)p); lua_insert(L, -2); lua_rawset(L, idx); } static void lua_rawgetp(lua_State *L, int idx, const void *p) { if (idx < 0) { idx += lua_gettop(L) + 1; } lua_pushlightuserdata(L, (void *)p); lua_rawget(L, idx); } static void lua_getuservalue(lua_State *L, int idx) { lua_getfenv(L, idx); } static void mark_function_env(lua_State *L, lua_State *dL, const void * t) { lua_getfenv(L,-1); if (lua_istable(L,-1)) { mark_object(L, dL, t, "[environment]"); } else { lua_pop(L,1); } } // lua 5.1 has no light c function #define is_lightcfunction(L, idx) (0) #else #define mark_function_env(L,dL,t) static int is_lightcfunction(lua_State *L, int idx) { if (lua_iscfunction(L, idx)) { if (lua_getupvalue(L, idx, 1) == NULL) { return 1; } lua_pop(L, 1); } return 0; } #endif #include #include #include #define TABLE 1 #define FUNCTION 2 #define SOURCE 3 #define THREAD 4 #define USERDATA 5 #define MARK 6 static bool ismarked(lua_State *dL, const void *p) { lua_rawgetp(dL, MARK, p); if (lua_isnil(dL,-1)) { lua_pop(dL,1); lua_pushboolean(dL,1); lua_rawsetp(dL, MARK, p); return false; } lua_pop(dL,1); return true; } static const void * readobject(lua_State *L, lua_State *dL, const void *parent, const char *desc) { int t = lua_type(L, -1); int tidx = 0; switch (t) { case LUA_TTABLE: tidx = TABLE; break; case LUA_TFUNCTION: if (is_lightcfunction(L, -1)) { lua_pop(L, 1); return NULL; } tidx = FUNCTION; break; case LUA_TTHREAD: tidx = THREAD; break; case LUA_TUSERDATA: tidx = USERDATA; break; default: lua_pop(L, 1); return NULL; } const void * p = lua_topointer(L, -1); if (ismarked(dL, p)) { lua_rawgetp(dL, tidx, p); if (!lua_isnil(dL,-1)) { lua_pushstring(dL,desc); lua_rawsetp(dL, -2, parent); } lua_pop(dL,1); lua_pop(L,1); return NULL; } lua_newtable(dL); lua_pushstring(dL,desc); lua_rawsetp(dL, -2, parent); lua_rawsetp(dL, tidx, p); return p; } static const char * keystring(lua_State *L, int index, char * buffer, size_t size) { int t = lua_type(L,index); switch (t) { case LUA_TSTRING: return lua_tostring(L,index); case LUA_TNUMBER: snprintf(buffer, size, "[%lg]",lua_tonumber(L,index)); break; case LUA_TBOOLEAN: snprintf(buffer, size, "[%s]",lua_toboolean(L,index) ? "true" : "false"); break; case LUA_TNIL: snprintf(buffer, size, "[nil]"); break; default: snprintf(buffer, size, "[%s:%p]",lua_typename(L,t),lua_topointer(L,index)); break; } return buffer; } static void mark_table(lua_State *L, lua_State *dL, const void * parent, const char * desc) { const void * t = readobject(L, dL, parent, desc); if (t == NULL) return; bool weakk = false; bool weakv = false; if (lua_getmetatable(L, -1)) { lua_pushliteral(L, "__mode"); lua_rawget(L, -2); if (lua_isstring(L,-1)) { const char *mode = lua_tostring(L, -1); if (strchr(mode, 'k')) { weakk = true; } if (strchr(mode, 'v')) { weakv = true; } } lua_pop(L,1); luaL_checkstack(L, LUA_MINSTACK, NULL); mark_table(L, dL, t, "[metatable]"); } lua_pushnil(L); while (lua_next(L, -2) != 0) { if (weakv) { lua_pop(L,1); } else { char temp[32]; const char * desc = keystring(L, -2, temp, sizeof(temp)); mark_object(L, dL, t , desc); } if (!weakk) { lua_pushvalue(L,-1); mark_object(L, dL, t , "[key]"); } } lua_pop(L,1); } static void mark_userdata(lua_State *L, lua_State *dL, const void * parent, const char *desc) { const void * t = readobject(L, dL, parent, desc); if (t == NULL) return; if (lua_getmetatable(L, -1)) { mark_table(L, dL, t, "[metatable]"); } lua_getuservalue(L,-1); if (lua_isnil(L,-1)) { lua_pop(L,2); } else { mark_object(L, dL, t, "[uservalue]"); lua_pop(L,1); } } static void mark_function(lua_State *L, lua_State *dL, const void * parent, const char *desc) { const void * t = readobject(L, dL, parent, desc); if (t == NULL) return; mark_function_env(L,dL,t); int i; for (i=1;;i++) { const char *name = lua_getupvalue(L,-1,i); if (name == NULL) break; mark_object(L, dL, t, name[0] ? name : "[upvalue]"); } if (lua_iscfunction(L,-1)) { lua_pop(L,1); } else { lua_Debug ar; lua_getinfo(L, ">S", &ar); luaL_Buffer b; luaL_buffinit(dL, &b); luaL_addstring(&b, "func: "); luaL_addstring(&b, ar.short_src); char tmp[16]; sprintf(tmp,":%d",ar.linedefined); luaL_addstring(&b, tmp); luaL_pushresult(&b); lua_rawsetp(dL, SOURCE, t); } } static void mark_thread(lua_State *L, lua_State *dL, const void * parent, const char *desc) { const void * t = readobject(L, dL, parent, desc); if (t == NULL) return; int level = 0; lua_State *cL = lua_tothread(L,-1); if (cL == L) { level = 1; } else { // mark stack int top = lua_gettop(cL); luaL_checkstack(cL, 1, NULL); int i; char tmp[16]; for (i=0;i=0) { char tmp[16]; sprintf(tmp,":%d ",ar.currentline); luaL_addstring(&b, tmp); } int i,j; for (j=1;j>-1;j-=2) { for (i=j;;i+=j) { const char * name = lua_getlocal(cL, &ar, i); if (name == NULL) break; snprintf(tmp, sizeof(tmp), "%s : %s:%d",name,ar.short_src,ar.currentline); mark_object(cL, dL, t, tmp); } } ++level; } luaL_addstring(&b, "thread: "); luaL_pushresult(&b); lua_rawsetp(dL, SOURCE, t); lua_pop(L,1); } static void mark_object(lua_State *L, lua_State *dL, const void * parent, const char *desc) { luaL_checkstack(L, LUA_MINSTACK, NULL); int t = lua_type(L, -1); switch (t) { case LUA_TTABLE: mark_table(L, dL, parent, desc); break; case LUA_TUSERDATA: mark_userdata(L, dL, parent, desc); break; case LUA_TFUNCTION: mark_function(L, dL, parent, desc); break; case LUA_TTHREAD: mark_thread(L, dL, parent, desc); break; default: lua_pop(L,1); break; } } static int count_table(lua_State *L, int idx) { int n = 0; lua_pushnil(L); while (lua_next(L, idx) != 0) { ++n; lua_pop(L,1); } return n; } static void gen_table_desc(lua_State *dL, luaL_Buffer *b, const void * parent, const char *desc) { char tmp[32]; size_t l = snprintf(tmp, sizeof(tmp), "%p : ",parent); luaL_addlstring(b, tmp, l); luaL_addstring(b, desc); luaL_addchar(b, '\n'); } static void pdesc(lua_State *L, lua_State *dL, int idx, const char * typename) { lua_pushnil(dL); while (lua_next(dL, idx) != 0) { luaL_Buffer b; luaL_buffinit(L, &b); const void * key = lua_touserdata(dL, -2); if (idx == FUNCTION) { lua_rawgetp(dL, SOURCE, key); if (lua_isnil(dL, -1)) { luaL_addstring(&b,"cfunction\n"); } else { size_t l = 0; const char * s = lua_tolstring(dL, -1, &l); luaL_addlstring(&b,s,l); luaL_addchar(&b,'\n'); } lua_pop(dL, 1); } else if (idx == THREAD) { lua_rawgetp(dL, SOURCE, key); size_t l = 0; const char * s = lua_tolstring(dL, -1, &l); luaL_addlstring(&b,s,l); luaL_addchar(&b,'\n'); lua_pop(dL, 1); } else { luaL_addstring(&b, typename); luaL_addchar(&b,'\n'); } lua_pushnil(dL); while (lua_next(dL, -2) != 0) { const void * parent = lua_touserdata(dL,-2); const char * desc = luaL_checkstring(dL,-1); gen_table_desc(dL, &b, parent, desc); lua_pop(dL,1); } luaL_pushresult(&b); lua_rawsetp(L, -2, key); lua_pop(dL,1); } } static void gen_result(lua_State *L, lua_State *dL) { int count = 0; count += count_table(dL, TABLE); count += count_table(dL, FUNCTION); count += count_table(dL, USERDATA); count += count_table(dL, THREAD); lua_createtable(L, 0, count); pdesc(L, dL, TABLE, "table"); pdesc(L, dL, USERDATA, "userdata"); pdesc(L, dL, FUNCTION, "function"); pdesc(L, dL, THREAD, "thread"); } static int snapshot(lua_State *L) { int i; lua_State *dL = luaL_newstate(); for (i=0;i