summaryrefslogtreecommitdiff
path: root/libs/luajit-cmake/luajit/src/lj_profile.c
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2023-03-11 15:58:20 -0600
committersanine <sanine.not@pm.me>2023-03-11 15:58:20 -0600
commitebc50b387ab209c9f9a0d92e340ac293d5697274 (patch)
treeea8c8b3677a18c994d2b9d33dbef3461dcf18113 /libs/luajit-cmake/luajit/src/lj_profile.c
parentc2329b4c8258baa9429c77566c9def97d00e96d7 (diff)
build & link with luajit instead of lua5.1
Diffstat (limited to 'libs/luajit-cmake/luajit/src/lj_profile.c')
-rw-r--r--libs/luajit-cmake/luajit/src/lj_profile.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/libs/luajit-cmake/luajit/src/lj_profile.c b/libs/luajit-cmake/luajit/src/lj_profile.c
new file mode 100644
index 0000000..4a13537
--- /dev/null
+++ b/libs/luajit-cmake/luajit/src/lj_profile.c
@@ -0,0 +1,371 @@
+/*
+** Low-overhead profiling.
+** Copyright (C) 2005-2022 Mike Pall. See Copyright Notice in luajit.h
+*/
+
+#define lj_profile_c
+#define LUA_CORE
+
+#include "lj_obj.h"
+
+#if LJ_HASPROFILE
+
+#include "lj_buf.h"
+#include "lj_frame.h"
+#include "lj_debug.h"
+#include "lj_dispatch.h"
+#if LJ_HASJIT
+#include "lj_jit.h"
+#include "lj_trace.h"
+#endif
+#include "lj_profile.h"
+
+#include "luajit.h"
+
+#if LJ_PROFILE_SIGPROF
+
+#include <sys/time.h>
+#include <signal.h>
+#define profile_lock(ps) UNUSED(ps)
+#define profile_unlock(ps) UNUSED(ps)
+
+#elif LJ_PROFILE_PTHREAD
+
+#include <pthread.h>
+#include <time.h>
+#if LJ_TARGET_PS3
+#include <sys/timer.h>
+#endif
+#define profile_lock(ps) pthread_mutex_lock(&ps->lock)
+#define profile_unlock(ps) pthread_mutex_unlock(&ps->lock)
+
+#elif LJ_PROFILE_WTHREAD
+
+#define WIN32_LEAN_AND_MEAN
+#if LJ_TARGET_XBOX360
+#include <xtl.h>
+#include <xbox.h>
+#else
+#include <windows.h>
+#endif
+typedef unsigned int (WINAPI *WMM_TPFUNC)(unsigned int);
+#define profile_lock(ps) EnterCriticalSection(&ps->lock)
+#define profile_unlock(ps) LeaveCriticalSection(&ps->lock)
+
+#endif
+
+/* Profiler state. */
+typedef struct ProfileState {
+ global_State *g; /* VM state that started the profiler. */
+ luaJIT_profile_callback cb; /* Profiler callback. */
+ void *data; /* Profiler callback data. */
+ SBuf sb; /* String buffer for stack dumps. */
+ int interval; /* Sample interval in milliseconds. */
+ int samples; /* Number of samples for next callback. */
+ int vmstate; /* VM state when profile timer triggered. */
+#if LJ_PROFILE_SIGPROF
+ struct sigaction oldsa; /* Previous SIGPROF state. */
+#elif LJ_PROFILE_PTHREAD
+ pthread_mutex_t lock; /* g->hookmask update lock. */
+ pthread_t thread; /* Timer thread. */
+ int abort; /* Abort timer thread. */
+#elif LJ_PROFILE_WTHREAD
+#if LJ_TARGET_WINDOWS
+ HINSTANCE wmm; /* WinMM library handle. */
+ WMM_TPFUNC wmm_tbp; /* WinMM timeBeginPeriod function. */
+ WMM_TPFUNC wmm_tep; /* WinMM timeEndPeriod function. */
+#endif
+ CRITICAL_SECTION lock; /* g->hookmask update lock. */
+ HANDLE thread; /* Timer thread. */
+ int abort; /* Abort timer thread. */
+#endif
+} ProfileState;
+
+/* Sadly, we have to use a static profiler state.
+**
+** The SIGPROF variant needs a static pointer to the global state, anyway.
+** And it would be hard to extend for multiple threads. You can still use
+** multiple VMs in multiple threads, but only profile one at a time.
+*/
+static ProfileState profile_state;
+
+/* Default sample interval in milliseconds. */
+#define LJ_PROFILE_INTERVAL_DEFAULT 10
+
+/* -- Profiler/hook interaction ------------------------------------------- */
+
+#if !LJ_PROFILE_SIGPROF
+void LJ_FASTCALL lj_profile_hook_enter(global_State *g)
+{
+ ProfileState *ps = &profile_state;
+ if (ps->g) {
+ profile_lock(ps);
+ hook_enter(g);
+ profile_unlock(ps);
+ } else {
+ hook_enter(g);
+ }
+}
+
+void LJ_FASTCALL lj_profile_hook_leave(global_State *g)
+{
+ ProfileState *ps = &profile_state;
+ if (ps->g) {
+ profile_lock(ps);
+ hook_leave(g);
+ profile_unlock(ps);
+ } else {
+ hook_leave(g);
+ }
+}
+#endif
+
+/* -- Profile callbacks --------------------------------------------------- */
+
+/* Callback from profile hook (HOOK_PROFILE already cleared). */
+void LJ_FASTCALL lj_profile_interpreter(lua_State *L)
+{
+ ProfileState *ps = &profile_state;
+ global_State *g = G(L);
+ uint8_t mask;
+ profile_lock(ps);
+ mask = (g->hookmask & ~HOOK_PROFILE);
+ if (!(mask & HOOK_VMEVENT)) {
+ int samples = ps->samples;
+ ps->samples = 0;
+ g->hookmask = HOOK_VMEVENT;
+ lj_dispatch_update(g);
+ profile_unlock(ps);
+ ps->cb(ps->data, L, samples, ps->vmstate); /* Invoke user callback. */
+ profile_lock(ps);
+ mask |= (g->hookmask & HOOK_PROFILE);
+ }
+ g->hookmask = mask;
+ lj_dispatch_update(g);
+ profile_unlock(ps);
+}
+
+/* Trigger profile hook. Asynchronous call from OS-specific profile timer. */
+static void profile_trigger(ProfileState *ps)
+{
+ global_State *g = ps->g;
+ uint8_t mask;
+ profile_lock(ps);
+ ps->samples++; /* Always increment number of samples. */
+ mask = g->hookmask;
+ if (!(mask & (HOOK_PROFILE|HOOK_VMEVENT|HOOK_GC))) { /* Set profile hook. */
+ int st = g->vmstate;
+ ps->vmstate = st >= 0 ? 'N' :
+ st == ~LJ_VMST_INTERP ? 'I' :
+ st == ~LJ_VMST_C ? 'C' :
+ st == ~LJ_VMST_GC ? 'G' : 'J';
+ g->hookmask = (mask | HOOK_PROFILE);
+ lj_dispatch_update(g);
+ }
+ profile_unlock(ps);
+}
+
+/* -- OS-specific profile timer handling ---------------------------------- */
+
+#if LJ_PROFILE_SIGPROF
+
+/* SIGPROF handler. */
+static void profile_signal(int sig)
+{
+ UNUSED(sig);
+ profile_trigger(&profile_state);
+}
+
+/* Start profiling timer. */
+static void profile_timer_start(ProfileState *ps)
+{
+ int interval = ps->interval;
+ struct itimerval tm;
+ struct sigaction sa;
+ tm.it_value.tv_sec = tm.it_interval.tv_sec = interval / 1000;
+ tm.it_value.tv_usec = tm.it_interval.tv_usec = (interval % 1000) * 1000;
+ setitimer(ITIMER_PROF, &tm, NULL);
+#if LJ_TARGET_QNX
+ sa.sa_flags = 0;
+#else
+ sa.sa_flags = SA_RESTART;
+#endif
+ sa.sa_handler = profile_signal;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGPROF, &sa, &ps->oldsa);
+}
+
+/* Stop profiling timer. */
+static void profile_timer_stop(ProfileState *ps)
+{
+ struct itimerval tm;
+ tm.it_value.tv_sec = tm.it_interval.tv_sec = 0;
+ tm.it_value.tv_usec = tm.it_interval.tv_usec = 0;
+ setitimer(ITIMER_PROF, &tm, NULL);
+ sigaction(SIGPROF, &ps->oldsa, NULL);
+}
+
+#elif LJ_PROFILE_PTHREAD
+
+/* POSIX timer thread. */
+static void *profile_thread(ProfileState *ps)
+{
+ int interval = ps->interval;
+#if !LJ_TARGET_PS3
+ struct timespec ts;
+ ts.tv_sec = interval / 1000;
+ ts.tv_nsec = (interval % 1000) * 1000000;
+#endif
+ while (1) {
+#if LJ_TARGET_PS3
+ sys_timer_usleep(interval * 1000);
+#else
+ nanosleep(&ts, NULL);
+#endif
+ if (ps->abort) break;
+ profile_trigger(ps);
+ }
+ return NULL;
+}
+
+/* Start profiling timer thread. */
+static void profile_timer_start(ProfileState *ps)
+{
+ pthread_mutex_init(&ps->lock, 0);
+ ps->abort = 0;
+ pthread_create(&ps->thread, NULL, (void *(*)(void *))profile_thread, ps);
+}
+
+/* Stop profiling timer thread. */
+static void profile_timer_stop(ProfileState *ps)
+{
+ ps->abort = 1;
+ pthread_join(ps->thread, NULL);
+ pthread_mutex_destroy(&ps->lock);
+}
+
+#elif LJ_PROFILE_WTHREAD
+
+/* Windows timer thread. */
+static DWORD WINAPI profile_thread(void *psx)
+{
+ ProfileState *ps = (ProfileState *)psx;
+ int interval = ps->interval;
+#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP
+ ps->wmm_tbp(interval);
+#endif
+ while (1) {
+ Sleep(interval);
+ if (ps->abort) break;
+ profile_trigger(ps);
+ }
+#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP
+ ps->wmm_tep(interval);
+#endif
+ return 0;
+}
+
+/* Start profiling timer thread. */
+static void profile_timer_start(ProfileState *ps)
+{
+#if LJ_TARGET_WINDOWS && !LJ_TARGET_UWP
+ if (!ps->wmm) { /* Load WinMM library on-demand. */
+ ps->wmm = LJ_WIN_LOADLIBA("winmm.dll");
+ if (ps->wmm) {
+ ps->wmm_tbp = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeBeginPeriod");
+ ps->wmm_tep = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeEndPeriod");
+ if (!ps->wmm_tbp || !ps->wmm_tep) {
+ ps->wmm = NULL;
+ return;
+ }
+ }
+ }
+#endif
+ InitializeCriticalSection(&ps->lock);
+ ps->abort = 0;
+ ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL);
+}
+
+/* Stop profiling timer thread. */
+static void profile_timer_stop(ProfileState *ps)
+{
+ ps->abort = 1;
+ WaitForSingleObject(ps->thread, INFINITE);
+ DeleteCriticalSection(&ps->lock);
+}
+
+#endif
+
+/* -- Public profiling API ------------------------------------------------ */
+
+/* Start profiling. */
+LUA_API void luaJIT_profile_start(lua_State *L, const char *mode,
+ luaJIT_profile_callback cb, void *data)
+{
+ ProfileState *ps = &profile_state;
+ int interval = LJ_PROFILE_INTERVAL_DEFAULT;
+ while (*mode) {
+ int m = *mode++;
+ switch (m) {
+ case 'i':
+ interval = 0;
+ while (*mode >= '0' && *mode <= '9')
+ interval = interval * 10 + (*mode++ - '0');
+ if (interval <= 0) interval = 1;
+ break;
+#if LJ_HASJIT
+ case 'l': case 'f':
+ L2J(L)->prof_mode = m;
+ lj_trace_flushall(L);
+ break;
+#endif
+ default: /* Ignore unknown mode chars. */
+ break;
+ }
+ }
+ if (ps->g) {
+ luaJIT_profile_stop(L);
+ if (ps->g) return; /* Profiler in use by another VM. */
+ }
+ ps->g = G(L);
+ ps->interval = interval;
+ ps->cb = cb;
+ ps->data = data;
+ ps->samples = 0;
+ lj_buf_init(L, &ps->sb);
+ profile_timer_start(ps);
+}
+
+/* Stop profiling. */
+LUA_API void luaJIT_profile_stop(lua_State *L)
+{
+ ProfileState *ps = &profile_state;
+ global_State *g = ps->g;
+ if (G(L) == g) { /* Only stop profiler if started by this VM. */
+ profile_timer_stop(ps);
+ g->hookmask &= ~HOOK_PROFILE;
+ lj_dispatch_update(g);
+#if LJ_HASJIT
+ G2J(g)->prof_mode = 0;
+ lj_trace_flushall(L);
+#endif
+ lj_buf_free(g, &ps->sb);
+ ps->sb.w = ps->sb.e = NULL;
+ ps->g = NULL;
+ }
+}
+
+/* Return a compact stack dump. */
+LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt,
+ int depth, size_t *len)
+{
+ ProfileState *ps = &profile_state;
+ SBuf *sb = &ps->sb;
+ setsbufL(sb, L);
+ lj_buf_reset(sb);
+ lj_debug_dumpstack(L, sb, fmt, depth);
+ *len = (size_t)sbuflen(sb);
+ return sb->b;
+}
+
+#endif