[dds/dap] Handle some additional error causes when resuming isolates

Fixes https://github.com/flutter/flutter/issues/125064.

Change-Id: I521fe29d0f269694dc6c2b993d1e400c495b0605
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/296560
Reviewed-by: Ben Konyi <bkonyi@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
This commit is contained in:
Danny Tuppeny 2023-04-20 14:55:23 +00:00 committed by Commit Queue
parent 493f36690f
commit facfcf0f92
6 changed files with 76 additions and 8 deletions

View file

@ -1,3 +1,6 @@
# 2.7.9
- [DAP] Configuring and resuming isolates will no longer cause a crash if the isolate exits before the request is processed.
# 2.7.8
- [DAP] Sentinel values (such as uninitialized fields/locals) will no longer cause `scopesRequest`/`variablesRequest` to fail, instead showing appropriate text (like "<not initialized>") against the variable.

View file

@ -279,6 +279,17 @@ class IsolateManager {
thread.hasPendingResume = true;
try {
await _adapter.vmService?.resume(thread.isolate.id!, step: resumeType);
} on vm.SentinelException {
// It's possible during these async requests that the isolate went away
// (for example a shutdown/restart) and we no longer care about
// resuming it.
} on vm.RPCError catch (e) {
if (e.code == RpcErrorCodes.kIsolateMustBePaused) {
// It's possible something else resumed the thread (such as if another
// debugger is attached), we can just continue.
} else {
rethrow;
}
} finally {
thread.hasPendingResume = false;
}
@ -612,13 +623,19 @@ class IsolateManager {
) async {
final isolateId = isolate.id!;
final uriStrings = uris.map((uri) => uri.toString()).toList();
final res = await _adapter.vmService
?.lookupResolvedPackageUris(isolateId, uriStrings, local: true);
try {
final res = await _adapter.vmService
?.lookupResolvedPackageUris(isolateId, uriStrings, local: true);
return res?.uris
?.cast<String?>()
.map((uri) => uri != null ? Uri.parse(uri) : null)
.toList();
return res?.uris
?.cast<String?>()
.map((uri) => uri != null ? Uri.parse(uri) : null)
.toList();
} on vm.SentinelException {
// If the isolate disappeared before we sent this request, just return
// null responses.
return uris.map((e) => null).toList();
}
}
/// Interpolates and prints messages for any log points.

View file

@ -31,7 +31,7 @@ abstract class RpcErrorCodes {
static const kStreamNotSubscribed = 104;
// static const kIsolateMustBeRunnable = 105;
// static const kIsolateMustBePaused = 106;
static const kIsolateMustBePaused = 106;
// static const kCannotResume = 107;
// static const kIsolateIsReloading = 108;
// static const kIsolateReloadBarred = 109;

View file

@ -1,5 +1,5 @@
name: dds
version: 2.7.8
version: 2.7.9
description: >-
A library used to spawn the Dart Developer Service, used to communicate with
a Dart VM Service instance.

View file

@ -533,6 +533,41 @@ void main(List<String> args) async {
client.stepIn(stop.threadId!),
], eagerError: true);
});
test('does not fail if two debug clients resume the same thread', () async {
final testFile = dap.createTestFile(infiniteRunningProgram);
final breakpointLine = lineWith(testFile, breakpointMarker);
// Start a program and hit a breakpoint.
final client1 = dap.client;
final stop1 = await client1.hitBreakpoint(testFile, breakpointLine);
final vmServiceUri = (await client1.vmServiceUri)!;
final thread1 = stop1.threadId!;
// Attach a second debug adapter to it.
final dap2 = await DapTestSession.setUp();
final client2 = dap2.client;
await Future.wait([
// We'll still get event for existing pause.
client2.expectStop('breakpoint'),
client2.start(
launch: () => client2.attach(
vmServiceUri: vmServiceUri.toString(),
autoResume: false,
cwd: dap.testAppDir.path,
),
),
]);
final thread2 = (await client2.getValidThreads()).threads.single.id;
// Send resumes to both and ensure they complete without errors.
await Future.wait([
client1.continue_(thread1),
client2.continue_(thread2),
], eagerError: true);
await dap2.tearDown();
});
}, timeout: Timeout.none);
group('debug mode conditional breakpoints', () {

View file

@ -91,6 +91,19 @@ String stringPrintingProgram(String text) {
''';
}
/// A simple Dart script that just loops forever sleeping for 1 second each
/// iteration.
///
/// A breakpoint marker is included before the loop.
const infiniteRunningProgram = '''
void main(List<String> args) async {
print('Looping'); $breakpointMarker
while (true) {
await Future.delayed(const Duration(seconds: 1));
}
}
''';
/// A simple async Dart script that when stopped at the line of '// BREAKPOINT'
/// will contain multiple stack frames across some async boundaries.
const simpleAsyncProgram = '''