2 ** Low-overhead profiling.
3 ** Copyright (C) 2005-2023 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
|HOOK_GC
))) { /* 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
);
191 sa
.sa_flags
= SA_RESTART
;
193 sa
.sa_handler
= profile_signal
;
194 sigemptyset(&sa
.sa_mask
);
195 sigaction(SIGPROF
, &sa
, &ps
->oldsa
);
198 /* Stop profiling timer. */
199 static void profile_timer_stop(ProfileState
*ps
)
202 tm
.it_value
.tv_sec
= tm
.it_interval
.tv_sec
= 0;
203 tm
.it_value
.tv_usec
= tm
.it_interval
.tv_usec
= 0;
204 setitimer(ITIMER_PROF
, &tm
, NULL
);
205 sigaction(SIGPROF
, &ps
->oldsa
, NULL
);
208 #elif LJ_PROFILE_PTHREAD
210 /* POSIX timer thread. */
211 static void *profile_thread(ProfileState
*ps
)
213 int interval
= ps
->interval
;
216 ts
.tv_sec
= interval
/ 1000;
217 ts
.tv_nsec
= (interval
% 1000) * 1000000;
221 sys_timer_usleep(interval
* 1000);
223 nanosleep(&ts
, NULL
);
225 if (ps
->abort
) break;
231 /* Start profiling timer thread. */
232 static void profile_timer_start(ProfileState
*ps
)
234 pthread_mutex_init(&ps
->lock
, 0);
236 pthread_create(&ps
->thread
, NULL
, (void *(*)(void *))profile_thread
, ps
);
239 /* Stop profiling timer thread. */
240 static void profile_timer_stop(ProfileState
*ps
)
243 pthread_join(ps
->thread
, NULL
);
244 pthread_mutex_destroy(&ps
->lock
);
247 #elif LJ_PROFILE_WTHREAD
249 /* Windows timer thread. */
250 static DWORD WINAPI
profile_thread(void *psx
)
252 ProfileState
*ps
= (ProfileState
*)psx
;
253 int interval
= ps
->interval
;
254 #if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP
255 ps
->wmm_tbp(interval
);
259 if (ps
->abort
) break;
262 #if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP
263 ps
->wmm_tep(interval
);
268 /* Start profiling timer thread. */
269 static void profile_timer_start(ProfileState
*ps
)
271 #if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP
272 if (!ps
->wmm
) { /* Load WinMM library on-demand. */
273 ps
->wmm
= LJ_WIN_LOADLIBA("winmm.dll");
275 ps
->wmm_tbp
= (WMM_TPFUNC
)GetProcAddress(ps
->wmm
, "timeBeginPeriod");
276 ps
->wmm_tep
= (WMM_TPFUNC
)GetProcAddress(ps
->wmm
, "timeEndPeriod");
277 if (!ps
->wmm_tbp
|| !ps
->wmm_tep
) {
284 InitializeCriticalSection(&ps
->lock
);
286 ps
->thread
= CreateThread(NULL
, 0, profile_thread
, ps
, 0, NULL
);
289 /* Stop profiling timer thread. */
290 static void profile_timer_stop(ProfileState
*ps
)
293 WaitForSingleObject(ps
->thread
, INFINITE
);
294 DeleteCriticalSection(&ps
->lock
);
299 /* -- Public profiling API ------------------------------------------------ */
301 /* Start profiling. */
302 LUA_API
void luaJIT_profile_start(lua_State
*L
, const char *mode
,
303 luaJIT_profile_callback cb
, void *data
)
305 ProfileState
*ps
= &profile_state
;
306 int interval
= LJ_PROFILE_INTERVAL_DEFAULT
;
312 while (*mode
>= '0' && *mode
<= '9')
313 interval
= interval
* 10 + (*mode
++ - '0');
314 if (interval
<= 0) interval
= 1;
318 L2J(L
)->prof_mode
= m
;
319 lj_trace_flushall(L
);
322 default: /* Ignore unknown mode chars. */
327 luaJIT_profile_stop(L
);
328 if (ps
->g
) return; /* Profiler in use by another VM. */
331 ps
->interval
= interval
;
335 lj_buf_init(L
, &ps
->sb
);
336 profile_timer_start(ps
);
339 /* Stop profiling. */
340 LUA_API
void luaJIT_profile_stop(lua_State
*L
)
342 ProfileState
*ps
= &profile_state
;
343 global_State
*g
= ps
->g
;
344 if (G(L
) == g
) { /* Only stop profiler if started by this VM. */
345 profile_timer_stop(ps
);
346 g
->hookmask
&= ~HOOK_PROFILE
;
347 lj_dispatch_update(g
);
349 G2J(g
)->prof_mode
= 0;
350 lj_trace_flushall(L
);
352 lj_buf_free(g
, &ps
->sb
);
353 ps
->sb
.w
= ps
->sb
.e
= NULL
;
358 /* Return a compact stack dump. */
359 LUA_API
const char *luaJIT_profile_dumpstack(lua_State
*L
, const char *fmt
,
360 int depth
, size_t *len
)
362 ProfileState
*ps
= &profile_state
;
366 lj_debug_dumpstack(L
, sb
, fmt
, depth
);
367 *len
= (size_t)sbuflen(sb
);