Reland "[ VM / Service ] Add setIsolatePauseMode RPC"
This reverts commit 6ae9f310817fceb60c26301e9a3d4b641e3ab48f.
TEST=N/A
Change-Id: I98dcee3002f9f0882b1bbd5ad461d28ba2c874f4
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/220485
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
diff --git a/pkg/dds/lib/src/dap/isolate_manager.dart b/pkg/dds/lib/src/dap/isolate_manager.dart
index 47a8dde..6e00463 100644
--- a/pkg/dds/lib/src/dap/isolate_manager.dart
+++ b/pkg/dds/lib/src/dap/isolate_manager.dart
@@ -594,7 +594,10 @@
return;
}
- await service.setExceptionPauseMode(isolate.id!, _exceptionPauseMode);
+ await service.setIsolatePauseMode(
+ isolate.id!,
+ exceptionPauseMode: _exceptionPauseMode,
+ );
}
/// Calls setLibraryDebuggable for all libraries in the given isolate based
diff --git a/pkg/dds/pubspec.yaml b/pkg/dds/pubspec.yaml
index bafc2dc..e575eae 100644
--- a/pkg/dds/pubspec.yaml
+++ b/pkg/dds/pubspec.yaml
@@ -25,7 +25,7 @@
shelf_web_socket: ^1.0.0
sse: ^4.0.0
stream_channel: ^2.0.0
- vm_service: ^7.2.0
+ vm_service: ^7.5.0
web_socket_channel: ^2.0.0
dev_dependencies:
diff --git a/pkg/vm_service/CHANGELOG.md b/pkg/vm_service/CHANGELOG.md
index 50d973c..760c9d9 100644
--- a/pkg/vm_service/CHANGELOG.md
+++ b/pkg/vm_service/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## 7.5.0
+- Update to version `3.53` of the spec.
+- Added `setIsolatePauseMode` RPC.
+- Deprecated `setExceptionPauseMode` in favor of `setIsolatePauseMode`.
+
## 7.4.0
- Update to version `3.52` of the spec.
- Added `lookupResolvedPackageUris` and `lookupPackageUris` RPCs and `UriList`
diff --git a/pkg/vm_service/java/.gitignore b/pkg/vm_service/java/.gitignore
index 817e983..a54ba8c 100644
--- a/pkg/vm_service/java/.gitignore
+++ b/pkg/vm_service/java/.gitignore
@@ -36,6 +36,7 @@
src/org/dartlang/vm/service/consumer/ResumeConsumer.java
src/org/dartlang/vm/service/consumer/SetExceptionPauseModeConsumer.java
src/org/dartlang/vm/service/consumer/SetFlagConsumer.java
+src/org/dartlang/vm/service/consumer/SetIsolatePauseModeConsumer.java
src/org/dartlang/vm/service/consumer/SetLibraryDebuggableConsumer.java
src/org/dartlang/vm/service/consumer/SetNameConsumer.java
src/org/dartlang/vm/service/consumer/SetTraceClassAllocationConsumer.java
diff --git a/pkg/vm_service/java/version.properties b/pkg/vm_service/java/version.properties
index 8ae6998..b8e9261a 100644
--- a/pkg/vm_service/java/version.properties
+++ b/pkg/vm_service/java/version.properties
@@ -1 +1 @@
-version=3.52
+version=3.53
diff --git a/pkg/vm_service/lib/src/vm_service.dart b/pkg/vm_service/lib/src/vm_service.dart
index 51958bd..278e6cf 100644
--- a/pkg/vm_service/lib/src/vm_service.dart
+++ b/pkg/vm_service/lib/src/vm_service.dart
@@ -26,7 +26,7 @@
HeapSnapshotObjectNoData,
HeapSnapshotObjectNullData;
-const String vmServiceVersion = '3.52.0';
+const String vmServiceVersion = '3.53.0';
/// @optional
const String optional = 'optional';
@@ -236,6 +236,7 @@
'resume': const ['Success'],
'setBreakpointState': const ['Breakpoint'],
'setExceptionPauseMode': const ['Success'],
+ 'setIsolatePauseMode': const ['Success'],
'setFlag': const ['Success', 'Error'],
'setLibraryDebuggable': const ['Success'],
'setName': const ['Success'],
@@ -1078,9 +1079,34 @@
///
/// This method will throw a [SentinelException] in the case a [Sentinel] is
/// returned.
+ @Deprecated('Use setIsolatePauseMode instead')
Future<Success> setExceptionPauseMode(
String isolateId, /*ExceptionPauseMode*/ String mode);
+ /// The `setIsolatePauseMode` RPC is used to control if or when an isolate
+ /// will pause due to a change in execution state.
+ ///
+ /// The `shouldPauseOnExit` parameter specify whether the target isolate
+ /// should pause on exit.
+ ///
+ /// The `setExceptionPauseMode` RPC is used to control if an isolate pauses
+ /// when an exception is thrown.
+ ///
+ /// mode | meaning
+ /// ---- | -------
+ /// None | Do not pause isolate on thrown exceptions
+ /// Unhandled | Pause isolate on unhandled exceptions
+ /// All | Pause isolate on all thrown exceptions
+ ///
+ /// If `isolateId` refers to an isolate which has exited, then the `Collected`
+ /// [Sentinel] is returned.
+ ///
+ /// This method will throw a [SentinelException] in the case a [Sentinel] is
+ /// returned.
+ Future<Success> setIsolatePauseMode(String isolateId,
+ {/*ExceptionPauseMode*/ String? exceptionPauseMode,
+ bool? shouldPauseOnExit});
+
/// The `setFlag` RPC is used to set a VM flag at runtime. Returns an error if
/// the named flag does not exist, the flag may not be set at runtime, or the
/// value is of the wrong type for the flag.
@@ -1101,6 +1127,7 @@
/// provided value. If set to false when the profiler is already running, the
/// profiler will be stopped but may not free its sample buffer depending on
/// platform limitations.
+ /// - Isolate pause settings will only be applied to newly spawned isolates.
///
/// See [Success].
///
@@ -1549,11 +1576,19 @@
);
break;
case 'setExceptionPauseMode':
+ // ignore: deprecated_member_use_from_same_package
response = await _serviceImplementation.setExceptionPauseMode(
params!['isolateId'],
params['mode'],
);
break;
+ case 'setIsolatePauseMode':
+ response = await _serviceImplementation.setIsolatePauseMode(
+ params!['isolateId'],
+ exceptionPauseMode: params['exceptionPauseMode'],
+ shouldPauseOnExit: params['shouldPauseOnExit'],
+ );
+ break;
case 'setFlag':
response = await _serviceImplementation.setFlag(
params!['name'],
@@ -2081,12 +2116,24 @@
'enable': enable
});
+ @Deprecated('Use setIsolatePauseMode instead')
@override
Future<Success> setExceptionPauseMode(
String isolateId, /*ExceptionPauseMode*/ String mode) =>
_call('setExceptionPauseMode', {'isolateId': isolateId, 'mode': mode});
@override
+ Future<Success> setIsolatePauseMode(String isolateId,
+ {/*ExceptionPauseMode*/ String? exceptionPauseMode,
+ bool? shouldPauseOnExit}) =>
+ _call('setIsolatePauseMode', {
+ 'isolateId': isolateId,
+ if (exceptionPauseMode != null)
+ 'exceptionPauseMode': exceptionPauseMode,
+ if (shouldPauseOnExit != null) 'shouldPauseOnExit': shouldPauseOnExit,
+ });
+
+ @override
Future<Response> setFlag(String name, String value) =>
_call('setFlag', {'name': name, 'value': value});
diff --git a/pkg/vm_service/pubspec.yaml b/pkg/vm_service/pubspec.yaml
index 2984592..8bd5ed5 100644
--- a/pkg/vm_service/pubspec.yaml
+++ b/pkg/vm_service/pubspec.yaml
@@ -3,7 +3,7 @@
A library to communicate with a service implementing the Dart VM
service protocol.
-version: 7.4.0
+version: 7.5.0
homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service
diff --git a/pkg/vm_service/test/common/test_helper.dart b/pkg/vm_service/test/common/test_helper.dart
index 585722f..8bf30b8 100644
--- a/pkg/vm_service/test/common/test_helper.dart
+++ b/pkg/vm_service/test/common/test_helper.dart
@@ -277,7 +277,6 @@
vm = await vmServiceConnectUri(serviceWebsocketAddress);
print('Done loading VM');
isolate = await getFirstIsolate(vm);
- print('Got first isolate');
});
});
@@ -335,14 +334,12 @@
if (event.kind == EventKind.kIsolateRunnable) {
print(event.isolate!.name);
vm = await service.getVM();
- //assert(vmIsolates.isNotEmpty);
await subscription.cancel();
await service.streamCancel(EventStreams.kIsolate);
completer!.complete(event.isolate!);
completer = null;
}
});
-
await service.streamListen(EventStreams.kIsolate);
// The isolate may have started before we subscribed.
diff --git a/pkg/vm_service/test/pause_on_exceptions_legacy_test.dart b/pkg/vm_service/test/pause_on_exceptions_legacy_test.dart
new file mode 100644
index 0000000..21ff3a8
--- /dev/null
+++ b/pkg/vm_service/test/pause_on_exceptions_legacy_test.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+
+doThrow() {
+ throw "TheException"; // Line 13.
+}
+
+doCaught() {
+ try {
+ doThrow();
+ } catch (e) {
+ return "end of doCaught";
+ }
+}
+
+doUncaught() {
+ doThrow();
+ return "end of doUncaught";
+}
+
+final tests = <IsolateTest>[
+ (VmService service, IsolateRef isolateRef) async {
+ final isolate = await service.getIsolate(isolateRef.id!);
+ final lib = await service.getObject(isolateRef.id!, isolate.rootLib!.id!);
+
+ Completer? onPaused;
+ Completer? onResume;
+
+ final stream = service.onDebugEvent;
+ final subscription = stream.listen((Event event) {
+ print("Event $event");
+ if (event.kind == EventKind.kPauseException) {
+ if (onPaused == null) throw "Unexpected pause event $event";
+ final t = onPaused;
+ onPaused = null;
+ t!.complete(event);
+ }
+ if (event.kind == EventKind.kResume) {
+ if (onResume == null) throw "Unexpected resume event $event";
+ final t = onResume;
+ onResume = null;
+ t!.complete(event);
+ }
+ });
+ await service.streamListen(EventStreams.kDebug);
+
+ test(String pauseMode, String expression, bool shouldPause,
+ bool shouldBeCaught) async {
+ print("Evaluating $expression with pause on $pauseMode exception");
+
+ // ignore: deprecated_member_use_from_same_package
+ await service.setExceptionPauseMode(isolate.id!, pauseMode);
+
+ late Completer t;
+ if (shouldPause) {
+ t = Completer();
+ onPaused = t;
+ }
+ final fres = service.evaluate(isolate.id!, lib.id!, expression);
+ if (shouldPause) {
+ await t.future;
+
+ final stack = await service.getStack(isolate.id!);
+ expect(stack.frames![0].function!.name, 'doThrow');
+
+ t = Completer();
+ onResume = t;
+ await service.resume(isolate.id!);
+ await t.future;
+ }
+
+ dynamic res = await fres;
+ if (shouldBeCaught) {
+ expect(res is InstanceRef, true);
+ expect(res.kind, 'String');
+ expect(res.valueAsString, equals("end of doCaught"));
+ } else {
+ print(res.json);
+ expect(res is ErrorRef, true);
+ res = await service.getObject(isolate.id!, res.id!);
+ expect(res is Error, true);
+ expect(res.exception.kind, 'String');
+ expect(res.exception.valueAsString, equals("TheException"));
+ }
+ }
+
+ await test("All", "doCaught()", true, true);
+ await test("All", "doUncaught()", true, false);
+
+ await test("Unhandled", "doCaught()", false, true);
+ await test("Unhandled", "doUncaught()", true, false);
+
+ await test("None", "doCaught()", false, true);
+ await test("None", "doUncaught()", false, false);
+
+ await subscription.cancel();
+ },
+];
+
+main([args = const <String>[]]) => runIsolateTests(
+ args,
+ tests,
+ 'pause_on_exceptions_test.dart',
+ );
diff --git a/pkg/vm_service/test/pause_on_exceptions_test.dart b/pkg/vm_service/test/pause_on_exceptions_test.dart
new file mode 100644
index 0000000..9da7dfc
--- /dev/null
+++ b/pkg/vm_service/test/pause_on_exceptions_test.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+
+doThrow() {
+ throw "TheException"; // Line 13.
+}
+
+doCaught() {
+ try {
+ doThrow();
+ } catch (e) {
+ return "end of doCaught";
+ }
+}
+
+doUncaught() {
+ doThrow();
+ return "end of doUncaught";
+}
+
+final tests = <IsolateTest>[
+ (VmService service, IsolateRef isolateRef) async {
+ final isolate = await service.getIsolate(isolateRef.id!);
+ final lib = await service.getObject(isolateRef.id!, isolate.rootLib!.id!);
+
+ Completer? onPaused;
+ Completer? onResume;
+
+ final stream = service.onDebugEvent;
+ final subscription = stream.listen((Event event) {
+ print("Event $event");
+ if (event.kind == EventKind.kPauseException) {
+ if (onPaused == null) throw "Unexpected pause event $event";
+ final t = onPaused;
+ onPaused = null;
+ t!.complete(event);
+ }
+ if (event.kind == EventKind.kResume) {
+ if (onResume == null) throw "Unexpected resume event $event";
+ final t = onResume;
+ onResume = null;
+ t!.complete(event);
+ }
+ });
+ await service.streamListen(EventStreams.kDebug);
+
+ test(String pauseMode, String expression, bool shouldPause,
+ bool shouldBeCaught) async {
+ print("Evaluating $expression with pause on $pauseMode exception");
+
+ await service.setIsolatePauseMode(isolate.id!,
+ exceptionPauseMode: pauseMode);
+
+ late Completer t;
+ if (shouldPause) {
+ t = Completer();
+ onPaused = t;
+ }
+ final fres = service.evaluate(isolate.id!, lib.id!, expression);
+ if (shouldPause) {
+ await t.future;
+
+ final stack = await service.getStack(isolate.id!);
+ expect(stack.frames![0].function!.name, 'doThrow');
+
+ t = Completer();
+ onResume = t;
+ await service.resume(isolate.id!);
+ await t.future;
+ }
+
+ dynamic res = await fres;
+ if (shouldBeCaught) {
+ expect(res is InstanceRef, true);
+ expect(res.kind, 'String');
+ expect(res.valueAsString, equals("end of doCaught"));
+ } else {
+ print(res.json);
+ expect(res is ErrorRef, true);
+ res = await service.getObject(isolate.id!, res.id!);
+ expect(res is Error, true);
+ expect(res.exception.kind, 'String');
+ expect(res.exception.valueAsString, equals("TheException"));
+ }
+ }
+
+ await test("All", "doCaught()", true, true);
+ await test("All", "doUncaught()", true, false);
+
+ await test("Unhandled", "doCaught()", false, true);
+ await test("Unhandled", "doUncaught()", true, false);
+
+ await test("None", "doCaught()", false, true);
+ await test("None", "doUncaught()", false, false);
+
+ await subscription.cancel();
+ },
+];
+
+main([args = const <String>[]]) => runIsolateTests(
+ args,
+ tests,
+ 'pause_on_exceptions_test.dart',
+ );
diff --git a/pkg/vm_service/test/should_pause_on_exit_test.dart b/pkg/vm_service/test/should_pause_on_exit_test.dart
new file mode 100644
index 0000000..575530c
--- /dev/null
+++ b/pkg/vm_service/test/should_pause_on_exit_test.dart
@@ -0,0 +1,51 @@
+// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:test/test.dart';
+import 'package:vm_service/vm_service.dart';
+
+import 'common/test_helper.dart';
+
+void testMain() {
+ print('Hello world!');
+}
+
+Future<bool> shouldPauseOnExit(VmService service, IsolateRef isolateRef) async {
+ final isolate = await service.getIsolate(isolateRef.id!);
+ return isolate.pauseOnExit!;
+}
+
+final tests = <IsolateTest>[
+ (VmService service, IsolateRef isolateRef) async {
+ await service.setIsolatePauseMode(isolateRef.id!, shouldPauseOnExit: false);
+ expect(await shouldPauseOnExit(service, isolateRef), false);
+ final completer = Completer<void>();
+
+ final stream = service.onDebugEvent;
+ final subscription = stream.listen((Event event) {
+ if (event.kind == EventKind.kPauseExit) {
+ completer.complete();
+ }
+ });
+ await service.streamListen(EventStreams.kDebug);
+
+ await service.setIsolatePauseMode(isolateRef.id!, shouldPauseOnExit: true);
+ expect(await shouldPauseOnExit(service, isolateRef), true);
+ await service.resume(isolateRef.id!);
+ await completer.future;
+ await service.resume(isolateRef.id!);
+ await subscription.cancel();
+ },
+];
+
+void main([args = const <String>[]]) => runIsolateTests(
+ args,
+ tests,
+ 'should_pause_on_exit_test.dart',
+ pause_on_start: true,
+ pause_on_exit: true,
+ testeeConcurrent: testMain,
+ );
diff --git a/pkg/vm_service/tool/common/parser.dart b/pkg/vm_service/tool/common/parser.dart
index 08d3709..233320a 100644
--- a/pkg/vm_service/tool/common/parser.dart
+++ b/pkg/vm_service/tool/common/parser.dart
@@ -167,6 +167,23 @@
.trim();
}
+ String? consumeString() {
+ StringBuffer buf = StringBuffer();
+ String startQuotation = advance()!.text!;
+ if (startQuotation != '"' && startQuotation != "'") {
+ return null;
+ }
+ while (peek()!.text != startQuotation) {
+ Token t = advance()!;
+ if (t.text == null) {
+ throw FormatException('Reached EOF');
+ }
+ buf.write('${t.text} ');
+ }
+ advance();
+ return buf.toString().trim();
+ }
+
void validate(bool result, String message) {
if (!result) throw 'expected ${message}';
}
diff --git a/pkg/vm_service/tool/dart/generate_dart.dart b/pkg/vm_service/tool/dart/generate_dart.dart
index 844b1ff..4f2eb16 100644
--- a/pkg/vm_service/tool/dart/generate_dart.dart
+++ b/pkg/vm_service/tool/dart/generate_dart.dart
@@ -725,6 +725,9 @@
}
return result;
};
+ if (m.deprecated) {
+ gen.writeln("// ignore: deprecated_member_use_from_same_package");
+ }
gen.write("response = await _serviceImplementation.${m.name}(");
// Positional args
m.args.where((arg) => !arg.optional).forEach((MethodArg arg) {
@@ -1130,6 +1133,8 @@
final String? docs;
MemberType returnType = MemberType();
+ bool get deprecated => deprecationMessage != null;
+ String? deprecationMessage;
List<MethodArg> args = [];
Method(this.name, String definition, [this.docs]) {
@@ -1196,6 +1201,9 @@
}
if (_docs.isNotEmpty) gen.writeDocs(_docs);
}
+ if (deprecated) {
+ gen.writeln("@Deprecated('$deprecationMessage')");
+ }
if (withOverrides) gen.writeln('@override');
gen.write('Future<${returnType.name}> ${name}(');
bool startedOptional = false;
@@ -2128,7 +2136,12 @@
void parseInto(Method method) {
// method is return type, name, (, args )
// args is type name, [optional], comma
-
+ if (peek()?.text?.startsWith('@deprecated') ?? false) {
+ advance();
+ expect('(');
+ method.deprecationMessage = consumeString()!;
+ expect(')');
+ }
method.returnType.parse(this, isReturnType: true);
Token t = expectName();
diff --git a/pkg/vm_service/tool/java/generate_java.dart b/pkg/vm_service/tool/java/generate_java.dart
index 9ef243d..c80a481 100644
--- a/pkg/vm_service/tool/java/generate_java.dart
+++ b/pkg/vm_service/tool/java/generate_java.dart
@@ -496,6 +496,7 @@
final String? docs;
MemberType returnType = MemberType();
+ bool deprecated = false;
List<MethodArg> args = [];
Method(this.name, String definition, [this.docs]) {
@@ -606,7 +607,7 @@
}
}
writer.addLine('request("$name", params, consumer);');
- }, javadoc: javadoc.toString());
+ }, javadoc: javadoc.toString(), isDeprecated: deprecated);
}
void _parse(Token? token) {
@@ -647,6 +648,13 @@
// method is return type, name, (, args )
// args is type name, [optional], comma
+ if (peek()?.text?.startsWith('@deprecated') ?? false) {
+ advance();
+ expect('(');
+ consumeString();
+ expect(')');
+ method.deprecated = true;
+ }
method.returnType.parse(this);
Token t = expectName();
diff --git a/pkg/vm_service/tool/java/src_gen_java.dart b/pkg/vm_service/tool/java/src_gen_java.dart
index b67c203..a2e62e5 100644
--- a/pkg/vm_service/tool/java/src_gen_java.dart
+++ b/pkg/vm_service/tool/java/src_gen_java.dart
@@ -214,6 +214,7 @@
String? modifiers = 'public',
String? returnType = 'void',
bool isOverride = false,
+ bool isDeprecated = false,
}) {
var methodDecl = StringBuffer();
if (javadoc != null && javadoc.isNotEmpty) {
@@ -223,6 +224,9 @@
.forEach((line) => methodDecl.writeln(' * $line'.trimRight()));
methodDecl.writeln(' */');
}
+ if (isDeprecated) {
+ methodDecl.writeln(' @Deprecated');
+ }
if (isOverride) {
methodDecl.writeln(' @Override');
}
diff --git a/runtime/observatory/tests/service/get_version_rpc_test.dart b/runtime/observatory/tests/service/get_version_rpc_test.dart
index 4ad1d93..7b1f275 100644
--- a/runtime/observatory/tests/service/get_version_rpc_test.dart
+++ b/runtime/observatory/tests/service/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], 'Version');
expect(result['major'], 3);
- expect(result['minor'], 52);
+ expect(result['minor'], 53);
expect(result['_privateMajor'], 0);
expect(result['_privateMinor'], 0);
},
diff --git a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
index 12ffc1c..e9c87c9 100644
--- a/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
+++ b/runtime/observatory_2/tests/service_2/get_version_rpc_test.dart
@@ -12,7 +12,7 @@
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], equals('Version'));
expect(result['major'], equals(3));
- expect(result['minor'], equals(52));
+ expect(result['minor'], equals(53));
expect(result['_privateMajor'], equals(0));
expect(result['_privateMinor'], equals(0));
},
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index b7576a4..dba5444 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -5145,6 +5145,44 @@
PrintSuccess(js);
}
+static const MethodParameter* const set_isolate_pause_mode_params[] = {
+ ISOLATE_PARAMETER,
+ new EnumParameter("exceptionPauseMode", false, exception_pause_mode_names),
+ new BoolParameter("shouldPauseOnExit", false),
+ nullptr,
+};
+
+static void SetIsolatePauseMode(Thread* thread, JSONStream* js) {
+ bool state_changed = false;
+ const char* exception_pause_mode = js->LookupParam("exceptionPauseMode");
+ if (exception_pause_mode != nullptr) {
+ Dart_ExceptionPauseInfo info =
+ EnumMapper(exception_pause_mode, exception_pause_mode_names,
+ exception_pause_mode_values);
+ if (info == kInvalidExceptionPauseInfo) {
+ PrintInvalidParamError(js, "exceptionPauseMode");
+ return;
+ }
+ Isolate* isolate = thread->isolate();
+ isolate->debugger()->SetExceptionPauseInfo(info);
+ state_changed = true;
+ }
+
+ const char* pause_isolate_on_exit = js->LookupParam("shouldPauseOnExit");
+ if (pause_isolate_on_exit != nullptr) {
+ bool enable = BoolParameter::Parse(pause_isolate_on_exit, false);
+ thread->isolate()->message_handler()->set_should_pause_on_exit(enable);
+ state_changed = true;
+ }
+
+ if (state_changed && Service::debug_stream.enabled()) {
+ ServiceEvent event(thread->isolate(),
+ ServiceEvent::kDebuggerSettingsUpdate);
+ Service::HandleEvent(&event);
+ }
+ PrintSuccess(js);
+}
+
static const MethodParameter* const set_breakpoint_state_params[] = {
ISOLATE_PARAMETER,
new IdParameter("breakpointId", true),
@@ -5561,6 +5599,8 @@
set_breakpoint_state_params },
{ "setExceptionPauseMode", SetExceptionPauseMode,
set_exception_pause_mode_params },
+ { "setIsolatePauseMode", SetIsolatePauseMode,
+ set_isolate_pause_mode_params },
{ "setFlag", SetFlag,
set_flags_params },
{ "setLibraryDebuggable", SetLibraryDebuggable,
diff --git a/runtime/vm/service.h b/runtime/vm/service.h
index d519124..59e9e24 100644
--- a/runtime/vm/service.h
+++ b/runtime/vm/service.h
@@ -15,7 +15,7 @@
namespace dart {
#define SERVICE_PROTOCOL_MAJOR_VERSION 3
-#define SERVICE_PROTOCOL_MINOR_VERSION 52
+#define SERVICE_PROTOCOL_MINOR_VERSION 53
class Array;
class EmbedderServiceHandler;
diff --git a/runtime/vm/service/service.md b/runtime/vm/service/service.md
index ba4add1..0865d15 100644
--- a/runtime/vm/service/service.md
+++ b/runtime/vm/service/service.md
@@ -1,8 +1,8 @@
-# Dart VM Service Protocol 3.52
+# Dart VM Service Protocol 3.53
> Please post feedback to the [observatory-discuss group][discuss-list]
-This document describes of _version 3.52_ of the Dart VM Service Protocol. This
+This document describes of _version 3.53_ of the Dart VM Service Protocol. This
protocol is used to communicate with a running Dart Virtual Machine.
To use the Service Protocol, start the VM with the *--observe* flag.
@@ -1378,6 +1378,7 @@
### setExceptionPauseMode
```
+@deprecated('Use setIsolatePauseMode instead')
Success|Sentinel setExceptionPauseMode(string isolateId,
ExceptionPauseMode mode)
```
@@ -1394,6 +1395,31 @@
If _isolateId_ refers to an isolate which has exited, then the
_Collected_ [Sentinel](#sentinel) is returned.
+### setIsolatePauseMode
+
+```
+Success|Sentinel setIsolatePauseMode(string isolateId,
+ ExceptionPauseMode exceptionPauseMode [optional],
+ bool shouldPauseOnExit [optional])
+```
+
+The _setIsolatePauseMode_ RPC is used to control if or when an isolate will
+pause due to a change in execution state.
+
+The _shouldPauseOnExit_ parameter specify whether the target isolate should pause on exit.
+
+The _setExceptionPauseMode_ RPC is used to control if an isolate pauses when
+an exception is thrown.
+
+mode | meaning
+---- | -------
+None | Do not pause isolate on thrown exceptions
+Unhandled | Pause isolate on unhandled exceptions
+All | Pause isolate on all thrown exceptions
+
+If _isolateId_ refers to an isolate which has exited, then the
+_Collected_ [Sentinel](#sentinel) is returned.
+
### setFlag
```
@@ -1420,6 +1446,7 @@
provided value. If set to false when the profiler is already running, the
profiler will be stopped but may not free its sample buffer depending on
platform limitations.
+ * Isolate pause settings will only be applied to newly spawned isolates.
See [Success](#success).
@@ -4235,4 +4262,5 @@
3.50 | Added `returnType`, `parameters`, and `typeParameters` to `@Instance`, and `implicit` to `@Function`. Added `Parameter` type.
3.51 | Added optional `reportLines` parameter to `getSourceReport` RPC.
3.52 | Added `lookupResolvedPackageUris` and `lookupPackageUris` RPCs and `UriList` type.
+3.53 | Added `setIsolatePauseMode` RPC.
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss