comparison third_party/luajit/src/lj_profile.c @ 186:8cf4ec5e2191 hg-web

Fixed merge conflict.
author MrJuneJune <me@mrjunejune.com>
date Fri, 23 Jan 2026 22:38:59 -0800
parents 94705b5986b3
children
comparison
equal deleted inserted replaced
176:fed99fc04e12 186:8cf4ec5e2191
1 /*
2 ** Low-overhead profiling.
3 ** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
4 */
5
6 #define lj_profile_c
7 #define LUA_CORE
8
9 #include "lj_obj.h"
10
11 #if LJ_HASPROFILE
12
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"
22
23 #include "luajit.h"
24
25 #if LJ_PROFILE_SIGPROF
26
27 #include <sys/time.h>
28 #include <signal.h>
29 #define profile_lock(ps) UNUSED(ps)
30 #define profile_unlock(ps) UNUSED(ps)
31
32 #elif LJ_PROFILE_PTHREAD
33
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)
41
42 #elif LJ_PROFILE_WTHREAD
43
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)
54
55 #endif
56
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;
83
84 /* Sadly, we have to use a static profiler state.
85 **
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.
89 */
90 static ProfileState profile_state;
91
92 /* Default sample interval in milliseconds. */
93 #define LJ_PROFILE_INTERVAL_DEFAULT 10
94
95 /* -- Profiler/hook interaction ------------------------------------------- */
96
97 #if !LJ_PROFILE_SIGPROF
98 void LJ_FASTCALL lj_profile_hook_enter(global_State *g)
99 {
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);
107 }
108 }
109
110 void LJ_FASTCALL lj_profile_hook_leave(global_State *g)
111 {
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);
119 }
120 }
121 #endif
122
123 /* -- Profile callbacks --------------------------------------------------- */
124
125 /* Callback from profile hook (HOOK_PROFILE already cleared). */
126 void LJ_FASTCALL lj_profile_interpreter(lua_State *L)
127 {
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);
142 }
143 g->hookmask = mask;
144 lj_dispatch_update(g);
145 profile_unlock(ps);
146 }
147
148 /* Trigger profile hook. Asynchronous call from OS-specific profile timer. */
149 static void profile_trigger(ProfileState *ps)
150 {
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);
164 }
165 profile_unlock(ps);
166 }
167
168 /* -- OS-specific profile timer handling ---------------------------------- */
169
170 #if LJ_PROFILE_SIGPROF
171
172 /* SIGPROF handler. */
173 static void profile_signal(int sig)
174 {
175 UNUSED(sig);
176 profile_trigger(&profile_state);
177 }
178
179 /* Start profiling timer. */
180 static void profile_timer_start(ProfileState *ps)
181 {
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);
196 }
197
198 /* Stop profiling timer. */
199 static void profile_timer_stop(ProfileState *ps)
200 {
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);
206 }
207
208 #elif LJ_PROFILE_PTHREAD
209
210 /* POSIX timer thread. */
211 static void *profile_thread(ProfileState *ps)
212 {
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);
227 }
228 return NULL;
229 }
230
231 /* Start profiling timer thread. */
232 static void profile_timer_start(ProfileState *ps)
233 {
234 pthread_mutex_init(&ps->lock, 0);
235 ps->abort = 0;
236 pthread_create(&ps->thread, NULL, (void *(*)(void *))profile_thread, ps);
237 }
238
239 /* Stop profiling timer thread. */
240 static void profile_timer_stop(ProfileState *ps)
241 {
242 ps->abort = 1;
243 pthread_join(ps->thread, NULL);
244 pthread_mutex_destroy(&ps->lock);
245 }
246
247 #elif LJ_PROFILE_WTHREAD
248
249 /* Windows timer thread. */
250 static DWORD WINAPI profile_thread(void *psx)
251 {
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);
261 }
262 #if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP
263 ps->wmm_tep(interval);
264 #endif
265 return 0;
266 }
267
268 /* Start profiling timer thread. */
269 static void profile_timer_start(ProfileState *ps)
270 {
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;
280 }
281 }
282 }
283 #endif
284 InitializeCriticalSection(&ps->lock);
285 ps->abort = 0;
286 ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL);
287 }
288
289 /* Stop profiling timer thread. */
290 static void profile_timer_stop(ProfileState *ps)
291 {
292 ps->abort = 1;
293 WaitForSingleObject(ps->thread, INFINITE);
294 DeleteCriticalSection(&ps->lock);
295 }
296
297 #endif
298
299 /* -- Public profiling API ------------------------------------------------ */
300
301 /* Start profiling. */
302 LUA_API void luaJIT_profile_start(lua_State *L, const char *mode,
303 luaJIT_profile_callback cb, void *data)
304 {
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;
324 }
325 }
326 if (ps->g) {
327 luaJIT_profile_stop(L);
328 if (ps->g) return; /* Profiler in use by another VM. */
329 }
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);
337 }
338
339 /* Stop profiling. */
340 LUA_API void luaJIT_profile_stop(lua_State *L)
341 {
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;
355 }
356 }
357
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)
361 {
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;
369 }
370
371 #endif