3 ** Copyright Kepler Project 2005-2007 (http://www.keplerproject.org/luaprofiler)
4 ** $Id: lua50_profiler.c,v 1.13 2008/05/19 18:36:23 mascarenhas Exp $
7 /*****************************************************************************
9 Lua version dependent profiler interface
10 *****************************************************************************/
17 #include "core_profiler.h"
18 #include "function_meter.h"
23 /* Indices for the main profiler stack and for the original exit function */
25 static int profstate_id
;
27 /* Forward declaration */
28 static float calcCallTime(lua_State
*L
);
30 /* called by Lua (via the callhook mechanism) */
31 static void callhook(lua_State
*L
, lua_Debug
*ar
) {
33 lua_Debug previous_ar
;
35 lua_pushlightuserdata(L
, &profstate_id
);
36 lua_gettable(L
, LUA_REGISTRYINDEX
);
37 S
= (lprofP_STATE
*)lua_touserdata(L
, -1);
39 if (lua_getstack(L
, 1, &previous_ar
) == 0) {
42 lua_getinfo(L
, "l", &previous_ar
);
43 currentline
= previous_ar
.currentline
;
46 lua_getinfo(L
, "nS", ar
);
49 /* entering a function */
50 lprofP_callhookIN(S
, (char *)ar
->name
,
51 (char *)ar
->source
, ar
->linedefined
,
54 else { /* ar->event == "return" */
55 lprofP_callhookOUT(S
);
60 /* Lua function to exit politely the profiler */
61 /* redefines the lua exit() function to not break the log file integrity */
62 /* The log file is assumed to be valid if the last entry has a stack level */
63 /* of 1 (meaning that the function 'main' has been exited) */
64 static void exit_profiler(lua_State
*L
) {
66 lua_pushlightuserdata(L
, &profstate_id
);
67 lua_gettable(L
, LUA_REGISTRYINDEX
);
68 S
= (lprofP_STATE
*)lua_touserdata(L
, -1);
69 /* leave all functions under execution */
70 while (lprofP_callhookOUT(S
)) ;
71 /* call the original Lua 'exit' function */
72 lua_pushlightuserdata(L
, &exit_id
);
73 lua_gettable(L
, LUA_REGISTRYINDEX
);
77 /* Our new coroutine.create function */
78 /* Creates a new profile state for the coroutine */
80 static int coroutine_create(lua_State
*L
) {
82 lua_State
*NL
= lua_newthread(L
);
83 luaL_argcheck(L
, lua_isfunction(L
, 1) && !lua_iscfunction(L
, 1), 1,
84 "Lua function expected");
85 lua_pushvalue(L
, 1); /* move function to top */
86 lua_xmove(L
, NL
, 1); /* move function from L to NL */
87 /* Inits profiler and sets profiler hook for this coroutine */
89 lua_pushlightuserdata(L
, NL
);
90 lua_pushlightuserdata(L
, S
);
91 lua_settable(L
, LUA_REGISTRYINDEX
);
92 lua_sethook(NL
, (lua_Hook
)callhook
, LUA_MASKCALL
| LUA_MASKRET
, 0);
97 static int profiler_pause(lua_State
*L
) {
99 lua_pushlightuserdata(L
, &profstate_id
);
100 lua_gettable(L
, LUA_REGISTRYINDEX
);
101 S
= (lprofP_STATE
*)lua_touserdata(L
, -1);
102 lprofM_pause_function(S
);
106 static int profiler_resume(lua_State
*L
) {
108 lua_pushlightuserdata(L
, &profstate_id
);
109 lua_gettable(L
, LUA_REGISTRYINDEX
);
110 S
= (lprofP_STATE
*)lua_touserdata(L
, -1);
111 lprofM_pause_function(S
);
115 static int profiler_init(lua_State
*L
) {
118 float function_call_time
;
120 function_call_time
= calcCallTime(L
);
123 if(lua_gettop(L
) == 1)
124 outfile
= luaL_checkstring(L
, -1);
126 lua_sethook(L
, (lua_Hook
)callhook
, LUA_MASKCALL
| LUA_MASKRET
, 0);
127 /* init with default file name and printing a header line */
128 if (!(S
=lprofP_init_core_profiler(outfile
, 1, function_call_time
))) {
129 luaL_error(L
,"LuaProfiler error: output file could not be opened!");
134 lua_pushlightuserdata(L
, &profstate_id
);
135 lua_pushlightuserdata(L
, S
);
136 lua_settable(L
, LUA_REGISTRYINDEX
);
138 /* use our own exit function instead */
139 lua_getglobal(L
, "os");
140 lua_pushlightuserdata(L
, &exit_id
);
141 lua_pushstring(L
, "exit");
143 lua_settable(L
, LUA_REGISTRYINDEX
);
144 lua_pushstring(L
, "exit");
145 lua_pushcfunction(L
, (lua_CFunction
)exit_profiler
);
149 /* use our own coroutine.create function instead */
150 lua_getglobal(L
, "coroutine");
151 lua_pushstring(L
, "create");
152 lua_pushcfunction(L
, (lua_CFunction
)coroutine_create
);
156 /* the following statement is to simulate how the execution stack is */
157 /* supposed to be by the time the profiler is activated when loaded */
160 lprofP_callhookIN(S
, "profiler_init", "(C)", -1, -1);
162 lua_pushboolean(L
, 1);
166 static int profiler_stop(lua_State
*L
) {
168 lua_sethook(L
, (lua_Hook
)callhook
, 0, 0);
169 lua_pushlightuserdata(L
, &profstate_id
);
170 lua_gettable(L
, LUA_REGISTRYINDEX
);
171 if(!lua_isnil(L
, -1)) {
172 S
= (lprofP_STATE
*)lua_touserdata(L
, -1);
173 /* leave all functions under execution */
174 while (lprofP_callhookOUT(S
));
175 lprofP_close_core_profiler(S
);
176 lua_pushboolean(L
, 1);
177 } else { lua_pushboolean(L
, 0); }
181 /* calculates the approximate time Lua takes to call a function */
182 static float calcCallTime(lua_State
*L
) {
184 char lua_code
[] = " \
185 function lprofT_mesure_function() \
188 local t = function() \
192 while (i < 100000) do \
198 lprofT_mesure_function() \
199 lprofT_mesure_function = nil \
202 lprofC_start_timer(&timer
);
203 luaL_dostring(L
, lua_code
);
204 return lprofC_get_seconds(timer
) / (float) 100000;
207 static const luaL_Reg prof_funcs
[] = {
208 { "pause", profiler_pause
},
209 { "resume", profiler_resume
},
210 { "start", profiler_init
},
211 { "stop", profiler_stop
},
215 int luaopen_profiler(lua_State
*L
) {
216 luaL_openlib(L
, "profiler", prof_funcs
, 0);
217 lua_pushliteral (L
, "_COPYRIGHT");
218 lua_pushliteral (L
, "Copyright (C) 2003-2007 Kepler Project");
219 lua_settable (L
, -3);
220 lua_pushliteral (L
, "_DESCRIPTION");
221 lua_pushliteral (L
, "LuaProfiler is a time profiler designed to help finding bottlenecks in your Lua program.");
222 lua_settable (L
, -3);
223 lua_pushliteral (L
, "_NAME");
224 lua_pushliteral (L
, "LuaProfiler");
225 lua_settable (L
, -3);
226 lua_pushliteral (L
, "_VERSION");
227 lua_pushliteral (L
, "2.0.1");
228 lua_settable (L
, -3);