src/lj_profile.c - luajit-2.0-src

Global variables defined

Data types defined

Functions defined

Macros defined

Source code

  1. /*
  2. ** Low-overhead profiling.
  3. ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h
  4. */

  5. #define lj_profile_c
  6. #define LUA_CORE

  7. #include "lj_obj.h"

  8. #if LJ_HASPROFILE

  9. #include "lj_buf.h"
  10. #include "lj_frame.h"
  11. #include "lj_debug.h"
  12. #include "lj_dispatch.h"
  13. #if LJ_HASJIT
  14. #include "lj_jit.h"
  15. #include "lj_trace.h"
  16. #endif
  17. #include "lj_profile.h"

  18. #include "luajit.h"

  19. #if LJ_PROFILE_SIGPROF

  20. #include <sys/time.h>
  21. #include <signal.h>
  22. #define profile_lock(ps)        UNUSED(ps)
  23. #define profile_unlock(ps)        UNUSED(ps)

  24. #elif LJ_PROFILE_PTHREAD

  25. #include <pthread.h>
  26. #include <time.h>
  27. #if LJ_TARGET_PS3
  28. #include <sys/timer.h>
  29. #endif
  30. #define profile_lock(ps)        pthread_mutex_lock(&ps->lock)
  31. #define profile_unlock(ps)        pthread_mutex_unlock(&ps->lock)

  32. #elif LJ_PROFILE_WTHREAD

  33. #define WIN32_LEAN_AND_MEAN
  34. #if LJ_TARGET_XBOX360
  35. #include <xtl.h>
  36. #include <xbox.h>
  37. #else
  38. #include <windows.h>
  39. #endif
  40. typedef unsigned int (WINAPI *WMM_TPFUNC)(unsigned int);
  41. #define profile_lock(ps)        EnterCriticalSection(&ps->lock)
  42. #define profile_unlock(ps)        LeaveCriticalSection(&ps->lock)

  43. #endif

  44. /* Profiler state. */
  45. typedef struct ProfileState {
  46.   global_State *g;                /* VM state that started the profiler. */
  47.   luaJIT_profile_callback cb;        /* Profiler callback. */
  48.   void *data;                        /* Profiler callback data. */
  49.   SBuf sb;                        /* String buffer for stack dumps. */
  50.   int interval;                        /* Sample interval in milliseconds. */
  51.   int samples;                        /* Number of samples for next callback. */
  52.   int vmstate;                        /* VM state when profile timer triggered. */
  53. #if LJ_PROFILE_SIGPROF
  54.   struct sigaction oldsa;        /* Previous SIGPROF state. */
  55. #elif LJ_PROFILE_PTHREAD
  56.   pthread_mutex_t lock;                /* g->hookmask update lock. */
  57.   pthread_t thread;                /* Timer thread. */
  58.   int abort;                        /* Abort timer thread. */
  59. #elif LJ_PROFILE_WTHREAD
  60. #if LJ_TARGET_WINDOWS
  61.   HINSTANCE wmm;                /* WinMM library handle. */
  62.   WMM_TPFUNC wmm_tbp;                /* WinMM timeBeginPeriod function. */
  63.   WMM_TPFUNC wmm_tep;                /* WinMM timeEndPeriod function. */
  64. #endif
  65.   CRITICAL_SECTION lock;        /* g->hookmask update lock. */
  66.   HANDLE thread;                /* Timer thread. */
  67.   int abort;                        /* Abort timer thread. */
  68. #endif
  69. } ProfileState;

  70. /* Sadly, we have to use a static profiler state.
  71. **
  72. ** The SIGPROF variant needs a static pointer to the global state, anyway.
  73. ** And it would be hard to extend for multiple threads. You can still use
  74. ** multiple VMs in multiple threads, but only profile one at a time.
  75. */
  76. static ProfileState profile_state;

  77. /* Default sample interval in milliseconds. */
  78. #define LJ_PROFILE_INTERVAL_DEFAULT        10

  79. /* -- Profiler/hook interaction ------------------------------------------- */

  80. #if !LJ_PROFILE_SIGPROF
  81. void LJ_FASTCALL lj_profile_hook_enter(global_State *g)
  82. {
  83.   ProfileState *ps = &profile_state;
  84.   if (ps->g) {
  85.     profile_lock(ps);
  86.     hook_enter(g);
  87.     profile_unlock(ps);
  88.   } else {
  89.     hook_enter(g);
  90.   }
  91. }

  92. void LJ_FASTCALL lj_profile_hook_leave(global_State *g)
  93. {
  94.   ProfileState *ps = &profile_state;
  95.   if (ps->g) {
  96.     profile_lock(ps);
  97.     hook_leave(g);
  98.     profile_unlock(ps);
  99.   } else {
  100.     hook_leave(g);
  101.   }
  102. }
  103. #endif

  104. /* -- Profile callbacks --------------------------------------------------- */

  105. /* Callback from profile hook (HOOK_PROFILE already cleared). */
  106. void LJ_FASTCALL lj_profile_interpreter(lua_State *L)
  107. {
  108.   ProfileState *ps = &profile_state;
  109.   global_State *g = G(L);
  110.   uint8_t mask;
  111.   profile_lock(ps);
  112.   mask = (g->hookmask & ~HOOK_PROFILE);
  113.   if (!(mask & HOOK_VMEVENT)) {
  114.     int samples = ps->samples;
  115.     ps->samples = 0;
  116.     g->hookmask = HOOK_VMEVENT;
  117.     lj_dispatch_update(g);
  118.     profile_unlock(ps);
  119.     ps->cb(ps->data, L, samples, ps->vmstate);  /* Invoke user callback. */
  120.     profile_lock(ps);
  121.     mask |= (g->hookmask & HOOK_PROFILE);
  122.   }
  123.   g->hookmask = mask;
  124.   lj_dispatch_update(g);
  125.   profile_unlock(ps);
  126. }

  127. /* Trigger profile hook. Asynchronous call from OS-specific profile timer. */
  128. static void profile_trigger(ProfileState *ps)
  129. {
  130.   global_State *g = ps->g;
  131.   uint8_t mask;
  132.   profile_lock(ps);
  133.   ps->samples++;  /* Always increment number of samples. */
  134.   mask = g->hookmask;
  135.   if (!(mask & (HOOK_PROFILE|HOOK_VMEVENT))) {  /* Set profile hook. */
  136.     int st = g->vmstate;
  137.     ps->vmstate = st >= 0 ? 'N' :
  138.                   st == ~LJ_VMST_INTERP ? 'I' :
  139.                   st == ~LJ_VMST_C ? 'C' :
  140.                   st == ~LJ_VMST_GC ? 'G' : 'J';
  141.     g->hookmask = (mask | HOOK_PROFILE);
  142.     lj_dispatch_update(g);
  143.   }
  144.   profile_unlock(ps);
  145. }

  146. /* -- OS-specific profile timer handling ---------------------------------- */

  147. #if LJ_PROFILE_SIGPROF

  148. /* SIGPROF handler. */
  149. static void profile_signal(int sig)
  150. {
  151.   UNUSED(sig);
  152.   profile_trigger(&profile_state);
  153. }

  154. /* Start profiling timer. */
  155. static void profile_timer_start(ProfileState *ps)
  156. {
  157.   int interval = ps->interval;
  158.   struct itimerval tm;
  159.   struct sigaction sa;
  160.   tm.it_value.tv_sec = tm.it_interval.tv_sec = interval / 1000;
  161.   tm.it_value.tv_usec = tm.it_interval.tv_usec = (interval % 1000) * 1000;
  162.   setitimer(ITIMER_PROF, &tm, NULL);
  163.   sa.sa_flags = SA_RESTART;
  164.   sa.sa_handler = profile_signal;
  165.   sigemptyset(&sa.sa_mask);
  166.   sigaction(SIGPROF, &sa, &ps->oldsa);
  167. }

  168. /* Stop profiling timer. */
  169. static void profile_timer_stop(ProfileState *ps)
  170. {
  171.   struct itimerval tm;
  172.   tm.it_value.tv_sec = tm.it_interval.tv_sec = 0;
  173.   tm.it_value.tv_usec = tm.it_interval.tv_usec = 0;
  174.   setitimer(ITIMER_PROF, &tm, NULL);
  175.   sigaction(SIGPROF, &ps->oldsa, NULL);
  176. }

  177. #elif LJ_PROFILE_PTHREAD

  178. /* POSIX timer thread. */
  179. static void *profile_thread(ProfileState *ps)
  180. {
  181.   int interval = ps->interval;
  182. #if !LJ_TARGET_PS3
  183.   struct timespec ts;
  184.   ts.tv_sec = interval / 1000;
  185.   ts.tv_nsec = (interval % 1000) * 1000000;
  186. #endif
  187.   while (1) {
  188. #if LJ_TARGET_PS3
  189.     sys_timer_usleep(interval * 1000);
  190. #else
  191.     nanosleep(&ts, NULL);
  192. #endif
  193.     if (ps->abort) break;
  194.     profile_trigger(ps);
  195.   }
  196.   return NULL;
  197. }

  198. /* Start profiling timer thread. */
  199. static void profile_timer_start(ProfileState *ps)
  200. {
  201.   pthread_mutex_init(&ps->lock, 0);
  202.   ps->abort = 0;
  203.   pthread_create(&ps->thread, NULL, (void *(*)(void *))profile_thread, ps);
  204. }

  205. /* Stop profiling timer thread. */
  206. static void profile_timer_stop(ProfileState *ps)
  207. {
  208.   ps->abort = 1;
  209.   pthread_join(ps->thread, NULL);
  210.   pthread_mutex_destroy(&ps->lock);
  211. }

  212. #elif LJ_PROFILE_WTHREAD

  213. /* Windows timer thread. */
  214. static DWORD WINAPI profile_thread(void *psx)
  215. {
  216.   ProfileState *ps = (ProfileState *)psx;
  217.   int interval = ps->interval;
  218. #if LJ_TARGET_WINDOWS
  219.   ps->wmm_tbp(interval);
  220. #endif
  221.   while (1) {
  222.     Sleep(interval);
  223.     if (ps->abort) break;
  224.     profile_trigger(ps);
  225.   }
  226. #if LJ_TARGET_WINDOWS
  227.   ps->wmm_tep(interval);
  228. #endif
  229.   return 0;
  230. }

  231. /* Start profiling timer thread. */
  232. static void profile_timer_start(ProfileState *ps)
  233. {
  234. #if LJ_TARGET_WINDOWS
  235.   if (!ps->wmm) {  /* Load WinMM library on-demand. */
  236.     ps->wmm = LoadLibraryA("winmm.dll");
  237.     if (ps->wmm) {
  238.       ps->wmm_tbp = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeBeginPeriod");
  239.       ps->wmm_tep = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeEndPeriod");
  240.       if (!ps->wmm_tbp || !ps->wmm_tep) {
  241.         ps->wmm = NULL;
  242.         return;
  243.       }
  244.     }
  245.   }
  246. #endif
  247.   InitializeCriticalSection(&ps->lock);
  248.   ps->abort = 0;
  249.   ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL);
  250. }

  251. /* Stop profiling timer thread. */
  252. static void profile_timer_stop(ProfileState *ps)
  253. {
  254.   ps->abort = 1;
  255.   WaitForSingleObject(ps->thread, INFINITE);
  256.   DeleteCriticalSection(&ps->lock);
  257. }

  258. #endif

  259. /* -- Public profiling API ------------------------------------------------ */

  260. /* Start profiling. */
  261. LUA_API void luaJIT_profile_start(lua_State *L, const char *mode,
  262.                                   luaJIT_profile_callback cb, void *data)
  263. {
  264.   ProfileState *ps = &profile_state;
  265.   int interval = LJ_PROFILE_INTERVAL_DEFAULT;
  266.   while (*mode) {
  267.     int m = *mode++;
  268.     switch (m) {
  269.     case 'i':
  270.       interval = 0;
  271.       while (*mode >= '0' && *mode <= '9')
  272.         interval = interval * 10 + (*mode++ - '0');
  273.       if (interval <= 0) interval = 1;
  274.       break;
  275. #if LJ_HASJIT
  276.     case 'l': case 'f':
  277.       L2J(L)->prof_mode = m;
  278.       lj_trace_flushall(L);
  279.       break;
  280. #endif
  281.     default:  /* Ignore unknown mode chars. */
  282.       break;
  283.     }
  284.   }
  285.   if (ps->g) {
  286.     luaJIT_profile_stop(L);
  287.     if (ps->g) return/* Profiler in use by another VM. */
  288.   }
  289.   ps->g = G(L);
  290.   ps->interval = interval;
  291.   ps->cb = cb;
  292.   ps->data = data;
  293.   ps->samples = 0;
  294.   lj_buf_init(L, &ps->sb);
  295.   profile_timer_start(ps);
  296. }

  297. /* Stop profiling. */
  298. LUA_API void luaJIT_profile_stop(lua_State *L)
  299. {
  300.   ProfileState *ps = &profile_state;
  301.   global_State *g = ps->g;
  302.   if (G(L) == g) {  /* Only stop profiler if started by this VM. */
  303.     profile_timer_stop(ps);
  304.     g->hookmask &= ~HOOK_PROFILE;
  305.     lj_dispatch_update(g);
  306. #if LJ_HASJIT
  307.     G2J(g)->prof_mode = 0;
  308.     lj_trace_flushall(L);
  309. #endif
  310.     lj_buf_free(g, &ps->sb);
  311.     setmref(ps->sb.b, NULL);
  312.     setmref(ps->sb.e, NULL);
  313.     ps->g = NULL;
  314.   }
  315. }

  316. /* Return a compact stack dump. */
  317. LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt,
  318.                                              int depth, size_t *len)
  319. {
  320.   ProfileState *ps = &profile_state;
  321.   SBuf *sb = &ps->sb;
  322.   setsbufL(sb, L);
  323.   lj_buf_reset(sb);
  324.   lj_debug_dumpstack(L, sb, fmt, depth);
  325.   *len = (size_t)sbuflen(sb);
  326.   return sbufB(sb);
  327. }

  328. #endif