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
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;
|
|
}
|