From 4b91822d7ef3b46c24364ccafbe67db596d6047e Mon Sep 17 00:00:00 2001 From: Eugene Blikh Date: Mon, 18 May 2026 22:50:45 +0300 Subject: [PATCH] bench/jit_trace: harden mcode arena + jit.off the listener MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two infra fixes for the trace-stability gate, both reproduced on Tarantool 3.8.0-entrypoint / LuaJIT 2.1.0-beta3 / macOS arm64. 1. Default JIT mcode arena (sizemcode=32K, maxmcode=512K) is too small for our hot-path codegen footprint. Roughly 1 in 10 runs the gate reports every check as 'stops=0' with no diagnostic — indistinguishable from a real JIT topology regression. jit.v shows 'failed to allocate mcode memory at hello_pb.lua:890' (the packed lucky_numbers varint loop). Calling jit.opt.start('sizemcode=64','maxmcode=4096') at the top of the gate gives the arena enough headroom to hold both encoder and decoder bodies for the proto3 + proto2 fixtures. 2. The trace listener callback itself can become hot enough to be JIT-compiled, which then races with the function being recorded (recording-while-recording). Reproduced with a fat cb that appends event tuples to a table: starts go up but stops drop to ~0. Calling jit.off(cb) on the listener prevents this. The current cb body happens to dodge this because its branches keep call sites polymorphic — but it's fragile; any future extension (per-event timing, pc context) would re-trigger the bug. Adds the jit.off as defense. Verified 20/20 consecutive runs now report 37/37 passing (was intermittently 0/37 before). Also drops a now-stale "(PLAN.M6)" reference from the header comment. beads-tarantool-protobuf-3o2 --- bench/jit_trace.lua | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/bench/jit_trace.lua b/bench/jit_trace.lua index ce852452b7ac9ba2a14a58cdaba45e65f020f10b..4fdca356af61e4ceca31423e281b234e398337bc 100644 --- a/bench/jit_trace.lua +++ b/bench/jit_trace.lua @@ -1,5 +1,5 @@ #!/usr/bin/env tarantool --- Trace-stability gate (PLAN.md M6). +-- Trace-stability gate for protobuf encode/decode hot paths. -- -- For each hot encode/decode path, run a few thousand iterations with -- a `trace` listener attached and assert that no trace aborts in @@ -30,6 +30,15 @@ package.path = './runtime/?.lua;./runtime/?/init.lua;' .. package.path jit.on() +-- macOS arm64 mcode arena hardening: the default sizemcode/maxmcode are too +-- small for our combined hot-path codegen footprint, and the allocator +-- intermittently fails to find an executable page within the signed-32-bit +-- offset window. When that happens the gate reports every check as +-- 'stops=0' with no diagnostic — indistinguishable from a real JIT +-- topology regression. Raising the arena past our peak need eliminates +-- the failure mode and keeps the gate's pass/fail signal load-bearing. +jit.opt.start('sizemcode=64', 'maxmcode=4096') + local vmdef = require('jit.vmdef') -- Codes from jit.vmdef.traceerr (1-indexed). We treat these as fatal — @@ -94,6 +103,16 @@ local function record(fn, warmup_iters, measured_iters) end end end + -- Defensive: keep the listener out of the JIT. If the callback ever + -- becomes hot enough to be traced, recording the cb while recording + -- the function-under-test races and stop events are dropped (starts + -- still fire, but most traces never finish recording). The current + -- cb body branches enough to dodge this organically, but any future + -- extension (per-event timing, pc context capture, etc.) would + -- re-trigger it silently. Reproducible in a few lines: a fat cb + -- that appends event tuples to a table drops Person_encode's stop + -- count from ~9 to 0 on this codebase. + jit.off(cb) jit.attach(cb, 'trace') for _ = 1, measured_iters do fn() end jit.attach(cb)