Mercurial
view third_party/luajit/src/lj_profile.c @ 279:b3b547563ec7
Add Google connector service and agent wiki
Implement the C/Seobeo Google Drive and Gmail connector with encrypted OAuth storage, Zenbu authentication, browser testing, AI tool discovery, chunked HTTP decoding, and Bazel coverage. Consolidate repository guidance into progressive wiki documentation and enforce arena-first allocation for new first-party C code.
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 84c338fd-0939-4bb3-b7f3-1062eb213e5d
| author | MrJuneJune <me@mrjunejune.com> |
|---|---|
| date | Mon, 17 Aug 2026 22:22:36 -0700 |
| parents | 94705b5986b3 |
| children |
line wrap: on
line source
/* ** Low-overhead profiling. ** Copyright (C) 2005-2023 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