1 /*
2 ** LuaProfiler
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 $
5 */
6 
7 /*****************************************************************************
8 lua50_profiler.c:
9    Lua version dependent profiler interface
10 *****************************************************************************/
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include "clocks.h"
17 #include "core_profiler.h"
18 #include "function_meter.h"
19 
20 #include "lua.h"
21 #include "lauxlib.h"
22 
23 /* Indices for the main profiler stack and for the original exit function */
24 static int exit_id;
25 static int profstate_id;
26 
27 /* Forward declaration */
28 static float calcCallTime(lua_State *L);
29 
30 /* called by Lua (via the callhook mechanism) */
callhook(lua_State * L,lua_Debug * ar)31 static void callhook(lua_State *L, lua_Debug *ar) {
32   int currentline;
33   lua_Debug previous_ar;
34   lprofP_STATE* S;
35   lua_pushlightuserdata(L, &profstate_id);
36   lua_gettable(L, LUA_REGISTRYINDEX);
37   S = (lprofP_STATE*)lua_touserdata(L, -1);
38 
39   if (lua_getstack(L, 1, &previous_ar) == 0) {
40     currentline = -1;
41   } else {
42     lua_getinfo(L, "l", &previous_ar);
43     currentline = previous_ar.currentline;
44   }
45 
46   lua_getinfo(L, "nS", ar);
47 
48   if (!ar->event) {
49     /* entering a function */
50     lprofP_callhookIN(S, (char *)ar->name,
51 		      (char *)ar->source, ar->linedefined,
52 		      currentline);
53   }
54   else { /* ar->event == "return" */
55     lprofP_callhookOUT(S);
56   }
57 }
58 
59 
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)                  */
exit_profiler(lua_State * L)64 static void exit_profiler(lua_State *L) {
65   lprofP_STATE* S;
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);
74   lua_call(L, 0, 0);
75 }
76 
77 /* Our new coroutine.create function  */
78 /* Creates a new profile state for the coroutine */
79 #if 0
80 static int coroutine_create(lua_State *L) {
81   lprofP_STATE* S;
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 */
88   S = lprofM_init();
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);
93   return 1;
94 }
95 #endif
96 
profiler_pause(lua_State * L)97 static int profiler_pause(lua_State *L) {
98   lprofP_STATE* S;
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);
103   return 0;
104 }
105 
profiler_resume(lua_State * L)106 static int profiler_resume(lua_State *L) {
107   lprofP_STATE* S;
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);
112   return 0;
113 }
114 
profiler_init(lua_State * L)115 static int profiler_init(lua_State *L) {
116   lprofP_STATE* S;
117   const char* outfile;
118   float function_call_time;
119 
120   function_call_time = calcCallTime(L);
121 
122   outfile = NULL;
123   if(lua_gettop(L) == 1)
124     outfile = luaL_checkstring(L, -1);
125 
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!");
130     lua_pushnil(L);
131     return 1;
132   }
133 
134   lua_pushlightuserdata(L, &profstate_id);
135   lua_pushlightuserdata(L, S);
136   lua_settable(L, LUA_REGISTRYINDEX);
137 
138   /* use our own exit function instead */
139   lua_getglobal(L, "os");
140   lua_pushlightuserdata(L, &exit_id);
141   lua_pushstring(L, "exit");
142   lua_gettable(L, -3);
143   lua_settable(L, LUA_REGISTRYINDEX);
144   lua_pushstring(L, "exit");
145   lua_pushcfunction(L, (lua_CFunction)exit_profiler);
146   lua_settable(L, -3);
147 
148 #if 0
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);
153   lua_settable(L, -3);
154 #endif
155 
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  */
158   /* as a library.                                                     */
159 
160   lprofP_callhookIN(S, "profiler_init", "(C)", -1, -1);
161 
162   lua_pushboolean(L, 1);
163   return 1;
164 }
165 
profiler_stop(lua_State * L)166 static int profiler_stop(lua_State *L) {
167   lprofP_STATE* S;
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); }
178   return 1;
179 }
180 
181 /* calculates the approximate time Lua takes to call a function */
calcCallTime(lua_State * L)182 static float calcCallTime(lua_State *L) {
183   clock_t timer;
184   char lua_code[] = "                                     \
185                    function lprofT_mesure_function()    \
186                    local i                              \
187                                                         \
188                       local t = function()              \
189                       end                               \
190                                                         \
191                       i = 1                             \
192                       while (i < 100000) do             \
193                          t()                            \
194                          i = i + 1                      \
195                       end                               \
196                    end                                  \
197                                                         \
198                    lprofT_mesure_function()             \
199                    lprofT_mesure_function = nil         \
200                  ";
201 
202   lprofC_start_timer(&timer);
203   luaL_dostring(L, lua_code);
204   return lprofC_get_seconds(timer) / (float) 100000;
205 }
206 
207 static const luaL_Reg prof_funcs[] = {
208   { "pause", profiler_pause },
209   { "resume", profiler_resume },
210   { "start", profiler_init },
211   { "stop", profiler_stop },
212   { NULL, NULL }
213 };
214 
luaopen_profiler(lua_State * L)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);
229   return 1;
230 }
231