beta-0.89.2
[luatex.git] / source / libs / luajit / LuaJIT-src / src / lj_profile.c
blobc7e53963b5b526a791b8bce617482b3db03e33b5
1 /*
2 ** Low-overhead profiling.
3 ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h
4 */
6 #define lj_profile_c
7 #define LUA_CORE
9 #include "lj_obj.h"
11 #if LJ_HASPROFILE
13 #include "lj_buf.h"
14 #include "lj_frame.h"
15 #include "lj_debug.h"
16 #include "lj_dispatch.h"
17 #if LJ_HASJIT
18 #include "lj_jit.h"
19 #include "lj_trace.h"
20 #endif
21 #include "lj_profile.h"
23 #include "luajit.h"
25 #if LJ_PROFILE_SIGPROF
27 #include <sys/time.h>
28 #include <signal.h>
29 #define profile_lock(ps) UNUSED(ps)
30 #define profile_unlock(ps) UNUSED(ps)
32 #elif LJ_PROFILE_PTHREAD
34 #include <pthread.h>
35 #include <time.h>
36 #if LJ_TARGET_PS3
37 #include <sys/timer.h>
38 #endif
39 #define profile_lock(ps) pthread_mutex_lock(&ps->lock)
40 #define profile_unlock(ps) pthread_mutex_unlock(&ps->lock)
42 #elif LJ_PROFILE_WTHREAD
44 #define WIN32_LEAN_AND_MEAN
45 #if LJ_TARGET_XBOX360
46 #include <xtl.h>
47 #include <xbox.h>
48 #else
49 #include <windows.h>
50 #endif
51 typedef unsigned int (WINAPI *WMM_TPFUNC)(unsigned int);
52 #define profile_lock(ps) EnterCriticalSection(&ps->lock)
53 #define profile_unlock(ps) LeaveCriticalSection(&ps->lock)
55 #endif
57 /* Profiler state. */
58 typedef struct ProfileState {
59 global_State *g; /* VM state that started the profiler. */
60 luaJIT_profile_callback cb; /* Profiler callback. */
61 void *data; /* Profiler callback data. */
62 SBuf sb; /* String buffer for stack dumps. */
63 int interval; /* Sample interval in milliseconds. */
64 int samples; /* Number of samples for next callback. */
65 int vmstate; /* VM state when profile timer triggered. */
66 #if LJ_PROFILE_SIGPROF
67 struct sigaction oldsa; /* Previous SIGPROF state. */
68 #elif LJ_PROFILE_PTHREAD
69 pthread_mutex_t lock; /* g->hookmask update lock. */
70 pthread_t thread; /* Timer thread. */
71 int abort; /* Abort timer thread. */
72 #elif LJ_PROFILE_WTHREAD
73 #if LJ_TARGET_WINDOWS
74 HINSTANCE wmm; /* WinMM library handle. */
75 WMM_TPFUNC wmm_tbp; /* WinMM timeBeginPeriod function. */
76 WMM_TPFUNC wmm_tep; /* WinMM timeEndPeriod function. */
77 #endif
78 CRITICAL_SECTION lock; /* g->hookmask update lock. */
79 HANDLE thread; /* Timer thread. */
80 int abort; /* Abort timer thread. */
81 #endif
82 } ProfileState;
84 /* Sadly, we have to use a static profiler state.
86 ** The SIGPROF variant needs a static pointer to the global state, anyway.
87 ** And it would be hard to extend for multiple threads. You can still use
88 ** multiple VMs in multiple threads, but only profile one at a time.
90 static ProfileState profile_state;
92 /* Default sample interval in milliseconds. */
93 #define LJ_PROFILE_INTERVAL_DEFAULT 10
95 /* -- Profiler/hook interaction ------------------------------------------- */
97 #if !LJ_PROFILE_SIGPROF
98 void LJ_FASTCALL lj_profile_hook_enter(global_State *g)
100 ProfileState *ps = &profile_state;
101 if (ps->g) {
102 profile_lock(ps);
103 hook_enter(g);
104 profile_unlock(ps);
105 } else {
106 hook_enter(g);
110 void LJ_FASTCALL lj_profile_hook_leave(global_State *g)
112 ProfileState *ps = &profile_state;
113 if (ps->g) {
114 profile_lock(ps);
115 hook_leave(g);
116 profile_unlock(ps);
117 } else {
118 hook_leave(g);
121 #endif
123 /* -- Profile callbacks --------------------------------------------------- */
125 /* Callback from profile hook (HOOK_PROFILE already cleared). */
126 void LJ_FASTCALL lj_profile_interpreter(lua_State *L)
128 ProfileState *ps = &profile_state;
129 global_State *g = G(L);
130 uint8_t mask;
131 profile_lock(ps);
132 mask = (g->hookmask & ~HOOK_PROFILE);
133 if (!(mask & HOOK_VMEVENT)) {
134 int samples = ps->samples;
135 ps->samples = 0;
136 g->hookmask = HOOK_VMEVENT;
137 lj_dispatch_update(g);
138 profile_unlock(ps);
139 ps->cb(ps->data, L, samples, ps->vmstate); /* Invoke user callback. */
140 profile_lock(ps);
141 mask |= (g->hookmask & HOOK_PROFILE);
143 g->hookmask = mask;
144 lj_dispatch_update(g);
145 profile_unlock(ps);
148 /* Trigger profile hook. Asynchronous call from OS-specific profile timer. */
149 static void profile_trigger(ProfileState *ps)
151 global_State *g = ps->g;
152 uint8_t mask;
153 profile_lock(ps);
154 ps->samples++; /* Always increment number of samples. */
155 mask = g->hookmask;
156 if (!(mask & (HOOK_PROFILE|HOOK_VMEVENT))) { /* Set profile hook. */
157 int st = g->vmstate;
158 ps->vmstate = st >= 0 ? 'N' :
159 st == ~LJ_VMST_INTERP ? 'I' :
160 st == ~LJ_VMST_C ? 'C' :
161 st == ~LJ_VMST_GC ? 'G' : 'J';
162 g->hookmask = (mask | HOOK_PROFILE);
163 lj_dispatch_update(g);
165 profile_unlock(ps);
168 /* -- OS-specific profile timer handling ---------------------------------- */
170 #if LJ_PROFILE_SIGPROF
172 /* SIGPROF handler. */
173 static void profile_signal(int sig)
175 UNUSED(sig);
176 profile_trigger(&profile_state);
179 /* Start profiling timer. */
180 static void profile_timer_start(ProfileState *ps)
182 int interval = ps->interval;
183 struct itimerval tm;
184 struct sigaction sa;
185 tm.it_value.tv_sec = tm.it_interval.tv_sec = interval / 1000;
186 tm.it_value.tv_usec = tm.it_interval.tv_usec = (interval % 1000) * 1000;
187 setitimer(ITIMER_PROF, &tm, NULL);
188 sa.sa_flags = SA_RESTART;
189 sa.sa_handler = profile_signal;
190 sigemptyset(&sa.sa_mask);
191 sigaction(SIGPROF, &sa, &ps->oldsa);
194 /* Stop profiling timer. */
195 static void profile_timer_stop(ProfileState *ps)
197 struct itimerval tm;
198 tm.it_value.tv_sec = tm.it_interval.tv_sec = 0;
199 tm.it_value.tv_usec = tm.it_interval.tv_usec = 0;
200 setitimer(ITIMER_PROF, &tm, NULL);
201 sigaction(SIGPROF, &ps->oldsa, NULL);
204 #elif LJ_PROFILE_PTHREAD
206 /* POSIX timer thread. */
207 static void *profile_thread(ProfileState *ps)
209 int interval = ps->interval;
210 #if !LJ_TARGET_PS3
211 struct timespec ts;
212 ts.tv_sec = interval / 1000;
213 ts.tv_nsec = (interval % 1000) * 1000000;
214 #endif
215 while (1) {
216 #if LJ_TARGET_PS3
217 sys_timer_usleep(interval * 1000);
218 #else
219 nanosleep(&ts, NULL);
220 #endif
221 if (ps->abort) break;
222 profile_trigger(ps);
224 return NULL;
227 /* Start profiling timer thread. */
228 static void profile_timer_start(ProfileState *ps)
230 pthread_mutex_init(&ps->lock, 0);
231 ps->abort = 0;
232 pthread_create(&ps->thread, NULL, (void *(*)(void *))profile_thread, ps);
235 /* Stop profiling timer thread. */
236 static void profile_timer_stop(ProfileState *ps)
238 ps->abort = 1;
239 pthread_join(ps->thread, NULL);
240 pthread_mutex_destroy(&ps->lock);
243 #elif LJ_PROFILE_WTHREAD
245 /* Windows timer thread. */
246 static DWORD WINAPI profile_thread(void *psx)
248 ProfileState *ps = (ProfileState *)psx;
249 int interval = ps->interval;
250 #if LJ_TARGET_WINDOWS
251 ps->wmm_tbp(interval);
252 #endif
253 while (1) {
254 Sleep(interval);
255 if (ps->abort) break;
256 profile_trigger(ps);
258 #if LJ_TARGET_WINDOWS
259 ps->wmm_tep(interval);
260 #endif
261 return 0;
264 /* Start profiling timer thread. */
265 static void profile_timer_start(ProfileState *ps)
267 #if LJ_TARGET_WINDOWS
268 if (!ps->wmm) { /* Load WinMM library on-demand. */
269 ps->wmm = LoadLibraryExA("winmm.dll", NULL, 0);
270 if (ps->wmm) {
271 ps->wmm_tbp = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeBeginPeriod");
272 ps->wmm_tep = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeEndPeriod");
273 if (!ps->wmm_tbp || !ps->wmm_tep) {
274 ps->wmm = NULL;
275 return;
279 #endif
280 InitializeCriticalSection(&ps->lock);
281 ps->abort = 0;
282 ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL);
285 /* Stop profiling timer thread. */
286 static void profile_timer_stop(ProfileState *ps)
288 ps->abort = 1;
289 WaitForSingleObject(ps->thread, INFINITE);
290 DeleteCriticalSection(&ps->lock);
293 #endif
295 /* -- Public profiling API ------------------------------------------------ */
297 /* Start profiling. */
298 LUA_API void luaJIT_profile_start(lua_State *L, const char *mode,
299 luaJIT_profile_callback cb, void *data)
301 ProfileState *ps = &profile_state;
302 int interval = LJ_PROFILE_INTERVAL_DEFAULT;
303 while (*mode) {
304 int m = *mode++;
305 switch (m) {
306 case 'i':
307 interval = 0;
308 while (*mode >= '0' && *mode <= '9')
309 interval = interval * 10 + (*mode++ - '0');
310 if (interval <= 0) interval = 1;
311 break;
312 #if LJ_HASJIT
313 case 'l': case 'f':
314 L2J(L)->prof_mode = m;
315 lj_trace_flushall(L);
316 break;
317 #endif
318 default: /* Ignore unknown mode chars. */
319 break;
322 if (ps->g) {
323 luaJIT_profile_stop(L);
324 if (ps->g) return; /* Profiler in use by another VM. */
326 ps->g = G(L);
327 ps->interval = interval;
328 ps->cb = cb;
329 ps->data = data;
330 ps->samples = 0;
331 lj_buf_init(L, &ps->sb);
332 profile_timer_start(ps);
335 /* Stop profiling. */
336 LUA_API void luaJIT_profile_stop(lua_State *L)
338 ProfileState *ps = &profile_state;
339 global_State *g = ps->g;
340 if (G(L) == g) { /* Only stop profiler if started by this VM. */
341 profile_timer_stop(ps);
342 g->hookmask &= ~HOOK_PROFILE;
343 lj_dispatch_update(g);
344 #if LJ_HASJIT
345 G2J(g)->prof_mode = 0;
346 lj_trace_flushall(L);
347 #endif
348 lj_buf_free(g, &ps->sb);
349 setmref(ps->sb.b, NULL);
350 setmref(ps->sb.e, NULL);
351 ps->g = NULL;
355 /* Return a compact stack dump. */
356 LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt,
357 int depth, size_t *len)
359 ProfileState *ps = &profile_state;
360 SBuf *sb = &ps->sb;
361 setsbufL(sb, L);
362 lj_buf_reset(sb);
363 lj_debug_dumpstack(L, sb, fmt, depth);
364 *len = (size_t)sbuflen(sb);
365 return sbufB(sb);
368 #endif