ARM64: Fix IR_HREF code generation for constant FP keys.
[luajit-2.0.git] / src / lj_profile.c
blob8cefd5fb31db4f5427128908e637bfd8bcf2428e
1 /*
2 ** Low-overhead profiling.
3 ** Copyright (C) 2005-2023 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|HOOK_GC))) { /* 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 #if LJ_TARGET_QNX
189 sa.sa_flags = 0;
190 #else
191 sa.sa_flags = SA_RESTART;
192 #endif
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)
201 struct itimerval tm;
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;
214 #if !LJ_TARGET_PS3
215 struct timespec ts;
216 ts.tv_sec = interval / 1000;
217 ts.tv_nsec = (interval % 1000) * 1000000;
218 #endif
219 while (1) {
220 #if LJ_TARGET_PS3
221 sys_timer_usleep(interval * 1000);
222 #else
223 nanosleep(&ts, NULL);
224 #endif
225 if (ps->abort) break;
226 profile_trigger(ps);
228 return NULL;
231 /* Start profiling timer thread. */
232 static void profile_timer_start(ProfileState *ps)
234 pthread_mutex_init(&ps->lock, 0);
235 ps->abort = 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)
242 ps->abort = 1;
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);
256 #endif
257 while (1) {
258 Sleep(interval);
259 if (ps->abort) break;
260 profile_trigger(ps);
262 #if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP
263 ps->wmm_tep(interval);
264 #endif
265 return 0;
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");
274 if (ps->wmm) {
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) {
278 ps->wmm = NULL;
279 return;
283 #endif
284 InitializeCriticalSection(&ps->lock);
285 ps->abort = 0;
286 ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL);
289 /* Stop profiling timer thread. */
290 static void profile_timer_stop(ProfileState *ps)
292 ps->abort = 1;
293 WaitForSingleObject(ps->thread, INFINITE);
294 DeleteCriticalSection(&ps->lock);
297 #endif
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;
307 while (*mode) {
308 int m = *mode++;
309 switch (m) {
310 case 'i':
311 interval = 0;
312 while (*mode >= '0' && *mode <= '9')
313 interval = interval * 10 + (*mode++ - '0');
314 if (interval <= 0) interval = 1;
315 break;
316 #if LJ_HASJIT
317 case 'l': case 'f':
318 L2J(L)->prof_mode = m;
319 lj_trace_flushall(L);
320 break;
321 #endif
322 default: /* Ignore unknown mode chars. */
323 break;
326 if (ps->g) {
327 luaJIT_profile_stop(L);
328 if (ps->g) return; /* Profiler in use by another VM. */
330 ps->g = G(L);
331 ps->interval = interval;
332 ps->cb = cb;
333 ps->data = data;
334 ps->samples = 0;
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);
348 #if LJ_HASJIT
349 G2J(g)->prof_mode = 0;
350 lj_trace_flushall(L);
351 #endif
352 lj_buf_free(g, &ps->sb);
353 ps->sb.w = ps->sb.e = NULL;
354 ps->g = 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;
363 SBuf *sb = &ps->sb;
364 setsbufL(sb, L);
365 lj_buf_reset(sb);
366 lj_debug_dumpstack(L, sb, fmt, depth);
367 *len = (size_t)sbuflen(sb);
368 return sb->b;
371 #endif