2026-05-25 15:07:10 +02:00
|
|
|
// === Asyncify / fiber / modal diagnostics (LOGGING ONLY — no behavior change) ===
|
|
|
|
|
//
|
|
|
|
|
// Injected only when inject-dyncall-shims.sh runs with SHIM_DIAGNOSTICS=1.
|
|
|
|
|
// Observability for the Chrome/V8 renderer crash on the first coroutine resume
|
|
|
|
|
// (the main-context Asyncify rewind). Does NOT swallow or alter any call — it
|
|
|
|
|
// logs and traces, then delegates, so the real crash still happens and can be
|
|
|
|
|
// observed right up to the faulting point.
|
|
|
|
|
(function() {
|
|
|
|
|
var modalActive = false;
|
|
|
|
|
var tableLen = function() { return (typeof wasmTable !== "undefined" && wasmTable) ? wasmTable.length : -1; };
|
|
|
|
|
var asyncState = function() { return (typeof Asyncify !== "undefined") ? Asyncify.state : "N/A"; };
|
|
|
|
|
|
|
|
|
|
// 1. Timer scheduling — flag function pointers already out of bounds at schedule time.
|
|
|
|
|
if (typeof _emscripten_async_call !== "undefined") {
|
|
|
|
|
var __origAsyncCall = _emscripten_async_call;
|
|
|
|
|
_emscripten_async_call = function(func, arg, millis) {
|
|
|
|
|
var inBounds = func >= 0 && func < tableLen();
|
|
|
|
|
console.warn("[DIAG_ASYNC_CALL] func=" + func + " arg=" + arg + " millis=" + millis +
|
|
|
|
|
" inBounds=" + inBounds + " modalActive=" + modalActive + " state=" + asyncState());
|
|
|
|
|
if (!inBounds) { console.error("[DIAG_ASYNC_CALL] OUT OF BOUNDS at schedule time! func=" + func); console.trace(); }
|
|
|
|
|
return __origAsyncCall(func, arg, millis);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. Rewind target selection — which export Asyncify will re-enter on rewind.
|
|
|
|
|
if (typeof Asyncify !== "undefined" && Asyncify.setDataRewindFunc) {
|
|
|
|
|
var __origSetRewind = Asyncify.setDataRewindFunc.bind(Asyncify);
|
|
|
|
|
Asyncify.setDataRewindFunc = function(ptr, forced) {
|
|
|
|
|
console.warn("[DIAG_REWIND_FUNC] ptr=" + ptr + " forced=" + forced + " state=" + Asyncify.state +
|
|
|
|
|
" modalActive=" + modalActive + " callStack=" + JSON.stringify(Asyncify.exportCallStack));
|
|
|
|
|
return __origSetRewind(ptr, forced);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 3. doRewind — the actual rewind that crashes V8. Log the buffer + saved rewind id
|
|
|
|
|
// immediately before re-entering wasm, so the last line before the crash names it.
|
|
|
|
|
if (typeof Asyncify !== "undefined" && typeof Asyncify.doRewind === "function") {
|
|
|
|
|
var __origDoRewind = Asyncify.doRewind.bind(Asyncify);
|
|
|
|
|
Asyncify.doRewind = function(ptr) {
|
|
|
|
|
var rewindId = -1;
|
|
|
|
|
try { rewindId = (typeof GROWABLE_HEAP_I32 === "function") ? GROWABLE_HEAP_I32()[((ptr + 8) >> 2)] : HEAP32[((ptr + 8) >> 2)]; } catch (e) {}
|
|
|
|
|
console.warn("[DIAG_DOREWIND] ptr=" + ptr + " rewindId=" + rewindId + " state=" + asyncState() +
|
|
|
|
|
" modalActive=" + modalActive + " — re-entering wasm now");
|
|
|
|
|
return __origDoRewind(ptr);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. dynCall_vi — log (and trace) out-of-bounds pointers but DO NOT swallow; call through.
|
|
|
|
|
if (typeof dynCall_vi === "function") {
|
|
|
|
|
var __origDynCallVi = dynCall_vi;
|
|
|
|
|
dynCall_vi = function(index, a0) {
|
|
|
|
|
if (index < 0 || index >= tableLen()) {
|
|
|
|
|
console.error("[DIAG_DYNCALL_VI] OUT OF BOUNDS index=" + index + " tableLen=" + tableLen() +
|
|
|
|
|
" modalActive=" + modalActive + " state=" + asyncState());
|
|
|
|
|
console.trace();
|
|
|
|
|
}
|
|
|
|
|
return __origDynCallVi(index, a0);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. Modal lifecycle (startModal sets Module._endModal; detect appear/disappear).
|
|
|
|
|
if (typeof Module !== "undefined") {
|
|
|
|
|
var seen = false;
|
|
|
|
|
setInterval(function() {
|
|
|
|
|
if (Module._endModal && !seen) {
|
|
|
|
|
seen = true; modalActive = true;
|
|
|
|
|
console.warn("[DIAG_MODAL] modal started, state=" + asyncState());
|
|
|
|
|
var __origEnd = Module._endModal;
|
|
|
|
|
Module._endModal = function(code) {
|
|
|
|
|
console.warn("[DIAG_MODAL] EndModal code=" + code + " state=" + asyncState());
|
|
|
|
|
modalActive = false;
|
|
|
|
|
return __origEnd(code);
|
|
|
|
|
};
|
|
|
|
|
} else if (!Module._endModal && seen) {
|
|
|
|
|
seen = false;
|
|
|
|
|
console.warn("[DIAG_MODAL] modal cleanup, state=" + asyncState());
|
|
|
|
|
}
|
|
|
|
|
}, 100);
|
|
|
|
|
}
|
|
|
|
|
|
test(wasm): coroutine crash reproduction harness + per-engine runner
Investigation scaffolding for the Chrome-only KiCad coroutine renderer crash.
Adds isolated reproduction probes exercising the coroutine/Asyncify/fiber layer
under KiCad-like conditions, runnable in BOTH Firefox and system Chrome.
- tests/playwright-coroutine.config.ts + test:coroutine:firefox|chrome npm
scripts: run the coroutine specs in Firefox AND system Chrome (the old e2e
config only used bundled Chromium, which never reproduced the crash).
- tests/apps/standalone/coroutine-pthread/: no-wx + pthreads reproduction probes
(fiber-in-main, nested invoke_/dynCall boundaries, RunMainStack, embind,
main-loop/rAF activation) + worker_dom_stub.js for wx+pthreads builds.
- tests/apps/Makefile.wasm: coroutine-pthread{,-main,-nested,-nested-ex,-wx,
-embind,-mainloop} targets.
- scripts/common/shims/diagnostics.js: add EM_ASYNC_JS handleSleep enter/wake
tracking (DIAG_SLEEP) to detect nested-async at the crash.
Findings (details in research notes): every isolated factor so far — direct /
nested / RunMainStack fiber, wx event loop + all 13 scenarios incl EM_ASYNC_JS,
pthreads, and main-loop/rAF activation — runs CLEAN in system Chrome. The
coroutine/Asyncify layer is exonerated; GL/WebGL is the remaining untested factor
(next). The reliable FF-pass/Chrome-fail repro is still the KiCad pcbnew e2e.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 18:44:12 +02:00
|
|
|
// 6. EM_ASYNC_JS sleeps (startModal, js_enumerateFonts, clipboard, etc.) — log
|
|
|
|
|
// enter/wake so we can see whether an async sleep is NESTED with a fiber swap
|
|
|
|
|
// at the crash (the #9153 collision). Logging only; delegates unchanged.
|
|
|
|
|
if (typeof Asyncify !== "undefined" && typeof Asyncify.handleSleep === "function") {
|
|
|
|
|
var __diagOrigHandleSleep = Asyncify.handleSleep.bind(Asyncify);
|
|
|
|
|
var diagSleepId = 0;
|
|
|
|
|
Asyncify.handleSleep = function(startAsync) {
|
|
|
|
|
var id = ++diagSleepId;
|
|
|
|
|
console.warn("[DIAG_SLEEP] ENTER id=" + id + " state=" + asyncState() +
|
|
|
|
|
" currData=" + ((typeof Asyncify.currData !== "undefined" && Asyncify.currData) || "null"));
|
|
|
|
|
return __diagOrigHandleSleep(function(wakeUp) {
|
|
|
|
|
return startAsync(function(result) {
|
|
|
|
|
console.warn("[DIAG_SLEEP] WAKE id=" + id + " state=" + asyncState() +
|
|
|
|
|
" currData=" + ((typeof Asyncify.currData !== "undefined" && Asyncify.currData) || "null"));
|
|
|
|
|
return wakeUp(result);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-25 15:07:10 +02:00
|
|
|
console.warn("[DIAG] Asyncify/fiber/modal diagnostics installed (logging only)");
|
|
|
|
|
})();
|
|
|
|
|
// === End diagnostics ===
|