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).

The CL attempts to fix it by caching chrome breakpoint ids by js locations, so that when the debugger adds a breakpoint resolving to the same js location, it gets back the same js breakpoint id.

  • 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.

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 flow dart-lang#2 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 tha 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).

The CL attempts to fix it by caching chrome breakpoint ids by js locations, so that when the debugger adds a breakpoint resolving to the same js location, it gets back the same js breakpoint id.
@iinozemtsev
Copy link
Member Author

did you have a chance to look at this pr?

@bkonyi bkonyi requested review from Markzipan and removed request for elliette December 3, 2025 15:56
Copy link
Collaborator

@bkonyi bkonyi left a comment

Choose a reason for hiding this comment

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

I think this seems reasonable to me. @Markzipan, can you take a look too?

@Markzipan
Copy link
Contributor

@nshahan has more context about hot restart breakpoints - adding them here

@Markzipan Markzipan requested a review from nshahan December 3, 2025 18:51
@nshahan
Copy link
Contributor

nshahan commented Dec 3, 2025

@iinozemtsev
Do you have a reproduction for the problem and potentially use it in a test?

I believe this new implementation that blocks running main while the debugger is manipulating breakpoints was added by @srujzs. While adding support for breakpoints after a hot reload he found correctness issues and inconsistencies with the current approach. Do you think it would be possible to utilize the same logic when restarting from the injected UI? I think this would be preferable for multiple reasons:

  • Less Hot Restart flows for us to support and manually test every time we make a change.
  • Without blocking main there are still issues with this approach since the app can be running before all breakpoints have been set. We potentially have a race condition that allows executing through lines that should and will eventually have a breakpoint on them.

@iinozemtsev
Copy link
Member Author

potentially use it in a test?

if there are existing tests which wire dev extension, webdev server with dwds, and a dap client, I think adding a test would be relatively straightforward, otherwise I think it's too big task to chew for me at the moment.

I'll try to take a look tomorrow if there's an easy way to converge the in-app restart path into dwds_vm_client.

@iinozemtsev
Copy link
Member Author

I've sent cl/840666025 internally to demo alternative approach just for quicker prototyping, I can turn that into a real PR if LG.

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.

4 participants