Repository navigation
inspector: fix crash when pausing JS sessions - #65914
christianaurichzm wants to merge 1 commit into
Conversation
An in-process inspector.Session delivers protocol messages by calling into JavaScript, and V8 produces some of them while it asserts that JavaScript is not executed: Debugger.paused is broadcast from the Isolate::RequestInterrupt handler that Debugger.pause schedules, before V8 opens its own AllowJavascriptExecutionScope for the pause message loop. So the process aborts with "Invoke in DisallowJavascriptExecutionScope" as soon as a debugger client pauses it while JavaScript is running. Open an AllowJavascriptExecutionScope on the synchronous delivery path, the same opt-out d8's own inspector channel uses for every protocol message it delivers, see InspectorFrontend::Send() in deps/v8/src/d8/d8.cc. The messages produced where calling into JavaScript is really unsafe, such as GC callbacks, take the queueing branch instead and are delivered from a point where it is allowed again. Signed-off-by: Christian Aurich <christian.aurichzm@gmail.com>
|
Review requested:
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #65914 +/- ##
==========================================
+ Coverage 90.16% 90.17% +0.01%
==========================================
Files 771 771
Lines 265094 265170 +76
Branches 50355 50368 +13
==========================================
+ Hits 239027 239124 +97
+ Misses 17011 17001 -10
+ Partials 9056 9045 -11
🚀 New features to boost your workflow:
|
legendecas
left a comment
There was a problem hiding this comment.
This does not fix the problem. The linked #65397 has stated that:
In
v8::Isolate::RequestInterruptthe API contract says:API interrupt callbacks are forbidden from executing JavaScript on the interrupted Isolate.
This patch does not conform to the V8 API contract, rather, it contradicts the intentional DisallowJavascriptExecutionScope. An expected fix should fix the timing of calling into JS.
|
Thanks. I took another look at #65028 after your comment. You pointed out there that Node's interrupt flag only covers its own I tried the timing-based approach. Deferring I also tried a small V8-side change that extends the existing inspector scope across the pause/resume notifications. It passes the full local suite with no Would you prefer that I bring this upstream to V8 first? |
|
Since #65907 reverted the V8 check, this no longer reproduces on The gap is still in V8 upstream: @deepak1556 this will come back when the check is re-enabled. |
|
Thanks @christianaurichzm for looking into this, I am following up in https://chromium-review.googlesource.com/c/v8/v8/+/8173727/comments/48212f36_562dce9e to understand first the nature of the original scope addition. |
Debugger.pause and OOM breaks are scheduled with RequestInterrupt(), so handleProgramBreak() can run inside InvokeApiInterruptCallbacks() with JavaScript execution disallowed. b36cf9f allowed JavaScript around runMessageLoopOnPause(), but Debugger.paused is sent right before that scope and Debugger.resumed right after it. An embedder channel that delivers notifications to JavaScript on the same isolate, like the in-process inspector.Session in Node.js, crashes with "Invoke in DisallowJavascriptExecutionScope" when such a break happens. Extend the existing scope over both notifications. d8's InspectorFrontend opens its own AllowJavascriptExecutionScope, so no existing test covered this; add a unittest with a channel that calls into JavaScript. R=olivf@chromium.org, szuend@chromium.org Refs: nodejs/node#65914 Bug: 40067940, 540137721 Change-Id: I0b60fb40cf9e753f7444d69ee8c40b4dd5d843ec Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8459373 Reviewed-by: Simon Zünd <szuend@chromium.org> Commit-Queue: Olivier Flückiger <olivf@chromium.org> Reviewed-by: Olivier Flückiger <olivf@chromium.org> Cr-Commit-Position: refs/heads/main@{#110692}
|
@deepak1556 the V8 fix landed as v8/v8@35bd1582fe0a. I'd like to open the re-land of #65397 with a backport of it on top, keeping your two commits as they are (the defer one needed a conflict resolution with #65877). Does that work for you, or would you rather re-land it yourself? |
|
Sounds good, please go ahead. Also ensure #65397 (comment) is backported. Its better to land them all in one PR. |
parallel/test-inspector-multisession-wshas been failing across macOS, Linux, Windows ARM64, and s390x (nodejs/reliability#1654) with:This started after #65397 enabled
v8_disallow_js_in_api_interrupts_is_checked.An in-process
inspector.Sessiondelivers protocol messages by calling into JavaScript.JSBindingsConnection::SendMessageToFrontend()already defers messages produced from GC callbacks and Node-managed V8 interrupts.Debugger.pauseschedules a separateIsolate::RequestInterrupt()inside V8. That interrupt is not reflected byEnvironment::is_processing_v8_interrupt(). V8 then emitsDebugger.pausedbefore entering theAllowJavascriptExecutionScopeused for the pause message loop, so the JS session callback is invoked whileDisallowJavascriptExecutionis active.Add an
AllowJavascriptExecutionScopeonly to the synchronous delivery path, after the existing guards. d8 does the same inInspectorFrontend::Send(). The queued delivery path from #65397 is unchanged.The regression test fails 3/3 on
mainand passes with this change.Tested locally with all
parallel/test-inspector-*, the related sequential inspector/debugger/cpu-prof/heapdump tests, and--repeat=10for the affected multisession tests and the new test.test-inspector-multisession-wsandparallel.statusare unchanged.This is a process crash, not a test that should be marked flaky.
Refs: #65397
Refs: nodejs/reliability#1654