summaryrefslogtreecommitdiff
path: root/src/lj_profile.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lj_profile.c')
-rw-r--r--src/lj_profile.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/src/lj_profile.c b/src/lj_profile.c
new file mode 100644
index 00000000..8cefd5fb
--- /dev/null
+++ b/src/lj_profile.c
@@ -0,0 +1,371 @@
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
51typedef 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. */
58typedef 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*/
90static 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
98void 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
110void 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). */
126void 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. */
149static 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. */
173static void profile_signal(int sig)
174{
175 UNUSED(sig);
176 profile_trigger(&profile_state);
177}
178
179/* Start profiling timer. */
180static 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. */
199static 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. */
211static 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. */
232static 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. */
240static 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. */
250static 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. */
269static 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. */
290static 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. */
302LUA_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. */
340LUA_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. */
359LUA_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