Skip to content

Conversation

@iinozemtsev
Copy link
Member

Internally, there are two different code paths to trigger hot restart:

  1. Via connected debugger, triggering _hotRestart funciton in dwds_vm_client.dart. This codepath removes existing breakpoints and blocks main script execution (by passing pauseIsolatesOnStart into dartHotRestartDwds) until the connected debugger re-adds breakpoints.
  2. Via in-app shortcut in injected UI, which just calls dartHotRestartDwds directly. This codepath does not clear breakpoints and does not block main startup.

The second flow with in-app triggered restart is currently doesn't work as intended:

  1. Chrome breakpoints aren't cleared, but the attached debugger still gets an isolate start event
  2. It tries to set all breakpoints in a new isolate, and sends add breakpoint commands
  3. Chrome API returns an error that the breakpoint exists, and the error is propagated back to DAP server, which sends a set breakpoint error notification to an IDE.
  4. The IDE thinks that the breakpoint is "unconfirmed", and hides it from the UI completely.

As a result, the user does not see the breakpoint, but it's there, in Chrome debugger state, and the execution stops on it, and there's no way for user to remove it (except for clearing all breakpoints by the restart/reload from an IDE, which clears chrome debugger state).

This CL attempts to converge the code paths by changing how in-app restarts work:

  1. The app (injected client code from client.dart) sends HotRestartRequest to DWDS, so that it arrives to dev_handler.dart.
  2. If there's no debugger attached, the dev handler sends the request back to the app (via app_connection.dart).
  3. If there is attached debugger, the dev handler triggers the same code path, as the request from the debugger.
  • Thanks for your contribution! Please replace this text with a description of what this PR is changing or adding and why, list any relevant issues, and review the contribution guidelines below.

  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
Contribution guidelines:

Many Dart repos have a weekly cadence for reviewing PRs - please allow for some latency before initial review feedback.

Note: The Dart team is trialing Gemini Code Assist. Don't take its comments as final Dart team feedback. Use the suggestions if they're helpful; otherwise, wait for a human reviewer.

Copy link
Contributor

@srujzs srujzs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally LGTM, just not sure about the case where proxyService is null.

I'm guessing we'll have an integration test in google3?

@iinozemtsev
Copy link
Member Author

We'll have an integration test, but no ETA yet.

I think the proxyService will be null (no debugger attached) in 99+% cases (assuming it's null when no debugger attached, and interactive debugging isn't needed that often), so I think it's important to make sure that it is handled correctly.

Technically there is going to be a flow change for "no debugger attached" case, because previously the app would call hotRestartJs (lines 88-93 in client.dart) with pauseIsolatesOnStart set to false, and now the app would trigger handleWebSocketHotRestartRequest, but looks like both of them just call manager.hotRestart.

@srujzs
Copy link
Contributor

srujzs commented Dec 9, 2025

and now the app would trigger handleWebSocketHotRestartRequest, but looks like both of them just call manager.hotRestart.

Yeah this was the part I was a little unsure about. There are some responses sent back after the hot restart that seem like they should be okay but it may also confuse the WebSocketProxyService as it may not be tracking the pending hot restart (see completeHotRestart which gets called on the HotRestartResponse). Jessy may know more about the workings there.

@iinozemtsev
Copy link
Member Author

There are some responses sent back after the hot restart

Ah, I see, looks like I'm abusing some code paths, which are there for web socket connections, but happen to work for sse connections too. I think at least in google3 these responses should come again into dev_handler, which will ignore them on if (proxyService == null) { check: I see the logged warning about no proxy service to handle hot restart response, followed by similar warnings about other post restart messages like BatchedDebugEvents

@iinozemtsev iinozemtsev merged commit c820e2a into dart-lang:main Dec 10, 2025
85 of 86 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants