root/lj_profile.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lj_profile_hook_enter
  2. lj_profile_hook_leave
  3. lj_profile_interpreter
  4. profile_trigger
  5. profile_signal
  6. profile_timer_start
  7. profile_timer_stop
  8. profile_thread
  9. profile_timer_start
  10. profile_timer_stop
  11. profile_thread
  12. profile_timer_start
  13. profile_timer_stop
  14. luaJIT_profile_start
  15. luaJIT_profile_stop
  16. luaJIT_profile_dumpstack

   1 /*
   2 ** Low-overhead profiling.
   3 ** Copyright (C) 2005-2017 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

/* [<][>][^][v][top][bottom][index][help] */