/* ** LuaProfiler ** Copyright Kepler Project 2005-2007 (http://www.keplerproject.org/luaprofiler) ** $Id: lua50_profiler.c,v 1.13 2008/05/19 18:36:23 mascarenhas Exp $ */ /***************************************************************************** lua50_profiler.c: Lua version dependent profiler interface *****************************************************************************/ #include #include #include #include "clocks.h" #include "core_profiler.h" #include "function_meter.h" #include "lua.h" #include "lauxlib.h" /* Indices for the main profiler stack and for the original exit function */ static int exit_id; static int profstate_id; /* Forward declaration */ static float calcCallTime(lua_State *L); /* called by Lua (via the callhook mechanism) */ static void callhook(lua_State *L, lua_Debug *ar) { int currentline; lua_Debug previous_ar; lprofP_STATE* S; lua_pushlightuserdata(L, &profstate_id); lua_gettable(L, LUA_REGISTRYINDEX); S = (lprofP_STATE*)lua_touserdata(L, -1); if (lua_getstack(L, 1, &previous_ar) == 0) { currentline = -1; } else { lua_getinfo(L, "l", &previous_ar); currentline = previous_ar.currentline; } lua_getinfo(L, "nS", ar); if (!ar->event) { /* entering a function */ lprofP_callhookIN(S, (char *)ar->name, (char *)ar->source, ar->linedefined, currentline); } else { /* ar->event == "return" */ lprofP_callhookOUT(S); } } /* Lua function to exit politely the profiler */ /* redefines the lua exit() function to not break the log file integrity */ /* The log file is assumed to be valid if the last entry has a stack level */ /* of 1 (meaning that the function 'main' has been exited) */ static void exit_profiler(lua_State *L) { lprofP_STATE* S; lua_pushlightuserdata(L, &profstate_id); lua_gettable(L, LUA_REGISTRYINDEX); S = (lprofP_STATE*)lua_touserdata(L, -1); /* leave all functions under execution */ while (lprofP_callhookOUT(S)) ; /* call the original Lua 'exit' function */ lua_pushlightuserdata(L, &exit_id); lua_gettable(L, LUA_REGISTRYINDEX); lua_call(L, 0, 0); } /* Our new coroutine.create function */ /* Creates a new profile state for the coroutine */ #if 0 static int coroutine_create(lua_State *L) { lprofP_STATE* S; lua_State *NL = lua_newthread(L); luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, "Lua function expected"); lua_pushvalue(L, 1); /* move function to top */ lua_xmove(L, NL, 1); /* move function from L to NL */ /* Inits profiler and sets profiler hook for this coroutine */ S = lprofM_init(); lua_pushlightuserdata(L, NL); lua_pushlightuserdata(L, S); lua_settable(L, LUA_REGISTRYINDEX); lua_sethook(NL, (lua_Hook)callhook, LUA_MASKCALL | LUA_MASKRET, 0); return 1; } #endif static int profiler_pause(lua_State *L) { lprofP_STATE* S; lua_pushlightuserdata(L, &profstate_id); lua_gettable(L, LUA_REGISTRYINDEX); S = (lprofP_STATE*)lua_touserdata(L, -1); lprofM_pause_function(S); return 0; } static int profiler_resume(lua_State *L) { lprofP_STATE* S; lua_pushlightuserdata(L, &profstate_id); lua_gettable(L, LUA_REGISTRYINDEX); S = (lprofP_STATE*)lua_touserdata(L, -1); lprofM_pause_function(S); return 0; } static int profiler_init(lua_State *L) { lprofP_STATE* S; const char* outfile; float function_call_time; function_call_time = calcCallTime(L); outfile = NULL; if(lua_gettop(L) == 1) outfile = luaL_checkstring(L, -1); lua_sethook(L, (lua_Hook)callhook, LUA_MASKCALL | LUA_MASKRET, 0); /* init with default file name and printing a header line */ if (!(S=lprofP_init_core_profiler(outfile, 1, function_call_time))) { luaL_error(L,"LuaProfiler error: output file could not be opened!"); lua_pushnil(L); return 1; } lua_pushlightuserdata(L, &profstate_id); lua_pushlightuserdata(L, S); lua_settable(L, LUA_REGISTRYINDEX); /* use our own exit function instead */ lua_getglobal(L, "os"); lua_pushlightuserdata(L, &exit_id); lua_pushstring(L, "exit"); lua_gettable(L, -3); lua_settable(L, LUA_REGISTRYINDEX); lua_pushstring(L, "exit"); lua_pushcfunction(L, (lua_CFunction)exit_profiler); lua_settable(L, -3); #if 0 /* use our own coroutine.create function instead */ lua_getglobal(L, "coroutine"); lua_pushstring(L, "create"); lua_pushcfunction(L, (lua_CFunction)coroutine_create); lua_settable(L, -3); #endif /* the following statement is to simulate how the execution stack is */ /* supposed to be by the time the profiler is activated when loaded */ /* as a library. */ lprofP_callhookIN(S, "profiler_init", "(C)", -1, -1); lua_pushboolean(L, 1); return 1; } static int profiler_stop(lua_State *L) { lprofP_STATE* S; lua_sethook(L, (lua_Hook)callhook, 0, 0); lua_pushlightuserdata(L, &profstate_id); lua_gettable(L, LUA_REGISTRYINDEX); if(!lua_isnil(L, -1)) { S = (lprofP_STATE*)lua_touserdata(L, -1); /* leave all functions under execution */ while (lprofP_callhookOUT(S)); lprofP_close_core_profiler(S); lua_pushboolean(L, 1); } else { lua_pushboolean(L, 0); } return 1; } /* calculates the approximate time Lua takes to call a function */ static float calcCallTime(lua_State *L) { clock_t timer; char lua_code[] = " \ function lprofT_mesure_function() \ local i \ \ local t = function() \ end \ \ i = 1 \ while (i < 100000) do \ t() \ i = i + 1 \ end \ end \ \ lprofT_mesure_function() \ lprofT_mesure_function = nil \ "; lprofC_start_timer(&timer); luaL_dostring(L, lua_code); return lprofC_get_seconds(timer) / (float) 100000; } static const luaL_reg prof_funcs[] = { { "pause", profiler_pause }, { "resume", profiler_resume }, { "start", profiler_init }, { "stop", profiler_stop }, { NULL, NULL } }; int luaopen_profiler(lua_State *L) { luaL_openlib(L, "profiler", prof_funcs, 0); lua_pushliteral (L, "_COPYRIGHT"); lua_pushliteral (L, "Copyright (C) 2003-2007 Kepler Project"); lua_settable (L, -3); lua_pushliteral (L, "_DESCRIPTION"); lua_pushliteral (L, "LuaProfiler is a time profiler designed to help finding bottlenecks in your Lua program."); lua_settable (L, -3); lua_pushliteral (L, "_NAME"); lua_pushliteral (L, "LuaProfiler"); lua_settable (L, -3); lua_pushliteral (L, "_VERSION"); lua_pushliteral (L, "2.0.1"); lua_settable (L, -3); return 1; }