lj_profile.c (9185B)
1 /* 2 ** Low-overhead profiling. 3 ** Copyright (C) 2005-2016 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))) { /* 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 sa.sa_flags = SA_RESTART; 189 sa.sa_handler = profile_signal; 190 sigemptyset(&sa.sa_mask); 191 sigaction(SIGPROF, &sa, &ps->oldsa); 192 } 193 194 /* Stop profiling timer. */ 195 static void profile_timer_stop(ProfileState *ps) 196 { 197 struct itimerval tm; 198 tm.it_value.tv_sec = tm.it_interval.tv_sec = 0; 199 tm.it_value.tv_usec = tm.it_interval.tv_usec = 0; 200 setitimer(ITIMER_PROF, &tm, NULL); 201 sigaction(SIGPROF, &ps->oldsa, NULL); 202 } 203 204 #elif LJ_PROFILE_PTHREAD 205 206 /* POSIX timer thread. */ 207 static void *profile_thread(ProfileState *ps) 208 { 209 int interval = ps->interval; 210 #if !LJ_TARGET_PS3 211 struct timespec ts; 212 ts.tv_sec = interval / 1000; 213 ts.tv_nsec = (interval % 1000) * 1000000; 214 #endif 215 while (1) { 216 #if LJ_TARGET_PS3 217 sys_timer_usleep(interval * 1000); 218 #else 219 nanosleep(&ts, NULL); 220 #endif 221 if (ps->abort) break; 222 profile_trigger(ps); 223 } 224 return NULL; 225 } 226 227 /* Start profiling timer thread. */ 228 static void profile_timer_start(ProfileState *ps) 229 { 230 pthread_mutex_init(&ps->lock, 0); 231 ps->abort = 0; 232 pthread_create(&ps->thread, NULL, (void *(*)(void *))profile_thread, ps); 233 } 234 235 /* Stop profiling timer thread. */ 236 static void profile_timer_stop(ProfileState *ps) 237 { 238 ps->abort = 1; 239 pthread_join(ps->thread, NULL); 240 pthread_mutex_destroy(&ps->lock); 241 } 242 243 #elif LJ_PROFILE_WTHREAD 244 245 /* Windows timer thread. */ 246 static DWORD WINAPI profile_thread(void *psx) 247 { 248 ProfileState *ps = (ProfileState *)psx; 249 int interval = ps->interval; 250 #if LJ_TARGET_WINDOWS 251 ps->wmm_tbp(interval); 252 #endif 253 while (1) { 254 Sleep(interval); 255 if (ps->abort) break; 256 profile_trigger(ps); 257 } 258 #if LJ_TARGET_WINDOWS 259 ps->wmm_tep(interval); 260 #endif 261 return 0; 262 } 263 264 /* Start profiling timer thread. */ 265 static void profile_timer_start(ProfileState *ps) 266 { 267 #if LJ_TARGET_WINDOWS 268 if (!ps->wmm) { /* Load WinMM library on-demand. */ 269 ps->wmm = LoadLibraryExA("winmm.dll", NULL, 0); 270 if (ps->wmm) { 271 ps->wmm_tbp = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeBeginPeriod"); 272 ps->wmm_tep = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeEndPeriod"); 273 if (!ps->wmm_tbp || !ps->wmm_tep) { 274 ps->wmm = NULL; 275 return; 276 } 277 } 278 } 279 #endif 280 InitializeCriticalSection(&ps->lock); 281 ps->abort = 0; 282 ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL); 283 } 284 285 /* Stop profiling timer thread. */ 286 static void profile_timer_stop(ProfileState *ps) 287 { 288 ps->abort = 1; 289 WaitForSingleObject(ps->thread, INFINITE); 290 DeleteCriticalSection(&ps->lock); 291 } 292 293 #endif 294 295 /* -- Public profiling API ------------------------------------------------ */ 296 297 /* Start profiling. */ 298 LUA_API void luaJIT_profile_start(lua_State *L, const char *mode, 299 luaJIT_profile_callback cb, void *data) 300 { 301 ProfileState *ps = &profile_state; 302 int interval = LJ_PROFILE_INTERVAL_DEFAULT; 303 while (*mode) { 304 int m = *mode++; 305 switch (m) { 306 case 'i': 307 interval = 0; 308 while (*mode >= '0' && *mode <= '9') 309 interval = interval * 10 + (*mode++ - '0'); 310 if (interval <= 0) interval = 1; 311 break; 312 #if LJ_HASJIT 313 case 'l': case 'f': 314 L2J(L)->prof_mode = m; 315 lj_trace_flushall(L); 316 break; 317 #endif 318 default: /* Ignore unknown mode chars. */ 319 break; 320 } 321 } 322 if (ps->g) { 323 luaJIT_profile_stop(L); 324 if (ps->g) return; /* Profiler in use by another VM. */ 325 } 326 ps->g = G(L); 327 ps->interval = interval; 328 ps->cb = cb; 329 ps->data = data; 330 ps->samples = 0; 331 lj_buf_init(L, &ps->sb); 332 profile_timer_start(ps); 333 } 334 335 /* Stop profiling. */ 336 LUA_API void luaJIT_profile_stop(lua_State *L) 337 { 338 ProfileState *ps = &profile_state; 339 global_State *g = ps->g; 340 if (G(L) == g) { /* Only stop profiler if started by this VM. */ 341 profile_timer_stop(ps); 342 g->hookmask &= ~HOOK_PROFILE; 343 lj_dispatch_update(g); 344 #if LJ_HASJIT 345 G2J(g)->prof_mode = 0; 346 lj_trace_flushall(L); 347 #endif 348 lj_buf_free(g, &ps->sb); 349 setmref(ps->sb.b, NULL); 350 setmref(ps->sb.e, NULL); 351 ps->g = NULL; 352 } 353 } 354 355 /* Return a compact stack dump. */ 356 LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, 357 int depth, size_t *len) 358 { 359 ProfileState *ps = &profile_state; 360 SBuf *sb = &ps->sb; 361 setsbufL(sb, L); 362 lj_buf_reset(sb); 363 lj_debug_dumpstack(L, sb, fmt, depth); 364 *len = (size_t)sbuflen(sb); 365 return sbufB(sb); 366 } 367 368 #endif