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