diff --git a/gamedata/scripts/lua_help_ex.script b/gamedata/scripts/lua_help_ex.script index 57df322bf..a79496085 100644 --- a/gamedata/scripts/lua_help_ex.script +++ b/gamedata/scripts/lua_help_ex.script @@ -148,6 +148,10 @@ jit.allocprof.resume() // re-arm and continue capturing } + jit.util { // additions beyond stock jit.util + table jit.util.gcstat() // {total, threshold, estimate, debt} in bytes plus cycles = completed GC collections since start; read-only snapshot, values may be mid-cycle under parallel GC + } + globals { string dik_to_keyname(number dik, boolean localize) int get_modded_exes_version() // Returns modded exes version in integer format diff --git a/src/3rd party/luajit-2/src/lib_jit.c b/src/3rd party/luajit-2/src/lib_jit.c index 27f937453..c30f1452a 100644 --- a/src/3rd party/luajit-2/src/lib_jit.c +++ b/src/3rd party/luajit-2/src/lib_jit.c @@ -178,6 +178,12 @@ static void setintfield(lua_State *L, GCtab *t, const char *name, int32_t val) setintV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val); } +/* GC counters are MSize (uint32); a double holds the full range without the int32 wrap. */ +static void setnumfield(lua_State *L, GCtab *t, const char *name, lua_Number val) +{ + setnumV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val); +} + /* local info = jit.util.funcinfo(func [,pc]) */ LJLIB_CF(jit_util_funcinfo) { @@ -268,6 +274,29 @@ LJLIB_CF(jit_util_funcuvname) return 0; } +extern uint32_t lj_gc_cycles; /* Defined in lj_gc.c: completed GC cycles since start. */ + +/* local info = jit.util.gcstat() -- GC counters: total, threshold, estimate, debt (bytes) and cycles (completed collections) */ +LJLIB_CF(jit_util_gcstat) +{ + global_State *g = G(L); + /* Snapshot before allocating: lua_createtable/lj_str_newz can run a GC step that moves these. */ + lua_Number total = (lua_Number)g->gc.total; + lua_Number threshold = (lua_Number)g->gc.threshold; + lua_Number estimate = (lua_Number)g->gc.estimate; + lua_Number debt = (lua_Number)g->gc.debt; + lua_Number cycles = (lua_Number)lj_gc_cycles; + GCtab *t; + lua_createtable(L, 0, 5); + t = tabV(L->top-1); + setnumfield(L, t, "total", total); + setnumfield(L, t, "threshold", threshold); + setnumfield(L, t, "estimate", estimate); + setnumfield(L, t, "debt", debt); + setnumfield(L, t, "cycles", cycles); + return 1; +} + /* -- Reflection API for traces ------------------------------------------- */ #if LJ_HASJIT @@ -544,8 +573,9 @@ static void jit_profile_callback(lua_State *L2, lua_State *L, int samples, setstrV(L2, L2->top++, lj_str_new(L2, &vmst, 1)); status = lua_pcall(L2, 3, 0, 0); /* callback(thread, samples, vmstate) */ if (status) { + /* Panic logs and returns; drop the sample and keep sampling, never exit the game. */ if (G(L2)->panic) G(L2)->panic(L2); - exit(EXIT_FAILURE); + lua_settop(L2, 0); } lj_trace_abort(G(L2)); } diff --git a/src/3rd party/luajit-2/src/lj_allocprof.c b/src/3rd party/luajit-2/src/lj_allocprof.c index 12acdae44..3fa089cd0 100644 --- a/src/3rd party/luajit-2/src/lj_allocprof.c +++ b/src/3rd party/luajit-2/src/lj_allocprof.c @@ -16,6 +16,7 @@ #include "luajit.h" #include +#include #include #define ALLOCPROF_LEAF_SLOTS 4096 @@ -37,8 +38,8 @@ static struct { uint8_t saved_mask; int32_t saved_count; int32_t saved_cstart; - AllocProfEntry leaf[ALLOCPROF_LEAF_SLOTS]; - AllocProfEntry stack[ALLOCPROF_STACK_SLOTS]; + AllocProfEntry *leaf; /* Heap, first start; kept so dump reads work after stop. */ + AllocProfEntry *stack; } AS; static uint32_t allocprof_hash(const char *s, size_t n) @@ -54,7 +55,12 @@ static void allocprof_record(AllocProfEntry *tab, int slots, int *used, { size_t n = strlen(key); uint32_t h, i; - if (n >= sizeof(tab->key)) n = sizeof(tab->key) - 1; + if (n >= sizeof(tab->key)) { + /* Cut at the last ';' so a partial frame drops whole, never mid-name. */ + size_t j = sizeof(tab->key) - 1; + while (j > 0 && key[j] != ';') j--; + n = j > 0 ? j : sizeof(tab->key) - 1; + } h = allocprof_hash(key, n) & (uint32_t)(slots - 1); for (i = 0; i < (uint32_t)slots; i++) { AllocProfEntry *e = &tab[(h + i) & (uint32_t)(slots - 1)]; @@ -123,7 +129,20 @@ void lj_allocprof_start(lua_State *L, int depth) } if (depth < 1) depth = 1; if (depth > ALLOCPROF_MAX_DEPTH) depth = ALLOCPROF_MAX_DEPTH; - memset(&AS, 0, sizeof(AS)); + if (!AS.leaf) { + AS.leaf = (AllocProfEntry *)calloc(ALLOCPROF_LEAF_SLOTS, sizeof(AllocProfEntry)); + AS.stack = (AllocProfEntry *)calloc(ALLOCPROF_STACK_SLOTS, sizeof(AllocProfEntry)); + if (!AS.leaf || !AS.stack) { + free(AS.leaf); free(AS.stack); + AS.leaf = NULL; AS.stack = NULL; + return; + } + } else { + memset(AS.leaf, 0, ALLOCPROF_LEAF_SLOTS * sizeof(AllocProfEntry)); + memset(AS.stack, 0, ALLOCPROF_STACK_SLOTS * sizeof(AllocProfEntry)); + } + AS.leaf_used = 0; + AS.stack_used = 0; AS.g = g; AS.depth = depth; AS.saved_mask = g->hookmask; @@ -157,9 +176,9 @@ void lj_allocprof_stop(lua_State *L) void lj_allocprof_reset(void) { - if (!AS.g) return; - memset(AS.leaf, 0, sizeof(AS.leaf)); - memset(AS.stack, 0, sizeof(AS.stack)); + if (!(AS.g && AS.leaf)) return; + memset(AS.leaf, 0, ALLOCPROF_LEAF_SLOTS * sizeof(AllocProfEntry)); + memset(AS.stack, 0, ALLOCPROF_STACK_SLOTS * sizeof(AllocProfEntry)); AS.leaf_used = 0; AS.stack_used = 0; lj_allocprof_dropped = 0; @@ -208,7 +227,7 @@ int lj_allocprof_slots(int stacks) const AllocProfEntry *lj_allocprof_slot(int stacks, int k) { AllocProfEntry *tab = stacks ? AS.stack : AS.leaf; - return tab[k].bytes ? &tab[k] : NULL; + return (tab && tab[k].bytes) ? &tab[k] : NULL; } #endif diff --git a/src/3rd party/luajit-2/src/lj_allocprof.h b/src/3rd party/luajit-2/src/lj_allocprof.h index 99fe50c77..6a7dc83d0 100644 --- a/src/3rd party/luajit-2/src/lj_allocprof.h +++ b/src/3rd party/luajit-2/src/lj_allocprof.h @@ -10,7 +10,7 @@ #if LJ_HASPROFILE typedef struct AllocProfEntry { - char key[512]; + char key[4096]; uint64_t bytes; } AllocProfEntry; diff --git a/src/3rd party/luajit-2/src/lj_gc.c b/src/3rd party/luajit-2/src/lj_gc.c index bed48ddd5..83eae78e5 100644 --- a/src/3rd party/luajit-2/src/lj_gc.c +++ b/src/3rd party/luajit-2/src/lj_gc.c @@ -603,6 +603,10 @@ static void atomic(global_State *g, lua_State *L) } /* GC state machine. Returns a cost estimate for each step performed. */ +/* Completed GC cycles since process start, read via jit.util.gcstat. A file-scope +** global, not a GCState field, so it does not shift global_State layout. */ +uint32_t lj_gc_cycles = 0; + static size_t gc_onestep(lua_State *L) { global_State *g = G(L); @@ -646,6 +650,7 @@ static size_t gc_onestep(lua_State *L) } else { /* Otherwise skip this phase to help the JIT. */ g->gc.state = GCSpause; /* End of GC cycle. */ g->gc.debt = 0; + lj_gc_cycles++; } } return GCSWEEPMAX*GCSWEEPCOST; @@ -664,6 +669,7 @@ static size_t gc_onestep(lua_State *L) #endif g->gc.state = GCSpause; /* End of GC cycle. */ g->gc.debt = 0; + lj_gc_cycles++; return 0; default: lua_assert(0); diff --git a/src/3rd party/luajit-2/src/lj_profile.c b/src/3rd party/luajit-2/src/lj_profile.c index cb02b793b..c56aad76c 100644 --- a/src/3rd party/luajit-2/src/lj_profile.c +++ b/src/3rd party/luajit-2/src/lj_profile.c @@ -23,6 +23,7 @@ #include "lua.h" #include +#include #if LJ_PROFILE_SIGPROF @@ -249,6 +250,20 @@ static void profile_timer_stop(ProfileState *ps) #elif LJ_PROFILE_WTHREAD +static uint64_t profile_rng = 0x2545f4914f6cdd1dull; + +/* Randomized sleep from exp(interval): breaks phase-lock so periodic work cannot alias the sampler. */ +static int profile_jitter(int interval) +{ + uint64_t x = profile_rng; + double u, d; + x ^= x << 13; x ^= x >> 7; x ^= x << 17; + profile_rng = x; + u = ((double)(x >> 11) + 1.0) / 9007199254740993.0; + d = -(double)interval * log(u); + return d < 1.0 ? 1 : (int)d; +} + /* Windows timer thread. */ static DWORD WINAPI profile_thread(void *psx) { @@ -256,7 +271,7 @@ static DWORD WINAPI profile_thread(void *psx) int interval = ps->interval; if (ps->wmm_tbp) ps->wmm_tbp(interval); while (1) { - Sleep(interval); + Sleep((DWORD)profile_jitter(interval)); if (ps->abort) break; profile_trigger(ps); } @@ -342,8 +357,8 @@ 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); /* Join the timer thread first: no profile_trigger can read a cleared g. */ ps->g = NULL; - profile_timer_stop(ps); g->hookmask &= (uint8_t)~(HOOK_PROFILE | (ps->added_maskline ? LUA_MASKLINE : 0)); g->hookcount = ps->saved_count; @@ -356,7 +371,7 @@ LUA_API void luaJIT_profile_stop(lua_State *L) LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, int depth, size_t *len) { - static char buf[2048]; + static char buf[8192]; size_t n = 0; int level; int maxd = depth < 0 ? -depth : depth;