2 ** Low-overhead profiling.
3 ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h
16 #include "lj_dispatch.h"
21 #include "lj_profile.h"
25 #if LJ_PROFILE_SIGPROF
29 #define profile_lock(ps) UNUSED(ps)
30 #define profile_unlock(ps) UNUSED(ps)
32 #elif LJ_PROFILE_PTHREAD
37 #include <sys/timer.h>
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
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)
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
74 HINSTANCE wmm
; /* WinMM library handle. */
75 WMM_TPFUNC wmm_tbp
; /* WinMM timeBeginPeriod function. */
76 WMM_TPFUNC wmm_tep
; /* WinMM timeEndPeriod function. */
78 CRITICAL_SECTION lock
; /* g->hookmask update lock. */
79 HANDLE thread
; /* Timer thread. */
80 int abort
; /* Abort timer thread. */
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
;
110 void LJ_FASTCALL
lj_profile_hook_leave(global_State
*g
)
112 ProfileState
*ps
= &profile_state
;
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
);
132 mask
= (g
->hookmask
& ~HOOK_PROFILE
);
133 if (!(mask
& HOOK_VMEVENT
)) {
134 int samples
= ps
->samples
;
136 g
->hookmask
= HOOK_VMEVENT
;
137 lj_dispatch_update(g
);
139 ps
->cb(ps
->data
, L
, samples
, ps
->vmstate
); /* Invoke user callback. */
141 mask
|= (g
->hookmask
& HOOK_PROFILE
);
144 lj_dispatch_update(g
);
148 /* Trigger profile hook. Asynchronous call from OS-specific profile timer. */
149 static void profile_trigger(ProfileState
*ps
)
151 global_State
*g
= ps
->g
;
154 ps
->samples
++; /* Always increment number of samples. */
156 if (!(mask
& (HOOK_PROFILE
|HOOK_VMEVENT
))) { /* Set profile hook. */
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
);
168 /* -- OS-specific profile timer handling ---------------------------------- */
170 #if LJ_PROFILE_SIGPROF
172 /* SIGPROF handler. */
173 static void profile_signal(int sig
)
176 profile_trigger(&profile_state
);
179 /* Start profiling timer. */
180 static void profile_timer_start(ProfileState
*ps
)
182 int interval
= ps
->interval
;
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
)
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
;
212 ts
.tv_sec
= interval
/ 1000;
213 ts
.tv_nsec
= (interval
% 1000) * 1000000;
217 sys_timer_usleep(interval
* 1000);
219 nanosleep(&ts
, NULL
);
221 if (ps
->abort
) break;
227 /* Start profiling timer thread. */
228 static void profile_timer_start(ProfileState
*ps
)
230 pthread_mutex_init(&ps
->lock
, 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
)
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
);
255 if (ps
->abort
) break;
258 #if LJ_TARGET_WINDOWS
259 ps
->wmm_tep(interval
);
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);
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
) {
280 InitializeCriticalSection(&ps
->lock
);
282 ps
->thread
= CreateThread(NULL
, 0, profile_thread
, ps
, 0, NULL
);
285 /* Stop profiling timer thread. */
286 static void profile_timer_stop(ProfileState
*ps
)
289 WaitForSingleObject(ps
->thread
, INFINITE
);
290 DeleteCriticalSection(&ps
->lock
);
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
;
308 while (*mode
>= '0' && *mode
<= '9')
309 interval
= interval
* 10 + (*mode
++ - '0');
310 if (interval
<= 0) interval
= 1;
314 L2J(L
)->prof_mode
= m
;
315 lj_trace_flushall(L
);
318 default: /* Ignore unknown mode chars. */
323 luaJIT_profile_stop(L
);
324 if (ps
->g
) return; /* Profiler in use by another VM. */
327 ps
->interval
= interval
;
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
);
345 G2J(g
)->prof_mode
= 0;
346 lj_trace_flushall(L
);
348 lj_buf_free(g
, &ps
->sb
);
349 setmref(ps
->sb
.b
, NULL
);
350 setmref(ps
->sb
.e
, 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
;
363 lj_debug_dumpstack(L
, sb
, fmt
, depth
);
364 *len
= (size_t)sbuflen(sb
);