[dds/dap] Handle some additional kinds of errors when the VM Service connection shuts down
This handles an RPC Error -32000 with text "Service connection disposed" as reported in https://github.com/dart-lang/sdk/issues/60851. It extracts some of this logic into an extension on RPCError to simplify checking in multiple places.
Unfortunately I can't reproduce the specific conditions that produce this error in a test (it probably requires terminated the VM Service at just the right time during startup or an isolate starting?).
Fixes https://github.com/dart-lang/sdk/issues/60851
Change-Id: Iae945f60290766164d04b91d851fbb8b58f178c6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/432944
Reviewed-by: Derek Xu <derekx@google.com>
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Ben Konyi <bkonyi@google.com>
diff --git a/pkg/dds/CHANGELOG.md b/pkg/dds/CHANGELOG.md
index 77f29ff..9344e96 100644
--- a/pkg/dds/CHANGELOG.md
+++ b/pkg/dds/CHANGELOG.md
@@ -1,4 +1,5 @@
# 5.0.3
+- [DAP] Handle some additional errors if the VM Service is shutting down during an attempt to resume an isolate.
- [DAP] Stack frames with dots in paths will now be parsed and have locations attached to `OutputEvents`s.
- [DAP] Responses to `evaluateRequest` that are lists now include `indexedVariables` to allow for client-side paging.
diff --git a/pkg/dds/lib/src/dap/adapters/dart.dart b/pkg/dds/lib/src/dap/adapters/dart.dart
index 5f31a8d..5909aa1 100644
--- a/pkg/dds/lib/src/dap/adapters/dart.dart
+++ b/pkg/dds/lib/src/dap/adapters/dart.dart
@@ -15,7 +15,6 @@
import 'package:vm_service/vm_service.dart' as vm;
import '../../../dds.dart';
-import '../../rpc_error_codes.dart';
import '../base_debug_adapter.dart';
import '../isolate_manager.dart';
import '../logging.dart';
@@ -2838,8 +2837,7 @@
// outside of the DAP (eg. closing the simulator) so it's possible our
// requests will fail in this way before we've handled any event to set
// `isTerminating`.
- if (e.code == RpcErrorCodes.kServiceDisappeared ||
- e.code == RpcErrorCodes.kConnectionDisposed) {
+ if (e.isServiceDisposedError) {
return null;
}
@@ -2853,15 +2851,6 @@
if (isTerminating) {
return null;
}
-
- // Always ignore "client is closed" and "closed with pending request"
- // errors because these can always occur during shutdown if we were
- // just starting to send (or had just sent) a request.
- if (e.message.contains("The client is closed") ||
- e.message.contains("The client closed with pending request") ||
- e.message.contains("Service connection disposed")) {
- return null;
- }
}
// Otherwise, it's an unexpected/unknown failure and should be rethrown.
diff --git a/pkg/dds/lib/src/dap/isolate_manager.dart b/pkg/dds/lib/src/dap/isolate_manager.dart
index ca7801c..bfc2f49 100644
--- a/pkg/dds/lib/src/dap/isolate_manager.dart
+++ b/pkg/dds/lib/src/dap/isolate_manager.dart
@@ -378,6 +378,9 @@
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 if (e.isServiceDisposedError) {
+ // The VM service connection was terminated, we can silently ignore this
+ // because we're likely shutting down.
} else if (e.code == RpcErrorCodes.kInternalError &&
e.message.contains('No running isolate (inspector is not set).')) {
// TODO(bkonyi): remove once https://github.com/flutter/flutter/issues/156793
@@ -432,6 +435,9 @@
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 if (e.isServiceDisposedError) {
+ // The VM service connection was terminated, we can silently ignore this
+ // because we're likely shutting down.
} else if (e.code == RpcErrorCodes.kMethodNotFound) {
// Fallback to a regular resume if the DDS service extension isn't
// available:
diff --git a/pkg/dds/lib/src/dap/utils.dart b/pkg/dds/lib/src/dap/utils.dart
index 553cbb1..4ff90ea 100644
--- a/pkg/dds/lib/src/dap/utils.dart
+++ b/pkg/dds/lib/src/dap/utils.dart
@@ -5,6 +5,9 @@
import 'dart:io';
import 'package:path/path.dart' as path;
+import 'package:vm_service/vm_service.dart' as vm;
+
+import '../rpc_error_codes.dart';
/// Returns whether this URI is something that can be resolved to a file-like
/// URI via the VM Service.
@@ -133,3 +136,25 @@
}
typedef StackFrameLocation = ({Uri uri, int? line, int? column});
+
+extension RpcErrorExtension on vm.RPCError {
+ /// Whether this [vm.RPCError] is some kind of "VM Service connection has gone"
+ /// error that may occur if the VM is shut down.
+ bool get isServiceDisposedError {
+ if (code == RpcErrorCodes.kServiceDisappeared ||
+ code == RpcErrorCodes.kConnectionDisposed) {
+ return true;
+ }
+
+ if (code == RpcErrorCodes.kExtensionError) {
+ // Always ignore "client is closed" and "closed with pending request"
+ // errors because these can always occur during shutdown if we were
+ // just starting to send (or had just sent) a request.
+ return message.contains("The client is closed") ||
+ message.contains("The client closed with pending request") ||
+ message.contains("Service connection disposed");
+ }
+
+ return false;
+ }
+}
diff --git a/pkg/dds/lib/src/rpc_error_codes.dart b/pkg/dds/lib/src/rpc_error_codes.dart
index 56320d7..96a1064 100644
--- a/pkg/dds/lib/src/rpc_error_codes.dart
+++ b/pkg/dds/lib/src/rpc_error_codes.dart
@@ -22,7 +22,7 @@
static const kInvalidParams = -32602;
static const kInternalError = -32603;
- // static const kExtensionError = -32000;
+ static const kExtensionError = -32000;
static const kConnectionDisposed = -32010;