You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

653 lines
14 KiB
C

#include <lua.h>
#include <lauxlib.h>
#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 <stdbool.h>
#include <stdio.h>
#include <string.h>
#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<top;i++) {
lua_pushvalue(cL, i+1);
sprintf(tmp, "[%d]", i+1);
mark_object(cL, dL, cL, tmp, args);
}
}
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.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<MARK;i++) {
lua_newtable(dL);
}
lua_pushvalue(L, LUA_REGISTRYINDEX);
mark_table(L, dL, NULL, "[registry]", &args);
gen_result(L, dL);
lua_close(dL);
return 1;
}
static int
l_str2lightuserdata(lua_State* L) {
const char* s = luaL_checklstring(L, 1, NULL);
void* p = NULL;
sscanf(s, "%p", &p);
lua_pushlightuserdata(L, p);
return 1;
}
int
luaopen_snapshot(lua_State *L) {
luaL_checkversion(L);
luaL_Reg l[] = {
{"snapshot", snapshot},
{"str2ud", l_str2lightuserdata},
{NULL, NULL},
};
// lua_pushcfunction(L, snapshot);
luaL_newlib(L, l);
return 1;
}