Index: squeezeplay/src/jive_debug.c =================================================================== --- squeezeplay/src/jive_debug.c (revision 2079) +++ squeezeplay/src/jive_debug.c (working copy) @@ -94,13 +94,297 @@ } +struct heap_state { + long number; + long integer; + long boolean; + long string; + long table; + long function; + long thread; + long userdata; + long lightuserdata; + long new_table; + long new_function; + long new_thread; + long new_userdata; + long new_lightuserdata; + long free_table; + long free_function; + long free_thread; + long free_userdata; + long free_lightuserdata; +}; + + +static int jiveL_seenobj(lua_State *L, int type, int index) { + const void *ptr = lua_topointer(L, index); + + lua_pushinteger(L, (lua_Integer) ptr); + lua_gettable(L, 1); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + + lua_pushinteger(L, (lua_Integer) ptr); + lua_pushinteger(L, type); + lua_settable(L, 1); + + return 0; + } + else { + lua_pop(L, 1); + + return 1; + } +} + + +static int jiveL_newobj(lua_State *L, int type, int index) { + const void *ptr = lua_topointer(L, index); + + lua_pushinteger(L, (lua_Integer) ptr); + lua_gettable(L, 2); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + return 1; + } + else { + lua_pop(L, 1); + + lua_pushinteger(L, (lua_Integer) ptr); + lua_pushnil(L); + lua_settable(L, 2); + + return 0; + } +} + + +static void jiveL_inspect(lua_State *L, struct heap_state *s, int index) { + int type = lua_type(L, index); + + /* max stack */ + if (!lua_checkstack(L, 4)) { + printf("Stack error\n"); + return; + } + + /* count object */ + switch (type) { + case LUA_TNUMBER: + if (lua_isinteger(L, index)) { + s->integer++; + } + else { + s->number++; + } + break; + case LUA_TBOOLEAN: + s->boolean++; + break; + case LUA_TSTRING: + s->string++; + break; + case LUA_TTABLE: + if (jiveL_seenobj(L, LUA_TTABLE, index)) { + return; + } + + s->table++; + if (jiveL_newobj(L, LUA_TTABLE, index)) { + s->new_table++; + } + + lua_pushnil(L); + while (lua_next(L, index) != 0) { + int top = lua_gettop(L); + + jiveL_inspect(L, s, top - 1); + jiveL_inspect(L, s, top); + + lua_pop(L, 1); + } + break; + case LUA_TFUNCTION: + if (jiveL_seenobj(L, LUA_TFUNCTION, index)) { + return; + } + + s->function++; + if (jiveL_newobj(L, LUA_TFUNCTION, index)) { + s->new_function++; + } + + lua_getfenv(L, index); + jiveL_inspect(L, s, lua_gettop(L)); + lua_pop(L, 1); + break; + case LUA_TUSERDATA: + if (jiveL_seenobj(L, LUA_TUSERDATA, index)) { + return; + } + + s->userdata++; + if (jiveL_newobj(L, LUA_TUSERDATA, index)) { + s->new_userdata++; + } + break; + case LUA_TTHREAD: + if (jiveL_seenobj(L, LUA_TTHREAD, index)) { + return; + } + + s->thread++; + if (jiveL_newobj(L, LUA_TTHREAD, index)) { + s->new_thread++; + } + break; + case LUA_TLIGHTUSERDATA: + if (jiveL_seenobj(L, LUA_TLIGHTUSERDATA, index)) { + return; + } + + s->lightuserdata++; + if (jiveL_newobj(L, LUA_TLIGHTUSERDATA, index)) { + s->new_lightuserdata++; + } + break; + } + + /* count meta table */ + if (lua_getmetatable(L, index)) { + jiveL_inspect(L, s, lua_gettop(L)); + lua_pop(L, 1); + } +} + + +/* Inspect heap */ +static int jiveL_heap(lua_State *L) { + struct heap_state s; + + memset(&s, 0, sizeof(s)); + + /* heap history */ + lua_newtable(L); + lua_getfield(L, LUA_REGISTRYINDEX, "heap_debug"); + + /* stack is: + * 1: heap table + * 2: last heap table + */ + + /* globals */ + jiveL_inspect(L, &s, LUA_GLOBALSINDEX); + + /* XXXX: environ? */ + + /* count freed objects */ + lua_pushnil(L); + while (lua_next(L, 2) != 0) { + switch (lua_tointeger(L, -1)) { + case LUA_TTABLE: + s.free_table++; + break; + case LUA_TFUNCTION: + s.free_function++; + break; + case LUA_TUSERDATA: + s.free_userdata++; + break; + case LUA_TTHREAD: + s.free_thread++; + break; + case LUA_TLIGHTUSERDATA: + s.free_lightuserdata++; + break; + } + + lua_pop(L, 1); + } + + /* store heap history */ + lua_pop(L, 1); + lua_setfield(L, LUA_REGISTRYINDEX, "heap_debug"); + + /* results */ + lua_newtable(L); + + lua_pushinteger(L, s.number); + lua_setfield(L, -2, "number"); + + lua_pushinteger(L, s.integer); + lua_setfield(L, -2, "integer"); + + lua_pushinteger(L, s.boolean); + lua_setfield(L, -2, "boolean"); + + lua_pushinteger(L, s.string); + lua_setfield(L, -2, "string"); + + lua_pushinteger(L, s.table); + lua_setfield(L, -2, "table"); + + lua_pushinteger(L, s.new_table); + lua_setfield(L, -2, "new_table"); + + lua_pushinteger(L, s.free_table); + lua_setfield(L, -2, "free_table"); + + lua_pushinteger(L, s.function); + lua_setfield(L, -2, "function"); + + lua_pushinteger(L, s.new_function); + lua_setfield(L, -2, "new_function"); + + lua_pushinteger(L, s.free_function); + lua_setfield(L, -2, "free_function"); + + lua_pushinteger(L, s.thread); + lua_setfield(L, -2, "thread"); + + lua_pushinteger(L, s.new_thread); + lua_setfield(L, -2, "new_thread"); + + lua_pushinteger(L, s.free_thread); + lua_setfield(L, -2, "free_thread"); + + lua_pushinteger(L, s.userdata); + lua_setfield(L, -2, "userdata"); + + lua_pushinteger(L, s.new_userdata); + lua_setfield(L, -2, "new_userdata"); + + lua_pushinteger(L, s.free_userdata); + lua_setfield(L, -2, "free_userdata"); + + lua_pushinteger(L, s.lightuserdata); + lua_setfield(L, -2, "lightuserdata"); + + lua_pushinteger(L, s.new_lightuserdata); + lua_setfield(L, -2, "new_lightuserdata"); + + lua_pushinteger(L, s.free_lightuserdata); + lua_setfield(L, -2, "free_lightuserdata"); + + return 1; +} + + static const struct luaL_Reg debug_funcs[] = { { "perfhook", jiveL_perfhook }, + { "heap", jiveL_heap }, { NULL, NULL } }; int luaopen_jive_debug(lua_State *L) { + /* heap history */ + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, "heap_debug"); + luaL_register(L, "jive", debug_funcs); return 1; } Index: squeezeplay/share/jive/utils/log.lua =================================================================== --- squeezeplay/share/jive/utils/log.lua (revision 2079) +++ squeezeplay/share/jive/utils/log.lua (working copy) @@ -188,6 +188,7 @@ ["applets.misc"] = jiveLogger(logging.INFO), ["jive.main"] = jiveLogger(logging.INFO), + ["jive.heap"] = jiveLogger(logging.INFO), ["slimserver"] = jiveLogger(logging.INFO), ["slimserver.cache"] = jiveLogger(logging.INFO), Index: squeezeplay/share/jive/JiveMain.lua =================================================================== --- squeezeplay/share/jive/JiveMain.lua (revision 2079) +++ squeezeplay/share/jive/JiveMain.lua (working copy) @@ -59,6 +59,7 @@ local debug = require("jive.utils.debug") local log = require("jive.utils.log").logger("jive.main") +local logheap = require("jive.utils.log").logger("jive.heap") --require("profiler") local EVENT_KEY_ALL = jive.ui.EVENT_KEY_ALL @@ -188,6 +189,26 @@ true) splashTimer:start() + local heapTimer = Timer(60000, + function() + if not logheap:isDebug() then + return + end + + local s = jive.heap() + logheap:debug("--- HEAP total/new/free ---") + logheap:debug("number=", s["number"]); + logheap:debug("integer=", s["integer"]); + logheap:debug("boolean=", s["boolean"]); + logheap:debug("string=", s["string"]); + logheap:debug("table=", s["table"], "/", s["new_table"], "/", s["free_table"]); + logheap:debug("function=", s["function"], "/", s["new_function"], "/", s["free_function"]); + logheap:debug("thread=", s["thread"], "/", s["new_thread"], "/", s["free_thread"]); + logheap:debug("userdata=", s["userdata"], "/", s["new_userdata"], "/", s["free_userdata"]); + logheap:debug("lightuserdata=", s["lightuserdata"], "/", s["new_lightuserdata"], "/", s["free_lightuserdata"]); + end) + heapTimer:start() + -- run event loop Framework:eventLoop(jnt:task())