lj_profile.c 9.0 KB

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