Skip to content

Commit

Permalink
[ VM / Service ] Added getClientName, setClientName and `requireR…
Browse files Browse the repository at this point in the history
…esumeApproval` RPCs

Add support for naming VM service clients which allows for resume
permissions to be set for all clients of the same name. If a client
name requires resume approval, an isolate won't be resumed until all
clients which require resume approval have called the `resume` RPC.

Resume approvals can be set for the following pause events:
- PauseOnStart
- PausePostRequest (issued after `reloadSources(pause: true)`)
- PauseOnExit

Change-Id: I7dde3d8aaeccfcf47fa84f1f92159846f1560e16
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/133621
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
Reviewed-by: Gary Roumanis <grouma@google.com>
  • Loading branch information
bkonyi authored and commit-bot@chromium.org committed Feb 5, 2020
1 parent ce21fd0 commit 48808f7
Show file tree
Hide file tree
Showing 25 changed files with 1,235 additions and 40 deletions.
6 changes: 5 additions & 1 deletion pkg/vm_service/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# Changelog

## 2.3.2
- Added `getClientName`, `setClientName`, and `requireResumePermission` methods.
- Added `ClientName` class.

## 2.3.1
- Fixed issue where `dart:io` extensions were not being exported.

## 2.3.0
- Added `getHttpEnableTimelineLogging` and `setHttpEnableTimelineLogging` methods.
- Added `HttpTimelineLoggingState` class.`
- Added `HttpTimelineLoggingState` class.

## 2.2.1
- Fixed issue where `TimelineEvent.toJson` always returned an empty map.
Expand Down
7 changes: 7 additions & 0 deletions pkg/vm_service/example/vm_service_assert.dart
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,13 @@ vms.ClassList assertClassList(vms.ClassList obj) {
return obj;
}

vms.ClientName assertClientName(vms.ClientName obj) {
assertNotNull(obj);
assertString(obj.type);
assertString(obj.name);
return obj;
}

vms.CodeRef assertCodeRef(vms.CodeRef obj) {
assertNotNull(obj);
assertString(obj.type);
Expand Down
2 changes: 2 additions & 0 deletions pkg/vm_service/java/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
src/org/dartlang/vm/service/VmService.java
src/org/dartlang/vm/service/consumer/AllocationProfileConsumer.java
src/org/dartlang/vm/service/consumer/BreakpointConsumer.java
src/org/dartlang/vm/service/consumer/ClientNameConsumer.java
src/org/dartlang/vm/service/consumer/CpuSamplesConsumer.java
src/org/dartlang/vm/service/consumer/EvaluateConsumer.java
src/org/dartlang/vm/service/consumer/EvaluateInFrameConsumer.java
Expand Down Expand Up @@ -35,6 +36,7 @@ src/org/dartlang/vm/service/element/ClassHeapStats.java
src/org/dartlang/vm/service/element/ClassList.java
src/org/dartlang/vm/service/element/ClassObj.java
src/org/dartlang/vm/service/element/ClassRef.java
src/org/dartlang/vm/service/element/ClientName.java
src/org/dartlang/vm/service/element/Code.java
src/org/dartlang/vm/service/element/CodeKind.java
src/org/dartlang/vm/service/element/CodeRef.java
Expand Down
2 changes: 1 addition & 1 deletion pkg/vm_service/java/version.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version=3.27
version=3.29
122 changes: 117 additions & 5 deletions pkg/vm_service/lib/src/vm_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export 'snapshot_graph.dart'
HeapSnapshotObjectNoData,
HeapSnapshotObjectNullData;

const String vmServiceVersion = '3.27.0';
const String vmServiceVersion = '3.29.0';

/// @optional
const String optional = 'optional';
Expand Down Expand Up @@ -119,6 +119,7 @@ Map<String, Function> _typeFactories = {
'Class': Class.parse,
'ClassHeapStats': ClassHeapStats.parse,
'ClassList': ClassList.parse,
'ClientName': ClientName.parse,
'@Code': CodeRef.parse,
'Code': Code.parse,
'@Context': ContextRef.parse,
Expand Down Expand Up @@ -195,6 +196,7 @@ Map<String, List<String>> _methodReturnTypes = {
'evaluate': const ['InstanceRef', 'ErrorRef', 'Sentinel'],
'evaluateInFrame': const ['InstanceRef', 'ErrorRef', 'Sentinel'],
'getAllocationProfile': const ['AllocationProfile'],
'getClientName': const ['ClientName'],
'getCpuSamples': const ['CpuSamples'],
'getFlagList': const ['FlagList'],
'getInboundReferences': const ['InboundReferences', 'Sentinel'],
Expand All @@ -219,7 +221,9 @@ Map<String, List<String>> _methodReturnTypes = {
'reloadSources': const ['ReloadReport'],
'removeBreakpoint': const ['Success'],
'requestHeapSnapshot': const ['Success'],
'requirePermissionToResume': const ['Success'],
'resume': const ['Success'],
'setClientName': const ['Success'],
'setExceptionPauseMode': const ['Success'],
'setFlag': const ['Success', 'Error'],
'setLibraryDebuggable': const ['Success'],
Expand Down Expand Up @@ -447,6 +451,13 @@ abstract class VmServiceInterface {
Future<AllocationProfile> getAllocationProfile(String isolateId,
{bool reset, bool gc});

/// The `getClientName` RPC is used to retrieve the name associated with the
/// currently connected VM service client. If no name was previously set
/// through the [setClientName] RPC, a default name will be returned.
///
/// See [ClientName].
Future<ClientName> getClientName();

/// The `getCpuSamples` RPC is used to retrieve samples collected by the CPU
/// profiler. Only samples collected in the time range `[timeOriginMicros,
/// timeOriginMicros + timeExtentMicros]` will be reported.
Expand Down Expand Up @@ -690,8 +701,8 @@ abstract class VmServiceInterface {
/// `(timeOriginMicros, timeOriginMicros + timeExtentMicros)`.
///
/// If `getVMTimeline` is invoked while the current recorder is one of Fuchsia
/// or Macos or Systrace, the `114` error code, invalid timeline request, will be
/// returned as timeline events are handled by the OS in these modes.
/// or Macos or Systrace, the `114` error code, invalid timeline request, will
/// be returned as timeline events are handled by the OS in these modes.
Future<Timeline> getVMTimeline({int timeOriginMicros, int timeExtentMicros});

/// The `getVMTimelineFlags` RPC returns information about the current VM
Expand Down Expand Up @@ -775,6 +786,31 @@ abstract class VmServiceInterface {
/// offset.
Future<Success> requestHeapSnapshot(String isolateId);

/// The `requirePermissionToResume` RPC is used to change the pause/resume
/// behavior of isolates by providing a way for the VM service to wait for
/// approval to resume from some set of clients. This is useful for clients
/// which want to perform some operation on an isolate after a pause without
/// it being resumed by another client.
///
/// If the `onPauseStart` parameter is `true`, isolates will not resume after
/// pausing on start until the client sends a `resume` request and all other
/// clients which need to provide resume approval for this pause type have
/// done so.
///
/// If the `onPauseReload` parameter is `true`, isolates will not resume after
/// pausing after a reload until the client sends a `resume` request and all
/// other clients which need to provide resume approval for this pause type
/// have done so.
///
/// If the `onPauseExit` parameter is `true`, isolates will not resume after
/// pausing on exit until the client sends a `resume` request and all other
/// clients which need to provide resume approval for this pause type have
/// done so.
///
/// Important Notes:<strong>Important Notes:</strong>
Future<Success> requirePermissionToResume(
{bool onPauseStart, bool onPauseReload, bool onPauseExit});

/// The `resume` RPC is used to resume execution of a paused isolate.
///
/// If the `step` parameter is not provided, the program will resume regular
Expand All @@ -801,6 +837,15 @@ abstract class VmServiceInterface {
Future<Success> resume(String isolateId,
{/*StepOption*/ String step, int frameIndex});

/// The `setClientName` RPC is used to set a name to be associated with the
/// currently connected VM service client. If the `name` parameter is a
/// non-empty string, `name` will become the new name associated with the
/// client. If `name` is an empty string, the client's name will be reset to
/// its default name.
///
/// See [Success].
Future<Success> setClientName(String name);

/// The `setExceptionPauseMode` RPC is used to control if an isolate pauses
/// when an exception is thrown.
///
Expand Down Expand Up @@ -1038,6 +1083,9 @@ class VmServerConnection {
gc: params['gc'],
);
break;
case 'getClientName':
response = await _serviceImplementation.getClientName();
break;
case 'getCpuSamples':
response = await _serviceImplementation.getCpuSamples(
params['isolateId'],
Expand Down Expand Up @@ -1165,13 +1213,25 @@ class VmServerConnection {
params['isolateId'],
);
break;
case 'requirePermissionToResume':
response = await _serviceImplementation.requirePermissionToResume(
onPauseStart: params['onPauseStart'],
onPauseReload: params['onPauseReload'],
onPauseExit: params['onPauseExit'],
);
break;
case 'resume':
response = await _serviceImplementation.resume(
params['isolateId'],
step: params['step'],
frameIndex: params['frameIndex'],
);
break;
case 'setClientName':
response = await _serviceImplementation.setClientName(
params['name'],
);
break;
case 'setExceptionPauseMode':
response = await _serviceImplementation.setExceptionPauseMode(
params['isolateId'],
Expand Down Expand Up @@ -1486,6 +1546,9 @@ class VmService implements VmServiceInterface {
return _call('getAllocationProfile', m);
}

@override
Future<ClientName> getClientName() => _call('getClientName');

@override
Future<CpuSamples> getCpuSamples(
String isolateId, int timeOriginMicros, int timeExtentMicros) {
Expand Down Expand Up @@ -1668,6 +1731,22 @@ class VmService implements VmServiceInterface {
return _call('requestHeapSnapshot', {'isolateId': isolateId});
}

@override
Future<Success> requirePermissionToResume(
{bool onPauseStart, bool onPauseReload, bool onPauseExit}) {
Map m = {};
if (onPauseStart != null) {
m['onPauseStart'] = onPauseStart;
}
if (onPauseReload != null) {
m['onPauseReload'] = onPauseReload;
}
if (onPauseExit != null) {
m['onPauseExit'] = onPauseExit;
}
return _call('requirePermissionToResume', m);
}

@override
Future<Success> resume(String isolateId,
{/*StepOption*/ String step, int frameIndex}) {
Expand All @@ -1681,6 +1760,11 @@ class VmService implements VmServiceInterface {
return _call('resume', m);
}

@override
Future<Success> setClientName(String name) {
return _call('setClientName', {'name': name});
}

@override
Future<Success> setExceptionPauseMode(
String isolateId, /*ExceptionPauseMode*/ String mode) {
Expand Down Expand Up @@ -2718,6 +2802,34 @@ class ClassList extends Response {
String toString() => '[ClassList type: ${type}, classes: ${classes}]';
}

/// See [getClientName] and [setClientName].
class ClientName extends Response {
static ClientName parse(Map<String, dynamic> json) =>
json == null ? null : ClientName._fromJson(json);

/// The name of the currently connected VM service client.
String name;

ClientName({
@required this.name,
});
ClientName._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
name = json['name'];
}

@override
Map<String, dynamic> toJson() {
var json = <String, dynamic>{};
json['type'] = 'ClientName';
json.addAll({
'name': name,
});
return json;
}

String toString() => '[ClientName type: ${type}, name: ${name}]';
}

/// `CodeRef` is a reference to a `Code` object.
class CodeRef extends ObjRef {
static CodeRef parse(Map<String, dynamic> json) =>
Expand Down Expand Up @@ -6098,8 +6210,8 @@ class TimelineFlags extends Response {
json == null ? null : TimelineFlags._fromJson(json);

/// The name of the recorder currently in use. Recorder types include, but are
/// not limited to: Callback, Endless, Fuchsia, Macos, Ring, Startup, and Systrace.
/// Set to "null" if no recorder is currently set.
/// not limited to: Callback, Endless, Fuchsia, Macos, Ring, Startup, and
/// Systrace. Set to "null" if no recorder is currently set.
String recorderName;

/// The list of all available timeline streams.
Expand Down
2 changes: 1 addition & 1 deletion pkg/vm_service/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: vm_service
description: >-
A library to communicate with a service implementing the Dart VM
service protocol.
version: 2.3.1
version: 2.3.2

homepage: https://github.com/dart-lang/sdk/tree/master/pkg/vm_service

Expand Down
66 changes: 66 additions & 0 deletions runtime/observatory/tests/service/client_name_rpc_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2020, 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 'package:observatory/service_io.dart';
import 'package:unittest/unittest.dart';

import 'test_helper.dart';

var tests = <VMTest>[
(VM vm) async {
final defaultClientName = 'client1';
final clientName = 'agent-007';
var result = await vm.invokeRpcNoUpgrade('getClientName', {});
expect(result['type'], 'ClientName');
expect(result['name'], defaultClientName);

// Set the name for this client.
result = await vm.invokeRpcNoUpgrade(
'setClientName',
{
'name': clientName,
},
);
expect(result['type'], 'Success');

// Check it was set properly.
result = await vm.invokeRpcNoUpgrade('getClientName', {});
expect(result['type'], 'ClientName');
expect(result['name'], clientName);

// Check clearing works properly.
result = await vm.invokeRpcNoUpgrade(
'setClientName',
{
'name': '',
},
);
expect(result['type'], 'Success');

result = await vm.invokeRpcNoUpgrade('getClientName', {});
expect(result['type'], 'ClientName');
expect(result['name'], defaultClientName);
},
// Try to set an invalid agent name for this client.
(VM vm) async {
try {
await vm.invokeRpcNoUpgrade(
'setClientName',
{
'name': 42,
},
);
fail('Successfully set invalid client name');
} on ServerRpcException catch (e) {/* expected */}
},
// Missing parameters.
(VM vm) async {
try {
await vm.invokeRpcNoUpgrade('setClientName', {});
fail('Successfully set name with no type');
} on ServerRpcException catch (e) {/* expected */}
},
];

main(args) async => runVMTests(args, tests);

0 comments on commit 48808f7

Please sign in to comment.