From 2fa181645c32d6fafeaef2c9a4a508286d9c4e1a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 12 Sep 2026 04:12:16 +0000 Subject: [PATCH] fix(devtools): avoid pause-on-caught-throw for raw VM DAP attach MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DevTool::attach(HSQUIRRELVM) enabled sq_notifyallexceptions, so with break-on-error the error hook paused at the throw site before catch / reportError ran. That left a second pause hanging after one DAP continue and flaked as SEGFAULT on Linux CI teardown (devtools.dap.caughtErrorPausesAtReportSite). Keep notify-all only on Runtime-bound attach (hook already capture-only there) and force-resume in the test if the script thread is still paused. Co-authored-by: 西风逍遥游 --- src/engine/devtools/DevTool.cpp | 10 +++++++++- test/dap.cpp | 7 +++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/engine/devtools/DevTool.cpp b/src/engine/devtools/DevTool.cpp index f41add73..8b9f0448 100644 --- a/src/engine/devtools/DevTool.cpp +++ b/src/engine/devtools/DevTool.cpp @@ -135,6 +135,10 @@ void DevTool::attach(ssq::VM& vm, bool sampleLocals) { attach(vm.getHandle(), sa void DevTool::attach(eve::Runtime& runtime, bool sampleLocals) { attach(runtime.vm(), sampleLocals); runtime_ = &runtime; + // Capture throw-site stacks for try/catch so reportError / lastScriptError + // can surface the original site (load.nut pattern). Safe here because the + // Runtime-bound error hook only stashes context and does not notify/pause. + sq_notifyallexceptions(runtime.handle(), SQTrue); // Route Runtime-boundary errors into the slicer/report. The VM error hook // only captures the stack when this Runtime sink is bound; this handler // reports compile / reflect / unload failures and uncaught execute errors @@ -182,7 +186,11 @@ void DevTool::attach(HSQUIRRELVM vm, bool sampleLocals) { Debugger::instance().setPump([this]() { pumpWhilePaused(); }); sq_enabledebuginfo(vm_, SQTrue); - sq_notifyallexceptions(vm_, SQTrue); + // Do not enable sq_notifyallexceptions here. With notify-all + break-on-error, + // this hook would pause on every caught throw before script catch/reportError + // runs (double pause → hung script thread → flaky teardown SEGFAULT in + // devtools.dap.caughtErrorPausesAtReportSite). Runtime::installErrorHandler + // already turns notify-all on for production VMs; attach(Runtime&) keeps it. sq_setnativedebughook(vm_, nativeDebugHook); // Route uncaught script errors into the debugger (break-on-error aware). sq_newclosure(vm_, runtimeErrorHook, 0); diff --git a/test/dap.cpp b/test/dap.cpp index a86db9c3..55ea8ee5 100644 --- a/test/dap.cpp +++ b/test/dap.cpp @@ -869,6 +869,13 @@ TEST_CASE("devtools.dap.caughtErrorPausesAtReportSite") { std::this_thread::sleep_for(std::chrono::milliseconds(10)); dap.poll(); } + // If the script thread is still blocked in waitWhilePaused, force-resume + // before teardown so dap.stop()/detach cannot race a paused VM (SEGFAULT). + if (!scriptDone.load() && dbg.isPaused()) dbg.resume(); + for (int i = 0; i < 100 && !scriptDone.load(); ++i) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + dap.poll(); + } if (scriptThread.joinable()) { if (scriptDone.load()) scriptThread.join();