ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

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