summaryrefslogtreecommitdiff
path: root/Tools/lua-snapshot/snapshot.c
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/lua-snapshot/snapshot.c')
-rw-r--r--Tools/lua-snapshot/snapshot.c426
1 files changed, 426 insertions, 0 deletions
diff --git a/Tools/lua-snapshot/snapshot.c b/Tools/lua-snapshot/snapshot.c
new file mode 100644
index 0000000..869562e
--- /dev/null
+++ b/Tools/lua-snapshot/snapshot.c
@@ -0,0 +1,426 @@
+#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;
+}