lj_profile.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /*
  2. ** Low-overhead profiling.
  3. ** Copyright (C) 2005-2016 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 = LoadLibraryExA("winmm.dll", NULL, 0);
  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