123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- #include <lua.h>
- #include <lauxlib.h>
- 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 <stdbool.h>
- #include <stdio.h>
- #include <string.h>
- #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<top;i++) {
- lua_pushvalue(cL, i+1);
- sprintf(tmp, "[%d]", i+1);
- mark_object(cL, dL, cL, tmp);
- }
- }
- lua_Debug ar;
- luaL_Buffer b;
- luaL_buffinit(dL, &b);
- while (lua_getstack(cL, level, &ar)) {
- char tmp[128];
- lua_getinfo(cL, "Sl", &ar);
- luaL_addstring(&b, ar.short_src);
- if (ar.currentline >=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<MARK;i++) {
- lua_newtable(dL);
- }
- lua_pushvalue(L, LUA_REGISTRYINDEX);
- mark_table(L, dL, NULL, "[registry]");
- gen_result(L, dL);
- lua_close(dL);
- return 1;
- }
- int
- luaopen_snapshot(lua_State *L) {
- luaL_checkversion(L);
- lua_pushcfunction(L, snapshot);
- return 1;
- }
|