aboutsummaryrefslogtreecommitdiff
path: root/src/lj_profile.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lj_profile.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/lj_profile.c b/src/lj_profile.c
new file mode 100644
index 00000000..a58aefc8
--- /dev/null
+++ b/src/lj_profile.c
@@ -0,0 +1,289 @@
1/*
2** Low-overhead profiling.
3** Copyright (C) 2005-2013 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
30#elif LJ_PROFILE_PTHREAD
31
32#include <pthread.h>
33
34#elif LJ_PROFILE_WTHREAD
35
36#define WIN32_LEAN_AND_MEAN
37#include <windows.h>
38typedef unsigned int (WINAPI *WMM_TPFUNC)(unsigned int);
39
40#endif
41
42/* Profiler state. */
43typedef struct ProfileState {
44 global_State *g; /* VM state that started the profiler. */
45 luaJIT_profile_callback cb; /* Profiler callback. */
46 void *data; /* Profiler callback data. */
47 SBuf sb; /* String buffer for stack dumps. */
48 int interval; /* Sample interval in milliseconds. */
49 int samples; /* Number of samples for next callback. */
50 int vmstate; /* VM state when profile timer triggered. */
51#if LJ_PROFILE_SIGPROF
52 struct sigaction oldsa; /* Previous SIGPROF state. */
53#elif LJ_PROFILE_PTHREAD
54 pthread_t thread; /* Timer thread. */
55 int abort; /* Abort timer thread. */
56#elif LJ_PROFILE_WTHREAD
57 HINSTANCE wmm; /* WinMM library handle. */
58 WMM_TPFUNC wmm_tbp; /* WinMM timeBeginPeriod function. */
59 WMM_TPFUNC wmm_tep; /* WinMM timeEndPeriod function. */
60 HANDLE thread; /* Timer thread. */
61 int abort; /* Abort timer thread. */
62#endif
63} ProfileState;
64
65/* Sadly, we have to use a static profiler state.
66**
67** The SIGPROF variant needs a static pointer to the global state, anyway.
68** And it would be hard to extend for multiple threads. You can still use
69** multiple VMs in multiple threads, but only profile one at a time.
70*/
71static ProfileState profile_state;
72
73/* Default sample interval in milliseconds. */
74#define LJ_PROFILE_INTERVAL_DEFAULT 10
75
76/* -- Profile callbacks --------------------------------------------------- */
77
78/* Callback from profile hook (HOOK_PROFILE already cleared). */
79void LJ_FASTCALL lj_profile_interpreter(lua_State *L)
80{
81 ProfileState *ps = &profile_state;
82 int samples = ps->samples;
83 ps->samples = 0;
84 ps->cb(ps->data, L, samples, ps->vmstate); /* Invoke user callback. */
85}
86
87/* Trigger profile hook. Asynchronous call from OS-specific profile timer. */
88static void profile_trigger(ProfileState *ps)
89{
90 global_State *g = ps->g;
91 uint8_t mask;
92 ps->samples++; /* Always increment number of samples. */
93 mask = g->hookmask;
94 if (!(mask & HOOK_PROFILE)) { /* Set profile hook, unless already set. */
95 int st = g->vmstate;
96 ps->vmstate = st >= 0 ? 'N' :
97 st == ~LJ_VMST_INTERP ? 'I' :
98 st == ~LJ_VMST_C ? 'C' :
99 st == ~LJ_VMST_GC ? 'G' : 'J';
100 g->hookmask = (mask | HOOK_PROFILE);
101 lj_dispatch_update(g);
102 }
103}
104
105/* -- OS-specific profile timer handling ---------------------------------- */
106
107#if LJ_PROFILE_SIGPROF
108
109/* SIGPROF handler. */
110static void profile_signal(int sig)
111{
112 UNUSED(sig);
113 profile_trigger(&profile_state);
114}
115
116/* Start profiling timer. */
117static void profile_timer_start(ProfileState *ps)
118{
119 int interval = ps->interval;
120 struct itimerval tm;
121 struct sigaction sa;
122 tm.it_value.tv_sec = tm.it_interval.tv_sec = interval / 1000;
123 tm.it_value.tv_usec = tm.it_interval.tv_usec = (interval % 1000) * 1000;
124 setitimer(ITIMER_PROF, &tm, NULL);
125 sa.sa_flags = SA_RESTART;
126 sa.sa_handler = profile_signal;
127 sigemptyset(&sa.sa_mask);
128 sigaction(SIGPROF, &sa, &ps->oldsa);
129}
130
131/* Stop profiling timer. */
132static void profile_timer_stop(ProfileState *ps)
133{
134 struct itimerval tm;
135 tm.it_value.tv_sec = tm.it_interval.tv_sec = 0;
136 tm.it_value.tv_usec = tm.it_interval.tv_usec = 0;
137 setitimer(ITIMER_PROF, &tm, NULL);
138 sigaction(SIGPROF, &ps->oldsa, NULL);
139}
140
141#elif LJ_PROFILE_PTHREAD
142
143/* POSIX timer thread. */
144static void *profile_thread(ProfileState *ps)
145{
146 int interval = ps->interval;
147 struct timespec ts;
148 ts.tv_sec = interval / 1000;
149 ts.tv_nsec = (interval % 1000) * 1000000;
150 while (1) {
151 nanosleep(&ts, NULL);
152 if (ps->abort) break;
153 profile_trigger(ps);
154 }
155 return NULL;
156}
157
158/* Start profiling timer thread. */
159static void profile_timer_start(ProfileState *ps)
160{
161 ps->abort = 0;
162 pthread_create(&ps->thread, NULL, (void *(*)(void *))profile_thread, ps);
163}
164
165/* Stop profiling timer thread. */
166static void profile_timer_stop(ProfileState *ps)
167{
168 ps->abort = 1;
169 pthread_join(ps->thread, NULL);
170}
171
172#elif LJ_PROFILE_WTHREAD
173
174/* Windows timer thread. */
175static DWORD WINAPI profile_thread(void *psx)
176{
177 ProfileState *ps = (ProfileState *)psx;
178 int interval = ps->interval;
179 ps->wmm_tbp(1);
180 while (1) {
181 Sleep(interval);
182 if (ps->abort) break;
183 profile_trigger(ps);
184 }
185 ps->wmm_tep(1);
186 return 0;
187}
188
189/* Start profiling timer thread. */
190static void profile_timer_start(ProfileState *ps)
191{
192 if (!ps->wmm) { /* Load WinMM library on-demand. */
193 ps->wmm = LoadLibraryA("winmm.dll");
194 if (ps->wmm) {
195 ps->wmm_tbp = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeBeginPeriod");
196 ps->wmm_tep = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeEndPeriod");
197 if (!ps->wmm_tbp || !ps->wmm_tep) {
198 ps->wmm = NULL;
199 return;
200 }
201 }
202 }
203 ps->abort = 0;
204 ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL);
205}
206
207/* Stop profiling timer thread. */
208static void profile_timer_stop(ProfileState *ps)
209{
210 ps->abort = 1;
211 WaitForSingleObject(ps->thread, INFINITE);
212}
213
214#endif
215
216/* -- Public profiling API ------------------------------------------------ */
217
218/* Start profiling. */
219LUA_API void luaJIT_profile_start(lua_State *L, const char *mode,
220 luaJIT_profile_callback cb, void *data)
221{
222 ProfileState *ps = &profile_state;
223 int interval = LJ_PROFILE_INTERVAL_DEFAULT;
224 while (*mode) {
225 int m = *mode++;
226 switch (m) {
227 case 'i':
228 interval = 0;
229 while (*mode >= '0' && *mode <= '9')
230 interval = interval * 10 + (*mode++ - '0');
231 if (interval <= 0) interval = 1;
232 break;
233#if LJ_HASJIT
234 case 'l': case 'f':
235 L2J(L)->prof_mode = m;
236 lj_trace_flushall(L);
237 break;
238#endif
239 default: /* Ignore unknown mode chars. */
240 break;
241 }
242 }
243 if (ps->g) {
244 luaJIT_profile_stop(L);
245 if (ps->g) return; /* Profiler in use by another VM. */
246 }
247 ps->g = G(L);
248 ps->interval = interval;
249 ps->cb = cb;
250 ps->data = data;
251 ps->samples = 0;
252 lj_buf_init(L, &ps->sb);
253 profile_timer_start(ps);
254}
255
256/* Stop profiling. */
257LUA_API void luaJIT_profile_stop(lua_State *L)
258{
259 ProfileState *ps = &profile_state;
260 global_State *g = ps->g;
261 if (G(L) == g) { /* Only stop profiler if started by this VM. */
262 profile_timer_stop(ps);
263 g->hookmask &= ~HOOK_PROFILE;
264 lj_dispatch_update(g);
265#if LJ_HASJIT
266 G2J(g)->prof_mode = 0;
267 lj_trace_flushall(L);
268#endif
269 lj_buf_free(g, &ps->sb);
270 setmref(ps->sb.b, NULL);
271 setmref(ps->sb.e, NULL);
272 ps->g = NULL;
273 }
274}
275
276/* Return a compact stack dump. */
277LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt,
278 int depth, size_t *len)
279{
280 ProfileState *ps = &profile_state;
281 SBuf *sb = &ps->sb;
282 setsbufL(sb, L);
283 lj_buf_reset(sb);
284 lj_debug_dumpstack(L, sb, fmt, depth);
285 *len = (size_t)sbuflen(sb);
286 return sbufB(sb);
287}
288
289#endif