#include #include #include "lstate.h" #include "lstring.h" #include "ltable.h" #include "lfunc.h" struct snapshot_params { int max_count; int current_mark_count; }; static void mark_object(lua_State *L, lua_State *dL, const void * parent, const char * desc, struct snapshot_params* args); #define check_limit(L, args) do { \ if((args)->max_count > 0) { \ if(((args)->current_mark_count)++ > (args)->max_count) { \ lua_pop((L),1); \ return; \ } \ } \ } while(0); #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, struct snapshot_params* args) { 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,args) 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 #if LUA_VERSION_NUM == 504 /* ** True if value of 'alimit' is equal to the real size of the array ** part of table 't'. (Otherwise, the array part must be larger than ** 'alimit'.) */ #define limitequalsasize(t) (isrealasize(t) || ispow2((t)->alimit)) /* ** Returns the real size of the 'array' array */ static unsigned int _luaH_realasize (const Table *t) { if (limitequalsasize(t)) return t->alimit; /* this is the size */ else { unsigned int size = t->alimit; /* compute the smallest power of 2 not smaller than 'n' */ size |= (size >> 1); size |= (size >> 2); size |= (size >> 4); size |= (size >> 8); size |= (size >> 16); #if (UINT_MAX >> 30) > 3 size |= (size >> 32); /* unsigned int has more than 32 bits */ #endif size++; lua_assert(ispow2(size) && size/2 < t->alimit && t->alimit < size); return size; } } #endif #include #include #include #define TABLE 1 #define FUNCTION 2 #define SOURCE 3 #define THREAD 4 #define USERDATA 5 #define STRING 6 #define MARK 7 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; #ifdef DUMP_STRING case LUA_TSTRING: tidx = STRING; break; #endif default: lua_pop(L, 1); return NULL; } const void * p = NULL; if(t == LUA_TUSERDATA) { const TValue *o = NULL; #if LUA_VERSION_NUM == 504 o = s2v(L->top - 1); #else o = L->top - 1; #endif // const TValue *o = index2value(L, -1); p = (const void*)uvalue(o); } else if (t == LUA_TSTRING) { p = lua_tostring(L, -1); } else { p = (const void*)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) { int t = lua_type(L,index); switch (t) { case LUA_TSTRING: return lua_tostring(L,index); case LUA_TNUMBER: sprintf(buffer,"[%lg]",lua_tonumber(L,index)); break; case LUA_TBOOLEAN: sprintf(buffer,"[%s]",lua_toboolean(L,index) ? "true" : "false"); break; case LUA_TNIL: sprintf(buffer,"[nil]"); break; default: sprintf(buffer,"[%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, struct snapshot_params* args) { const void * t = readobject(L, dL, parent, desc); if (t == NULL) return; check_limit(L, args); 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]", args); } 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); mark_object(L, dL, t , desc, args); } if (!weakk) { lua_pushvalue(L,-1); mark_object(L, dL, t , "[key]", args); } } lua_pop(L,1); } static void mark_string(lua_State *L, lua_State *dL, const void * parent, const char *desc) { const void* t = readobject(L, dL, parent, desc); if(t == NULL) return; lua_pop(L,1); } static void mark_userdata(lua_State *L, lua_State *dL, const void * parent, const char *desc, struct snapshot_params* args) { const void * t = readobject(L, dL, parent, desc); if (t == NULL) return; check_limit(L, args); if (lua_getmetatable(L, -1)) { mark_table(L, dL, t, "[metatable]", args); } lua_getuservalue(L,-1); if (lua_isnil(L,-1)) { lua_pop(L,2); } else { mark_object(L, dL, t, "[uservalue]", args); lua_pop(L,1); } } static void mark_function(lua_State *L, lua_State *dL, const void * parent, const char *desc, struct snapshot_params* args) { const void * t = readobject(L, dL, parent, desc); if (t == NULL) return; check_limit(L, args); mark_function_env(L,dL,t, args); 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]", args); } 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, 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, struct snapshot_params* args) { const void * t = readobject(L, dL, parent, desc); if (t == NULL) return; check_limit(L, args); 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.linedefined); mark_object(cL, dL, t, tmp, args); } } ++level; } 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, struct snapshot_params* args) { luaL_checkstack(L, LUA_MINSTACK, NULL); int t = lua_type(L, -1); switch (t) { case LUA_TTABLE: mark_table(L, dL, parent, desc, args); break; case LUA_TUSERDATA: mark_userdata(L, dL, parent, desc, args); break; case LUA_TFUNCTION: mark_function(L, dL, parent, desc, args); break; case LUA_TTHREAD: mark_thread(L, dL, parent, desc, args); break; case LUA_TSTRING: mark_string(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 = sprintf(tmp,"%p : ",parent); luaL_addlstring(b, tmp, l); luaL_addstring(b, desc); luaL_addchar(b, '\n'); } static size_t _table_size(Table* p) { size_t size = sizeof(*p); size_t tl = (p->lastfree == NULL)?(0):(sizenode(p)); size += tl*sizeof(Node); #if LUA_VERSION_NUM == 504 size += _luaH_realasize(p)*sizeof(TValue); #else size += p->sizearray*sizeof(TValue); #endif return size; } static size_t _thread_size(struct lua_State* p) { size_t size = sizeof(*p); size += p->nci*sizeof(CallInfo); #ifdef stacksize size += stacksize(p)*sizeof(*p->stack); #else size += p->stacksize*sizeof(*p->stack); #endif return size; } static size_t _userdata_size(Udata *p) { #if LUA_VERSION_NUM == 504 int size = sizeudata(p->nuvalue, p->len); return size; #else int l = sizeudata(p); size_t size = sizeludata(l); return size; #endif } static size_t _cfunc_size(CClosure* p) { int n = (int)(p->nupvalues); size_t size = sizeCclosure(n); return size; } static size_t _lfunc_size(LClosure *p) { int n = (int)(p->nupvalues); size_t size = sizeLclosure(n); return size; } static void pdesc(lua_State *L, lua_State *dL, int idx, const char * typename) { lua_pushnil(dL); size_t size = 0; char buff_sz[128] = {0}; while (lua_next(dL, idx) != 0) { luaL_Buffer b; luaL_buffinit(L, &b); const void * key = lua_touserdata(dL, -2); switch(idx) { case FUNCTION: { lua_rawgetp(dL, SOURCE, key); if (lua_isnil(dL, -1)) { size = _cfunc_size((CClosure*)key); snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); luaL_addstring(&b,"cfunction"); luaL_addchar(&b,' '); luaL_addstring(&b, buff_sz); luaL_addchar(&b,'\n'); } else { size_t l = 0; size = _lfunc_size((LClosure*)key); snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); const char * s = lua_tolstring(dL, -1, &l); if(l==0) { s = "?"; l = 1; } luaL_addlstring(&b,s,l); luaL_addchar(&b,' '); luaL_addstring(&b, buff_sz); luaL_addchar(&b,'\n'); } lua_pop(dL, 1); }break; case THREAD: { lua_rawgetp(dL, SOURCE, key); size_t l = 0; size = _thread_size((struct lua_State*)key); snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); const char * s = lua_tolstring(dL, -1, &l); luaL_addstring(&b,"thread "); luaL_addlstring(&b,s,l); luaL_addchar(&b,' '); luaL_addstring(&b, buff_sz); luaL_addchar(&b,'\n'); lua_pop(dL, 1); }break; case TABLE: { size = _table_size((Table*)key); snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); luaL_addstring(&b, typename); luaL_addchar(&b,' '); luaL_addstring(&b, buff_sz); luaL_addchar(&b,'\n'); }break; case USERDATA: { Udata* p = (Udata*)key; size = _userdata_size(p); snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); luaL_addstring(&b, typename); luaL_addchar(&b,' '); luaL_addstring(&b, buff_sz); luaL_addchar(&b,'\n'); }break; case STRING: { #if LUA_VERSION_NUM == 504 TString* ts = (TString*)(((char*)key) - offsetof(TString, contents)); #else TString* ts = (TString*)(((char*)key) - sizeof(UTString)); #endif size = tsslen(ts); snprintf(buff_sz, sizeof(buff_sz), "{%zd}", size); luaL_addstring(&b, typename); luaL_addchar(&b,' '); luaL_addstring(&b, buff_sz); luaL_addchar(&b,'\n'); }break; default: { snprintf(buff_sz, sizeof(buff_sz), "{0}"); luaL_addstring(&b, typename); luaL_addchar(&b,' '); luaL_addstring(&b, buff_sz); luaL_addchar(&b,'\n'); }break; } 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); count += count_table(dL, STRING); 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"); pdesc(L, dL, STRING, "string"); } static int snapshot(lua_State *L) { int i; struct snapshot_params args = {0}; args.max_count = luaL_optinteger(L, 1, 0); lua_State *dL = luaL_newstate(); for (i=0;i