From af5944a6d040ea2be8d78123ad14c8db22d56909 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Wed, 20 Nov 2013 03:14:05 +0200 Subject: [PATCH] lua-utils: allocation tracking for the Lua object infra If enabled, the object infra keeps track of all allocated and still uncollected objects on a per classdef basis. Upon exit all existing objects are dumped to stdout. This behavior is enabled by setting __MURPHY_LUA_CONFIG="track=yes". Setting it to any other value (than true, or on) or clearing the environment variables disables object tracking. --- src/core/lua-utils/object.c | 113 +++++++++++++++++++++++++++++++++++++++++++- src/core/lua-utils/object.h | 6 +++ 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/src/core/lua-utils/object.c b/src/core/lua-utils/object.c index b91462f..164f794 100644 --- a/src/core/lua-utils/object.c +++ b/src/core/lua-utils/object.c @@ -40,8 +40,10 @@ #include #include #include +#include #include +#include #include #undef __MURPHY_MANGLE_CLASS_SELF__ /* extra self-mangling if defined */ @@ -59,6 +61,7 @@ typedef struct { int exttbl; /* table of object extensions */ int dead : 1; /* being cleaned up */ int initializing : 1; /* being initialized */ + mrp_list_hook_t hook[0]; /* to object list if we're tracking */ } userdata_t; @@ -101,6 +104,14 @@ static mrp_lua_classdef_t invalid_classdef = { /** + * object infra configurable settings + */ +static struct { + bool track; /* track objects per classdef */ +} cfg; + + +/** * indirect table to look up classdefs by type_ids */ static mrp_lua_classdef_t **classdefs; @@ -109,12 +120,34 @@ static int nclassdef; /** Our (reference to our) shared table for storing references within object. */ static int reftbl = LUA_NOREF; - /** * Macros to convert between userdata and user-visible data addresses. */ +#define USERDATA_SIZE \ + (cfg.track?MRP_OFFSET(userdata_t, hook[1]):MRP_OFFSET(userdata_t, hook[0])) + +#define USER_TO_DATA(u) \ + ((void *)(cfg.track ? \ + &((userdata_t *)(u))->hook[1] : &((userdata_t *)(u))->hook[0])) + +#define DATA_TO_USER(d) ((userdata_t *)(((void *)d) - USERDATA_SIZE)) + +#if 0 #define USER_TO_DATA(u) ((void *)(((userdata_t *)(u)) + 1)) #define DATA_TO_USER(d) (((userdata_t *)(d)) - 1) +#endif + + +/** Check our configuration from the environment. */ +static void check_config(void) +{ + char *config = getenv(MRP_LUA_CONFIG_ENVVAR); + + cfg.track = mrp_env_config_bool(config, "track", false); + + if (cfg.track) + mrp_log_info("Murphy Lua object tracking enabled."); +} /** Encode our self(ish) pointer. */ @@ -181,6 +214,8 @@ static inline void *object_get(userdata_t *u, bool check) /** Create and register a new object class definition. */ int mrp_lua_create_object_class(lua_State *L, mrp_lua_classdef_t *def) { + static bool chkconfig = true; + if (def->constructor == NULL) { mrp_log_error("Classes with NULL constructor not allowed."); mrp_log_error("Please define a constructor for class %s (type %s).", @@ -189,6 +224,11 @@ int mrp_lua_create_object_class(lua_State *L, mrp_lua_classdef_t *def) return -1; } + if (chkconfig) { + check_config(); + chkconfig = false; + } + /* make a metatatable for userdata, ie for 'c' part of object instances*/ luaL_newmetatable(L, def->userdata_id); lua_pushliteral(L, "__index"); @@ -226,6 +266,8 @@ int mrp_lua_create_object_class(lua_State *L, mrp_lua_classdef_t *def) } } + mrp_list_init(&def->objects); + /* make the class table */ luaL_openlib(L, def->constructor, def->methods, 0); @@ -427,10 +469,14 @@ void *mrp_lua_create_object(lua_State *L, mrp_lua_classdef_t *def, lua_pushliteral(L, "userdata"); - size = sizeof(userdata_t) + def->userdata_size; + size = USERDATA_SIZE + def->userdata_size; userdata = (userdata_t *)lua_newuserdata(L, size); memset(userdata, 0, size); + + if (cfg.track) + mrp_list_init(&userdata->hook[0]); + userdata->reftbl = LUA_NOREF; userdata->exttbl = LUA_NOREF; @@ -467,6 +513,9 @@ void *mrp_lua_create_object(lua_State *L, mrp_lua_classdef_t *def, return NULL; /* not reached */ } + if (cfg.track) + mrp_list_append(&def->objects, &userdata->hook[0]); + return USER_TO_DATA(userdata); } @@ -760,6 +809,10 @@ static int userdata_destructor(lua_State *L) luaL_error(L, "attempt to destroy unknown type of userdata"); else { def = userdata->def; + + if (cfg.track) + mrp_list_delete(&userdata->hook[0]); + lua_getfield(L, LUA_REGISTRYINDEX, def->userdata_id); if (!lua_rawequal(L, -1, -2)) luaL_typerror(L, -2, def->userdata_id); @@ -2359,6 +2412,62 @@ ssize_t mrp_lua_index_tostr(mrp_lua_tostr_mode_t mode, char *buf, size_t size, } +/** Dump all live or zombie Lua objects. */ +void mrp_lua_dump_objects(mrp_lua_tostr_mode_t mode, lua_State *L, FILE *fp) +{ + mrp_lua_classdef_t *def; + mrp_list_hook_t *p, *n; + userdata_t *u; + void *d; + int i, cnt; + char obj[4096]; + + fprintf(fp, "Lua memory usage: %d k, %.2f M\n", + lua_gc(L, LUA_GCCOUNT, 0), lua_gc(L, LUA_GCCOUNT, 0) / 1024.0); + + for (i = 0; i < nclassdef; i++) { + def = classdefs[i]; + + if (mrp_list_empty(&def->objects)) { + fprintf(fp, "No objects active for Lua class <%s> (%s).\n", + def->class_name, def->type_name); + continue; + } + else + fprintf(fp, "Active/zombie objects for Lua class <%s> (%s):\n", + def->class_name, def->type_name); + + cnt = 0; + mrp_list_foreach(&def->objects, p, n) { + u = mrp_list_entry(p, typeof(*u), hook[0]); + d = USER_TO_DATA(u); + + if (mrp_lua_object_tostr(mode, obj, sizeof(obj), L, d) > 0) + fprintf(fp, " #%d: %s\n", cnt, obj); + else + fprintf(fp, " failed to dump object %p(%p)\n", u, d); + cnt++; + } + } +} + + +static void MRP_EXIT cleanup_check(void) +{ + lua_State *L; + + if ((L = mrp_lua_get_lua_state()) == NULL) { + mrp_log_error("Failed to get Lua state, can't dump objects."); + return; + } + + if (cfg.track) { + lua_gc(L, LUA_GCCOLLECT, 0); + mrp_lua_dump_objects(MRP_LUA_TOSTR_CHECKDUMP, L, stdout); + } +} + + /* * Local Variables: * c-basic-offset: 4 diff --git a/src/core/lua-utils/object.h b/src/core/lua-utils/object.h index 3ad7163..8e8a221 100644 --- a/src/core/lua-utils/object.h +++ b/src/core/lua-utils/object.h @@ -33,6 +33,8 @@ #include #include +#define MRP_LUA_CONFIG_ENVVAR "__MURPHY_LUA_CONFIG" + #define MRP_LUA_ENTER \ mrp_debug("enter") @@ -195,6 +197,9 @@ typedef enum { /** Stack-dump configuration for errors. */ #define MRP_LUA_TOSTR_ERRORDUMP (MRP_LUA_TOSTR_ONELINE | MRP_LUA_TOSTR_BOTH) +/** Stack-dump confguration for object-tracking check dump. */ +#define MRP_LUA_TOSTR_CHECKDUMP (MRP_LUA_TOSTR_ONELINE | MRP_LUA_TOSTR_BOTH) + /** Type of an object stringification handler. */ typedef ssize_t (*mrp_lua_tostr_t)(mrp_lua_tostr_mode_t mode, char *buf, size_t size, lua_State *L, void *data); @@ -622,6 +627,7 @@ struct mrp_lua_classdef_s { luaL_reg *overrides; mrp_lua_tostr_t tostring; /* stringification handler */ + mrp_list_hook_t objects; /* instances of this class */ /* pre-declared members */ mrp_lua_class_member_t *members; /* pre-declared members */ -- 2.7.4