From 7af81e64ae0768713aa1288db7b343cc099ecb03 Mon Sep 17 00:00:00 2001 From: spin6lock Date: Fri, 8 Jun 2018 11:40:34 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E9=9B=86=E6=88=90lua-snapshot,=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=86=85=E5=AD=98=E5=BF=AB=E7=85=A7debug=E6=8C=87?= =?UTF-8?q?=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 1 + lualib-src/lua-snapshot.c | 412 +++++++++++++++++++++++++++++++ lualib/skynet/debug.lua | 40 ++- lualib/skynet/snapshot_utils.lua | 91 +++++++ 4 files changed, 534 insertions(+), 10 deletions(-) create mode 100644 lualib-src/lua-snapshot.c create mode 100644 lualib/skynet/snapshot_utils.lua diff --git a/Makefile b/Makefile index 4f8b17a78..81e46de69 100644 --- a/Makefile +++ b/Makefile @@ -64,6 +64,7 @@ LUA_CLIB_SKYNET = \ lua-mysqlaux.c \ lua-debugchannel.c \ lua-datasheet.c \ + lua-snapshot.c \ \ SKYNET_SRC = skynet_main.c skynet_handle.c skynet_module.c skynet_mq.c \ diff --git a/lualib-src/lua-snapshot.c b/lualib-src/lua-snapshot.c new file mode 100644 index 000000000..894da6c68 --- /dev/null +++ b/lualib-src/lua-snapshot.c @@ -0,0 +1,412 @@ +#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); + } +} + +#else + +#define mark_function_env(L,dL,t) + +#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: + tidx = FUNCTION; + break; + case LUA_TTHREAD: + tidx = THREAD; + break; + case LUA_TUSERDATA: + tidx = USERDATA; + break; + default: + 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) { + 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) { + 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); + 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_table(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)) { + if (i==1) { + // light c function + lua_pushnil(dL); + lua_rawsetp(dL, FUNCTION, t); + } + 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 = sprintf(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 Date: Mon, 11 Jun 2018 10:37:10 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0snapshot=5Futils=E6=A0=91?= =?UTF-8?q?=E7=AE=97=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/skynet/debug.lua | 1 + lualib/skynet/snapshot_utils.lua | 104 ++++++++++++++++++++++--------- 2 files changed, 74 insertions(+), 31 deletions(-) diff --git a/lualib/skynet/debug.lua b/lualib/skynet/debug.lua index f544f5cba..9648d8738 100644 --- a/lualib/skynet/debug.lua +++ b/lualib/skynet/debug.lua @@ -104,6 +104,7 @@ local function init(skynet, export) diff[k] = v end end + old_snapshot = new_snapshot local ret = construct_indentation(diff) skynet.ret(skynet.pack(ret)) end diff --git a/lualib/skynet/snapshot_utils.lua b/lualib/skynet/snapshot_utils.lua index 7cc358968..a041ba3dc 100644 --- a/lualib/skynet/snapshot_utils.lua +++ b/lualib/skynet/snapshot_utils.lua @@ -36,26 +36,66 @@ local function cleanup_key_value(input) end local function reduce(input_diff) - local ret = {} - local count = 0 + local a_set = {} + local b_set = {} + local step = 0 + -- 先收入叶节点 for self_addr, info in pairs(input_diff) do - local parent = info.parent - if not input_diff[parent] then - ret[self_addr] = info - count = count + 1 - else - if not ret[parent] then - ret[parent] = input_diff[parent] - count = count + 1 + local flag = true + for _, node in pairs(input_diff) do + if node.parent == self_addr then + flag = false + break end + end + if flag then + a_set[self_addr] = info + end + end + step = step + 1 + local MAX_DEPTH = 32 + local dirty + while step < MAX_DEPTH do + dirty = false + -- 遍历叶节点,将parent拉进来 + for self_addr, info in pairs(a_set) do local key = info.key - if not ret[parent].values then - ret[parent].values = {} + local parent = info.parent + local parent_node = input_diff[parent] + if parent_node then + if not b_set[parent] then + b_set[parent] = parent_node + end + parent_node[key] = info + step = step + 1 + dirty = true + else + b_set[self_addr] = info end - ret[parent].values[key] = info + a_set[self_addr] = nil + end + -- 遍历节点,将祖父节点拉进来 + for self_addr, info in pairs(b_set) do + local key = info.key + local parent = info.parent + local parent_node = input_diff[parent] + if parent_node then + if not a_set[parent] then + a_set[parent] = parent_node + end + parent_node[key] = info + step = step + 1 + dirty = true + else + a_set[self_addr] = info + end + b_set[self_addr] = nil + end + if not dirty then + break end end - return ret, count + return a_set end local unwanted_key = { @@ -63,29 +103,31 @@ local unwanted_key = { --key = 1, parent = 1, } -local function cleanup_forest(forest) - for k, v in pairs(forest) do - if unwanted_key[k] then - forest[k] = nil - else - if type(v) == "table" then - cleanup_forest(v) - end +local function cleanup_forest(input) + local cache = {[input] = "."} + local function _clean(forest) + if cache[forest] then + return end - end + for k, v in pairs(forest) do + if unwanted_key[k] then + forest[k] = nil + else + if type(v) == "table" then + cleanup_forest(v) + end + end + end + end + return _clean(input) end local M = {} function M.construct_indentation(input_diff) local clean_diff = cleanup_key_value(input_diff) - local forest, count = reduce(clean_diff) - local new_forest, new_count = reduce(forest) - while new_count ~= count do - forest, count = new_forest, new_count - new_forest, new_count = reduce(new_forest) - end - cleanup_forest(new_forest) - return new_forest + local forest = reduce(clean_diff) + cleanup_forest(forest) + return forest end return M From 00ed1d1fe8f0871456696e0542aa0a4ce5e746b9 Mon Sep 17 00:00:00 2001 From: spin6lock Date: Mon, 11 Jun 2018 11:29:47 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8E=92=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/skynet/debug.lua | 60 ++++++++++++++++---------------- lualib/skynet/snapshot_utils.lua | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/lualib/skynet/debug.lua b/lualib/skynet/debug.lua index 9648d8738..262cb5d10 100644 --- a/lualib/skynet/debug.lua +++ b/lualib/skynet/debug.lua @@ -77,37 +77,37 @@ local function init(skynet, export) function dbgcmd.LINK() skynet.response() -- get response , but not return. raise error when exit end - - function dbgcmd.TRACELOG(proto, flag) - if type(proto) ~= "string" then - flag = proto - proto = "lua" - end - skynet.error(string.format("Turn trace log %s for %s", flag, proto)) - skynet.traceproto(proto, flag) - skynet.ret() - end - - local old_snapshot - local snapshot = require "snapshot" - local construct_indentation = (require "skynet.snapshot_utils").construct_indentation - function dbgcmd.SNAPSHOT() + + function dbgcmd.TRACELOG(proto, flag) + if type(proto) ~= "string" then + flag = proto + proto = "lua" + end + skynet.error(string.format("Turn trace log %s for %s", flag, proto)) + skynet.traceproto(proto, flag) + skynet.ret() + end + + local old_snapshot + local snapshot = require "snapshot" + local construct_indentation = (require "skynet.snapshot_utils").construct_indentation + function dbgcmd.SNAPSHOT() collectgarbage "collect" - local new_snapshot = snapshot() - if not old_snapshot then - old_snapshot = new_snapshot - return skynet.ret(skynet.pack({})) - end - local diff = {} - for k,v in pairs(new_snapshot) do - if not old_snapshot[k] then - diff[k] = v - end - end - old_snapshot = new_snapshot - local ret = construct_indentation(diff) - skynet.ret(skynet.pack(ret)) - end + local new_snapshot = snapshot() + if not old_snapshot then + old_snapshot = new_snapshot + return skynet.ret(skynet.pack({})) + end + local diff = {} + for k,v in pairs(new_snapshot) do + if not old_snapshot[k] then + diff[k] = v + end + end + old_snapshot = new_snapshot + local ret = construct_indentation(diff) + skynet.ret(skynet.pack(ret)) + end return dbgcmd end -- function init_dbgcmd diff --git a/lualib/skynet/snapshot_utils.lua b/lualib/skynet/snapshot_utils.lua index a041ba3dc..246d2f2ba 100644 --- a/lualib/skynet/snapshot_utils.lua +++ b/lualib/skynet/snapshot_utils.lua @@ -28,7 +28,7 @@ local function cleanup_key_value(input) ret[clean_key] = { val_type = val_type, parent = parent, - extra = trim_extra, + extra = v, key = val_key, } end From 84d4d9b9f65a0bfa54d7a3ec5c895c3bca5bf065 Mon Sep 17 00:00:00 2001 From: spin6lock Date: Mon, 11 Jun 2018 14:14:36 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=94=B9=E4=B8=BA=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E6=97=B6=E5=86=8Drequire=20snapshot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lualib/skynet/debug.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lualib/skynet/debug.lua b/lualib/skynet/debug.lua index 262cb5d10..6838f3204 100644 --- a/lualib/skynet/debug.lua +++ b/lualib/skynet/debug.lua @@ -89,9 +89,9 @@ local function init(skynet, export) end local old_snapshot - local snapshot = require "snapshot" - local construct_indentation = (require "skynet.snapshot_utils").construct_indentation function dbgcmd.SNAPSHOT() + local snapshot = require "snapshot" + local construct_indentation = (require "skynet.snapshot_utils").construct_indentation collectgarbage "collect" local new_snapshot = snapshot() if not old_snapshot then From 7931abe77bdb1a4fc691fc621bdbb3bc750c3fdf Mon Sep 17 00:00:00 2001 From: spin6lock Date: Mon, 6 May 2019 16:19:13 +0800 Subject: [PATCH 5/5] move lua-snapshot into 3rd --- .gitmodules | 3 + 3rd/lua-snapshot | 1 + lualib-src/lua-snapshot.c | 412 ------------------------------- lualib/skynet/snapshot_utils.lua | 133 ---------- 4 files changed, 4 insertions(+), 545 deletions(-) create mode 160000 3rd/lua-snapshot delete mode 100644 lualib-src/lua-snapshot.c delete mode 100644 lualib/skynet/snapshot_utils.lua diff --git a/.gitmodules b/.gitmodules index 4d6e3fdcb..c9e57a63f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "3rd/jemalloc"] path = 3rd/jemalloc url = https://github.com/jemalloc/jemalloc.git +[submodule "3rd/lua-snapshot"] + path = 3rd/lua-snapshot + url = https://github.com/cloudwu/lua-snapshot.git diff --git a/3rd/lua-snapshot b/3rd/lua-snapshot new file mode 160000 index 000000000..5fdc88aaa --- /dev/null +++ b/3rd/lua-snapshot @@ -0,0 +1 @@ +Subproject commit 5fdc88aaa5335b3e7137f1ed01d76517f3d129c9 diff --git a/lualib-src/lua-snapshot.c b/lualib-src/lua-snapshot.c deleted file mode 100644 index 894da6c68..000000000 --- a/lualib-src/lua-snapshot.c +++ /dev/null @@ -1,412 +0,0 @@ -#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); - } -} - -#else - -#define mark_function_env(L,dL,t) - -#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: - tidx = FUNCTION; - break; - case LUA_TTHREAD: - tidx = THREAD; - break; - case LUA_TUSERDATA: - tidx = USERDATA; - break; - default: - 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) { - 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) { - 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); - 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_table(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)) { - if (i==1) { - // light c function - lua_pushnil(dL); - lua_rawsetp(dL, FUNCTION, t); - } - 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 = sprintf(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