Skip to content

Commit d0a7ef4

Browse files
DanTupCommit Queue
authored andcommitted
[dds/dap] Add/remove breakpoints as required instead of replacing the whole set
The DAP APIs for breakpoints send the whole set of breakpoints for a given file at once. Previously, we would just delete all breakpoints and then re-add them all, however since we added support for resolving breakpoints, this can result in all breakpoints in a file flickering to unresolved then back to resolved (as well as generally being slower). This change splits the method that would replace all breakpoints into methods for add+remove, and then skips over any breakpoints (in setBreakpoints) that already match breakpoints we have. Fixes Dart-Code/Dart-Code#4678 Change-Id: I489c6c295bac3ebd3a0851895a7a81e330d571e9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/433700 Reviewed-by: Ben Konyi <[email protected]> Reviewed-by: Derek Xu <[email protected]> Commit-Queue: Derek Xu <[email protected]>
1 parent 8e65022 commit d0a7ef4

File tree

6 files changed

+334
-137
lines changed

6 files changed

+334
-137
lines changed

pkg/dds/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# 5.0.4
2+
- [DAP] Breakpoints are now added/removed individually instead of all being cleared and re-added during a `setBreakpoints` request. This improves performance and can avoid breakpoints flickering between unresolved/resolved when adding new breakpoints in the same file.
3+
14
# 5.0.3
25
- [DAP] Handle some additional errors if the VM Service is shutting down during an attempt to resume an isolate.
36
- [DAP] Stack frames with dots in paths will now be parsed and have locations attached to `OutputEvents`s.

pkg/dds/lib/src/dap/adapters/dart.dart

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1577,26 +1577,70 @@ abstract class DartDebugAdapter<TL extends LaunchRequestArguments,
15771577
: name!;
15781578

15791579
// Use a completer to track when the response is sent, so any events related
1580-
// to these breakpoints are not sent before the client has the IDs.
1580+
// to new breakpoints will not be sent to the client before the response
1581+
// here which provides the IDs to the client.
15811582
final completer = Completer<void>();
15821583

1583-
final clientBreakpoints = breakpoints
1584-
.map((bp) => ClientBreakpoint(bp, completer.future))
1585-
.toList();
1586-
await isolateManager.setBreakpoints(uri, clientBreakpoints);
1587-
1588-
sendResponse(SetBreakpointsResponseBody(
1584+
// Map the provided breakpoints onto either new or existing instances of
1585+
// [ClientBreakpoint] that we use to track the clients breakpoints
1586+
// internally.
1587+
final clientBreakpoints = breakpoints.map((bp) {
1588+
return
1589+
// First try to match an existing breakpoint so we can avoid deleting
1590+
// and re-creating all breakpoints if a new one is added to a file.
1591+
isolateManager.findExistingClientBreakpoint(uri, bp) ??
1592+
ClientBreakpoint(bp, completer.future);
1593+
}).toList();
1594+
1595+
// Any breakpoints that are not in our new set will need to be removed from
1596+
// the VM.
1597+
//
1598+
// Because multiple client breakpoints may resolve to the same VM breakpoint
1599+
// we must exclude any that still remain in one of the kept breakpoints.
1600+
final referencedVmBreakpoints =
1601+
clientBreakpoints.map((bp) => bp.forThread.values).toSet();
1602+
final breakpointsToRemove = isolateManager.clientBreakpointsByUri[uri]
1603+
?.toSet()
1604+
// Remove any we're reusing.
1605+
.difference(clientBreakpoints.toSet())
1606+
// Remove any that map to VM breakpoints that are still referenced
1607+
// because we'll want to keep them.
1608+
.where((clientBreakpoint) => clientBreakpoint.forThread.values
1609+
.none(referencedVmBreakpoints.contains));
1610+
1611+
// Store this new set of breakpoints as the current set for this URI.
1612+
isolateManager.recordLatestClientBreakpoints(uri, clientBreakpoints);
1613+
1614+
// Prepare the response with the existing values before we start updating.
1615+
final breakpointResponse = SetBreakpointsResponseBody(
15891616
breakpoints: clientBreakpoints
1590-
// Send breakpoints back as unverified and with our generated IDs so we
1591-
// can update them with a 'breakpoint' event when we get the
1592-
// 'BreakpointAdded'/'BreakpointResolved' events from the VM.
15931617
.map((bp) => Breakpoint(
15941618
id: bp.id,
1595-
verified: false,
1596-
message: 'Breakpoint has not yet been resolved',
1597-
reason: 'pending'))
1619+
verified: bp.verified,
1620+
line: bp.verified ? bp.resolvedLine : null,
1621+
column: bp.verified ? bp.resolvedColumn : null,
1622+
message: bp.verified ? null : bp.verifiedMessage,
1623+
reason: bp.verified ? null : bp.verifiedReason))
15981624
.toList(),
1599-
));
1625+
);
1626+
1627+
// Update the breakpoints for all existing threads.
1628+
await Future.wait(isolateManager.threads.map((thread) async {
1629+
// Remove the deleted breakpoints.
1630+
if (breakpointsToRemove != null) {
1631+
await Future.wait(breakpointsToRemove.map((clientBreakpoint) =>
1632+
isolateManager.removeBreakpoint(clientBreakpoint, thread)));
1633+
}
1634+
1635+
// Add the new breakpoints.
1636+
await Future.wait(clientBreakpoints.map((clientBreakpoint) async {
1637+
if (!clientBreakpoint.isKnownToVm) {
1638+
await isolateManager.addBreakpoint(clientBreakpoint, thread, uri);
1639+
}
1640+
}));
1641+
}));
1642+
1643+
sendResponse(breakpointResponse);
16001644
completer.complete();
16011645
}
16021646

0 commit comments

Comments
 (0)