Reland "[vm/service] Introduce IsolateGroup entity to vm service api.""

This reverts commit f020ce5d23 with patchset 1 having original revert, rest - fixes
for calling destructors on unlinked IntrusiveDL elements and for assuming int is int64(which breaks down on simarm, ia32 bots).

Fixes https://github.com/dart-lang/sdk/issues/38926

Issue https://github.com/dart-lang/sdk/issues/36097

Change-Id: I867526c7de3786806670d1f43dbff07228f80028
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/121870
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
Alexander Aprelev 2019-10-17 03:45:41 +00:00 committed by commit-bot@chromium.org
parent 8e7fa2a35b
commit 9e636b5ab4
29 changed files with 936 additions and 47 deletions

View file

@ -672,6 +672,33 @@ vms.Isolate assertIsolate(vms.Isolate obj) {
return obj;
}
vms.IsolateGroupRef assertIsolateGroupRef(vms.IsolateGroupRef obj) {
assertNotNull(obj);
assertString(obj.type);
assertString(obj.id);
assertString(obj.number);
assertString(obj.name);
return obj;
}
List<vms.IsolateGroupRef> assertListOfIsolateGroupRef(
List<vms.IsolateGroupRef> list) {
for (vms.IsolateGroupRef elem in list) {
assertIsolateGroupRef(elem);
}
return list;
}
vms.IsolateGroup assertIsolateGroup(vms.IsolateGroup obj) {
assertNotNull(obj);
assertString(obj.type);
assertString(obj.id);
assertString(obj.number);
assertString(obj.name);
assertListOfIsolateRef(obj.isolates);
return obj;
}
vms.InboundReferences assertInboundReferences(vms.InboundReferences obj) {
assertNotNull(obj);
assertString(obj.type);
@ -1108,5 +1135,6 @@ vms.VM assertVM(vms.VM obj) {
assertInt(obj.pid);
assertInt(obj.startTime);
assertListOfIsolateRef(obj.isolates);
assertListOfIsolateGroupRef(obj.isolateGroups);
return obj;
}

View file

@ -9,6 +9,8 @@ src/org/dartlang/vm/service/consumer/EvaluateInFrameConsumer.java
src/org/dartlang/vm/service/consumer/FlagListConsumer.java
src/org/dartlang/vm/service/consumer/GetInboundReferencesConsumer.java
src/org/dartlang/vm/service/consumer/GetIsolateConsumer.java
src/org/dartlang/vm/service/consumer/GetIsolateGroupConsumer.java
src/org/dartlang/vm/service/consumer/GetIsolateGroupMemoryUsageConsumer.java
src/org/dartlang/vm/service/consumer/GetMemoryUsageConsumer.java
src/org/dartlang/vm/service/consumer/GetObjectConsumer.java
src/org/dartlang/vm/service/consumer/InstanceSetConsumer.java
@ -63,6 +65,8 @@ src/org/dartlang/vm/service/element/InstanceKind.java
src/org/dartlang/vm/service/element/InstanceRef.java
src/org/dartlang/vm/service/element/InstanceSet.java
src/org/dartlang/vm/service/element/Isolate.java
src/org/dartlang/vm/service/element/IsolateGroup.java
src/org/dartlang/vm/service/element/IsolateGroupRef.java
src/org/dartlang/vm/service/element/IsolateRef.java
src/org/dartlang/vm/service/element/Library.java
src/org/dartlang/vm/service/element/LibraryDependency.java

View file

@ -131,6 +131,8 @@ Map<String, Function> _typeFactories = {
'Instance': Instance.parse,
'@Isolate': IsolateRef.parse,
'Isolate': Isolate.parse,
'@IsolateGroup': IsolateGroupRef.parse,
'IsolateGroup': IsolateGroup.parse,
'InboundReferences': InboundReferences.parse,
'InboundReference': InboundReference.parse,
'InstanceSet': InstanceSet.parse,
@ -188,7 +190,9 @@ Map<String, List<String>> _methodReturnTypes = {
'getInboundReferences': const ['InboundReferences', 'Sentinel'],
'getInstances': const ['InstanceSet'],
'getIsolate': const ['Isolate', 'Sentinel'],
'getIsolateGroup': const ['IsolateGroup', 'Sentinel'],
'getMemoryUsage': const ['MemoryUsage', 'Sentinel'],
'getIsolateGroupMemoryUsage': const ['MemoryUsage', 'Sentinel'],
'getScripts': const ['ScriptList'],
'getObject': const ['Obj', 'Sentinel'],
'getRetainingPath': const ['RetainingPath'],
@ -506,6 +510,21 @@ abstract class VmServiceInterface {
/// The return value can be one of [Isolate] or [Sentinel].
Future<dynamic> getIsolate(String isolateId);
/// The `getIsolateGroup` RPC is used to lookup an `IsolateGroup` object by
/// its `id`.
///
/// If `isolateGroupId` refers to an isolate group which has exited, then the
/// `Expired` [Sentinel] is returned.
///
/// `IsolateGroup` `id` is an opaque identifier that can be fetched from an
/// `IsolateGroup`. List of active `IsolateGroup`'s, for example, is available
/// on `VM` object.
///
/// See [IsolateGroup], [VM].
///
/// The return value can be one of [IsolateGroup] or [Sentinel].
Future<dynamic> getIsolateGroup(String isolateGroupId);
/// The `getMemoryUsage` RPC is used to lookup an isolate's memory usage
/// statistics by its `id`.
///
@ -517,6 +536,17 @@ abstract class VmServiceInterface {
/// The return value can be one of [MemoryUsage] or [Sentinel].
Future<dynamic> getMemoryUsage(String isolateId);
/// The `getIsolateGroupMemoryUsage` RPC is used to lookup an isolate group's
/// memory usage statistics by its `id`.
///
/// If `isolateGroupId` refers to an isolate group which has exited, then the
/// `Expired` [Sentinel] is returned.
///
/// See [IsolateGroup].
///
/// The return value can be one of [MemoryUsage] or [Sentinel].
Future<dynamic> getIsolateGroupMemoryUsage(String isolateGroupId);
/// The `getScripts` RPC is used to retrieve a `ScriptList` containing all
/// scripts for an isolate based on the isolate's `isolateId`.
///
@ -1027,11 +1057,21 @@ class VmServerConnection {
params['isolateId'],
);
break;
case 'getIsolateGroup':
response = await _serviceImplementation.getIsolateGroup(
params['isolateGroupId'],
);
break;
case 'getMemoryUsage':
response = await _serviceImplementation.getMemoryUsage(
params['isolateId'],
);
break;
case 'getIsolateGroupMemoryUsage':
response = await _serviceImplementation.getIsolateGroupMemoryUsage(
params['isolateGroupId'],
);
break;
case 'getScripts':
response = await _serviceImplementation.getScripts(
params['isolateId'],
@ -1468,11 +1508,22 @@ class VmService implements VmServiceInterface {
return _call('getIsolate', {'isolateId': isolateId});
}
@override
Future<dynamic> getIsolateGroup(String isolateGroupId) {
return _call('getIsolateGroup', {'isolateGroupId': isolateGroupId});
}
@override
Future<dynamic> getMemoryUsage(String isolateId) {
return _call('getMemoryUsage', {'isolateId': isolateId});
}
@override
Future<dynamic> getIsolateGroupMemoryUsage(String isolateGroupId) {
return _call(
'getIsolateGroupMemoryUsage', {'isolateGroupId': isolateGroupId});
}
@override
Future<ScriptList> getScripts(String isolateId) {
return _call('getScripts', {'isolateId': isolateId});
@ -1699,6 +1750,7 @@ class VmService implements VmServiceInterface {
void dispose() {
_streamSub.cancel();
_completers.values.forEach((c) => c.completeError('disposed'));
_completers.clear();
if (_disposeHandler != null) {
_disposeHandler();
}
@ -4444,6 +4496,105 @@ class Isolate extends Response {
String toString() => '[Isolate]';
}
/// `IsolateGroupRef` is a reference to an `IsolateGroup` object.
class IsolateGroupRef extends Response {
static IsolateGroupRef parse(Map<String, dynamic> json) =>
json == null ? null : IsolateGroupRef._fromJson(json);
/// The id which is passed to the getIsolateGroup RPC to load this isolate
/// group.
String id;
/// A numeric id for this isolate group, represented as a string. Unique.
String number;
/// A name identifying this isolate group. Not guaranteed to be unique.
String name;
IsolateGroupRef({
@required this.id,
@required this.number,
@required this.name,
});
IsolateGroupRef._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
id = json['id'];
number = json['number'];
name = json['name'];
}
@override
Map<String, dynamic> toJson() {
var json = <String, dynamic>{};
json['type'] = '@IsolateGroup';
json.addAll({
'id': id,
'number': number,
'name': name,
});
return json;
}
int get hashCode => id.hashCode;
operator ==(other) => other is IsolateGroupRef && id == other.id;
String toString() =>
'[IsolateGroupRef type: ${type}, id: ${id}, number: ${number}, name: ${name}]';
}
/// An `Isolate` object provides information about one isolate in the VM.
class IsolateGroup extends Response {
static IsolateGroup parse(Map<String, dynamic> json) =>
json == null ? null : IsolateGroup._fromJson(json);
/// The id which is passed to the getIsolate RPC to reload this isolate.
String id;
/// A numeric id for this isolate, represented as a string. Unique.
String number;
/// A name identifying this isolate. Not guaranteed to be unique.
String name;
/// A list of all isolates in this isolate group.
List<IsolateRef> isolates;
IsolateGroup({
@required this.id,
@required this.number,
@required this.name,
@required this.isolates,
});
IsolateGroup._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
id = json['id'];
number = json['number'];
name = json['name'];
isolates = List<IsolateRef>.from(
createServiceObject(json['isolates'], const ['IsolateRef']));
}
@override
Map<String, dynamic> toJson() {
var json = <String, dynamic>{};
json['type'] = 'IsolateGroup';
json.addAll({
'id': id,
'number': number,
'name': name,
'isolates': isolates.map((f) => f.toJson()).toList(),
});
return json;
}
int get hashCode => id.hashCode;
operator ==(other) => other is IsolateGroup && id == other.id;
String toString() => '[IsolateGroup ' //
'type: ${type}, id: ${id}, number: ${number}, name: ${name}, ' //
'isolates: ${isolates}]';
}
/// See [getInboundReferences].
class InboundReferences extends Response {
static InboundReferences parse(Map<String, dynamic> json) =>
@ -6236,6 +6387,9 @@ class VM extends Response {
/// A list of isolates running in the VM.
List<IsolateRef> isolates;
/// A list of isolate groups running in the VM.
List<IsolateGroupRef> isolateGroups;
VM({
@required this.name,
@required this.architectureBits,
@ -6246,6 +6400,7 @@ class VM extends Response {
@required this.pid,
@required this.startTime,
@required this.isolates,
@required this.isolateGroups,
});
VM._fromJson(Map<String, dynamic> json) : super._fromJson(json) {
name = json['name'];
@ -6258,6 +6413,8 @@ class VM extends Response {
startTime = json['startTime'];
isolates = List<IsolateRef>.from(
createServiceObject(json['isolates'], const ['IsolateRef']));
isolateGroups = List<IsolateGroupRef>.from(
createServiceObject(json['isolateGroups'], const ['IsolateGroupRef']));
}
@override
@ -6274,6 +6431,7 @@ class VM extends Response {
'pid': pid,
'startTime': startTime,
'isolates': isolates.map((f) => f.toJson()).toList(),
'isolateGroups': isolateGroups.map((f) => f.toJson()).toList(),
});
return json;
}

View file

@ -0,0 +1,35 @@
// Copyright (c) 2019, 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:test/test.dart';
import 'package:vm_service/vm_service.dart';
import 'common/test_helper.dart';
var tests = <VMTest>[
(VmService service) async {
final vm = await service.getVM();
final result =
await service.getIsolateGroupMemoryUsage(vm.isolateGroups.first.id);
expect(result.heapUsage, isPositive);
expect(result.heapCapacity, isPositive);
expect(result.externalUsage, isNonNegative);
},
(VmService service) async {
bool caughtException;
try {
await service.getMemoryUsage('badid');
fail('Unreachable');
} on RPCError catch (e) {
caughtException = true;
expect(
e.details,
contains(
"getMemoryUsage: invalid 'isolateGroupId' parameter: badid"));
}
expect(caughtException, isTrue);
},
];
main(args) async => runVMTests(args, tests);

View file

@ -10,3 +10,8 @@ analyzer:
- tests/service/get_isolate_after_language_error_test.dart
- tests/service/get_user_level_retaining_path_rpc_test.dart
- tests/service/pause_on_unhandled_async_exceptions_test.dart
linter:
rules:
- prefer_final_fields
- prefer_final_locals

View file

@ -29,6 +29,7 @@ part 'src/models/objects/icdata.dart';
part 'src/models/objects/inbound_references.dart';
part 'src/models/objects/instance.dart';
part 'src/models/objects/isolate.dart';
part 'src/models/objects/isolate_group.dart';
part 'src/models/objects/library.dart';
part 'src/models/objects/local_var_descriptors.dart';
part 'src/models/objects/map_association.dart';
@ -74,6 +75,7 @@ part 'src/models/repositories/icdata.dart';
part 'src/models/repositories/inbound_references.dart';
part 'src/models/repositories/instance.dart';
part 'src/models/repositories/isolate.dart';
part 'src/models/repositories/isolate_group.dart';
part 'src/models/repositories/library.dart';
part 'src/models/repositories/megamorphiccache.dart';
part 'src/models/repositories/metric.dart';

View file

@ -30,6 +30,7 @@ part 'src/repositories/icdata.dart';
part 'src/repositories/inbound_references.dart';
part 'src/repositories/instance.dart';
part 'src/repositories/isolate.dart';
part 'src/repositories/isolate_group.dart';
part 'src/repositories/library.dart';
part 'src/repositories/megamorphiccache.dart';
part 'src/repositories/metric.dart';

View file

@ -0,0 +1,22 @@
// Copyright (c) 2019, 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.
part of models;
abstract class IsolateGroupRef {
/// The id which is passed to the getIsolateGroup RPC to reload this
/// isolate group.
String get id;
/// A numeric id for this isolate group, represented as a string. Unique.
int get number;
/// A name identifying this isolate group. Not guaranteed to be unique.
String get name;
}
abstract class IsolateGroup extends IsolateGroupRef {
/// A list of all isolates in this isolate group.
Iterable<IsolateRef> get isolates;
}

View file

@ -50,6 +50,7 @@ abstract class VM implements VMRef {
// A list of isolates running in the VM.
Iterable<IsolateRef> get isolates;
Iterable<IsolateGroupRef> get isolateGroups;
/// Enable the sampling profiler.
Future enableProfiler();

View file

@ -0,0 +1,9 @@
// Copyright (c) 2019, 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
part of models;
abstract class IsolateGroupRepository {
Future<IsolateGroup> get(IsolateGroupRef isolateGroup);
}

View file

@ -0,0 +1,22 @@
// Copyright (c) 2019, 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
part of repositories;
class IsolateGroupRepository extends M.IsolateGroupRepository {
final S.VM _vm;
IsolateGroupRepository(this._vm) : assert(_vm != null);
Future<M.IsolateGroup> get(M.IsolateGroupRef i) async {
S.IsolateGroup isolateGroup = i as S.IsolateGroup;
assert(isolateGroup != null);
try {
await isolateGroup.reload();
} on SC.NetworkRpcException catch (_) {
/* ignore */
}
return isolateGroup;
}
}

View file

@ -229,6 +229,9 @@ abstract class ServiceObject implements M.ObjectRef {
case 'Isolate':
obj = new Isolate._empty(owner.vm);
break;
case 'IsolateGroup':
obj = new IsolateGroup._empty(owner.vm);
break;
case 'Library':
obj = new Library._empty(owner);
break;
@ -661,10 +664,13 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
// TODO(johnmccutchan): Ensure that isolates do not end up in _cache.
Map<String, ServiceObject> _cache = new Map<String, ServiceObject>();
final Map<String, Isolate> _isolateCache = <String, Isolate>{};
final Map<String, IsolateGroup> _isolateGroupCache = <String, IsolateGroup>{};
// The list of live isolates, ordered by isolate start time.
final List<Isolate> isolates = <Isolate>[];
final List<IsolateGroup> isolateGroups = <IsolateGroup>[];
final List<Service> services = <Service>[];
String version = 'unknown';
@ -771,6 +777,7 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
}
static final String _isolateIdPrefix = 'isolates/';
static final String _isolateGroupIdPrefix = 'isolateGroups/';
ServiceObject getFromMap(Map map) {
if (map == null) {
@ -784,23 +791,44 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
}
String id = map['id'];
if ((id != null) && id.startsWith(_isolateIdPrefix)) {
// Check cache.
var isolate = _isolateCache[id];
if (isolate == null) {
// Add new isolate to the cache.
isolate = new ServiceObject._fromMap(this, map);
_isolateCache[id] = isolate;
_buildIsolateList();
if ((id != null)) {
if (id.startsWith(_isolateIdPrefix)) {
// Check cache.
var isolate = _isolateCache[id];
if (isolate == null) {
// Add new isolate to the cache.
isolate = ServiceObject._fromMap(this, map);
_isolateCache[id] = isolate;
_buildIsolateList();
// Eagerly load the isolate.
isolate.load().catchError((e, stack) {
Logger.root.info('Eagerly loading an isolate failed: $e\n$stack');
});
} else {
isolate.updateFromServiceMap(map);
// Eagerly load the isolate.
isolate.load().catchError((e, stack) {
Logger.root.info('Eagerly loading an isolate failed: $e\n$stack');
});
} else {
isolate.updateFromServiceMap(map);
}
return isolate;
}
if (id.startsWith(_isolateGroupIdPrefix)) {
// Check cache.
var isolateGroup = _isolateGroupCache[id];
if (isolateGroup == null) {
// Add new isolate to the cache.
isolateGroup = new ServiceObject._fromMap(this, map);
_isolateGroupCache[id] = isolateGroup;
_buildIsolateGroupList();
// Eagerly load the isolate.
isolateGroup.load().catchError((e, stack) {
Logger.root
.info('Eagerly loading an isolate group failed: $e\n$stack');
});
} else {
isolateGroup.updateFromServiceMap(map);
}
return isolateGroup;
}
return isolate;
}
// Build the object from the map directly.
@ -817,6 +845,27 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
return new Future.value(_isolateCache[isolateId]);
}
int _compareIsolateGroups(IsolateGroup a, IsolateGroup b) {
return a.id.compareTo(b.id);
}
void _buildIsolateGroupList() {
final isolateGroupList = _isolateGroupCache.values.toList();
isolateGroupList.sort(_compareIsolateGroups);
isolateGroups.clear();
isolateGroups.addAll(isolateGroupList);
}
void _removeDeadIsolateGroups(List newIsolateGroups) {
// Build a set of new isolates.
final Set newIsolateGroupSet =
newIsolateGroups.map((iso) => iso.id).toSet();
// Remove any old isolates which no longer exist.
_isolateGroupCache.removeWhere((id, _) => !newIsolateGroupSet.contains(id));
_buildIsolateGroupList();
}
// Implemented in subclass.
Future<Map> invokeRpcRaw(String method, Map params);
@ -989,6 +1038,7 @@ abstract class VM extends ServiceObjectOwner implements M.VM {
assertsEnabled = map['_assertsEnabled'];
typeChecksEnabled = map['_typeChecksEnabled'];
_removeDeadIsolates(map['isolates']);
_removeDeadIsolateGroups(map['isolateGroups']);
}
// Reload all isolates.
@ -1220,6 +1270,77 @@ class HeapSpace implements M.HeapSpace {
}
}
class IsolateGroup extends ServiceObjectOwner implements M.IsolateGroup {
IsolateGroup._empty(ServiceObjectOwner owner)
: assert(owner is VM),
super._empty(owner);
@override
void _update(Map map, bool mapIsRef) {
name = map['name'];
vmName = map.containsKey('_vmName') ? map['_vmName'] : name;
number = int.tryParse(map['number']);
if (mapIsRef) {
return;
}
_loaded = true;
isolates.clear();
isolates.addAll(map['isolates'] as List<Isolate>);
isolates.sort(ServiceObject.LexicalSortName);
vm._buildIsolateGroupList();
}
@override
ServiceObject getFromMap(Map map) {
if (map == null) {
return null;
}
final mapType = _stripRef(map['type']);
if (mapType == 'IsolateGroup') {
// There are sometimes isolate group refs in ServiceEvents.
return vm.getFromMap(map);
}
String mapId = map['id'];
var obj = (mapId != null) ? _cache[mapId] : null;
if (obj != null) {
obj.updateFromServiceMap(map);
return obj;
}
// Build the object from the map directly.
obj = new ServiceObject._fromMap(this, map);
if ((obj != null) && obj.canCache) {
_cache[mapId] = obj;
}
return obj;
}
Future<Map> invokeRpcNoUpgrade(String method, Map params) {
params['isolateGroupId'] = id;
return vm.invokeRpcNoUpgrade(method, params);
}
Future<ServiceObject> invokeRpc(String method, Map params) {
return invokeRpcNoUpgrade(method, params)
.then((Map response) => getFromMap(response));
}
@override
Future<Map> _fetchDirect({int count: kDefaultFieldLimit}) {
Map params = {
'isolateGroupId': id,
};
return vm.invokeRpcNoUpgrade('getIsolateGroup', params);
}
@override
final List<Isolate> isolates = <Isolate>[];
@override
int number;
final Map<String, ServiceObject> _cache = Map<String, ServiceObject>();
}
/// State for a running isolate.
class Isolate extends ServiceObjectOwner implements M.Isolate {
static const kLoggingStream = 'Logging';

View file

@ -164,6 +164,7 @@ observatory_sources = [
"lib/src/models/objects/inbound_references.dart",
"lib/src/models/objects/instance.dart",
"lib/src/models/objects/isolate.dart",
"lib/src/models/objects/isolate_group.dart",
"lib/src/models/objects/library.dart",
"lib/src/models/objects/local_var_descriptors.dart",
"lib/src/models/objects/map_association.dart",
@ -208,6 +209,7 @@ observatory_sources = [
"lib/src/models/repositories/inbound_references.dart",
"lib/src/models/repositories/instance.dart",
"lib/src/models/repositories/isolate.dart",
"lib/src/models/repositories/isolate_group.dart",
"lib/src/models/repositories/library.dart",
"lib/src/models/repositories/megamorphiccache.dart",
"lib/src/models/repositories/metric.dart",
@ -245,6 +247,7 @@ observatory_sources = [
"lib/src/repositories/inbound_references.dart",
"lib/src/repositories/instance.dart",
"lib/src/repositories/isolate.dart",
"lib/src/repositories/isolate_group.dart",
"lib/src/repositories/library.dart",
"lib/src/repositories/megamorphiccache.dart",
"lib/src/repositories/metric.dart",

View file

@ -0,0 +1,64 @@
// Copyright (c) 2019, 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 params = {
'isolateGroupId': vm.isolateGroups.first.id,
};
final result =
await vm.invokeRpcNoUpgrade('getIsolateGroupMemoryUsage', params);
expect(result['type'], equals('MemoryUsage'));
expect(result['heapUsage'], isPositive);
expect(result['heapCapacity'], isPositive);
expect(result['externalUsage'], isPositive);
},
(VM vm) async {
final params = {
'isolateGroupId': 'badid',
};
bool caughtException;
try {
await vm.invokeRpcNoUpgrade('getIsolateGroupMemoryUsage', params);
expect(false, isTrue, reason: 'Unreachable');
} on ServerRpcException catch (e) {
caughtException = true;
expect(e.code, equals(ServerRpcException.kInvalidParams));
expect(e.message,
"getIsolateGroupMemoryUsage: invalid 'isolateGroupId' parameter: badid");
}
expect(caughtException, isTrue);
},
// Plausible isolate group id, not found.
(VM vm) async {
final params = {
'isolateGroupId': 'isolateGroups/9999999999',
};
final result =
await vm.invokeRpcNoUpgrade('getIsolateGroupMemoryUsage', params);
expect(result['type'], equals('Sentinel'));
expect(result['kind'], equals('Expired'));
expect(result['valueAsString'], equals('<expired>'));
},
// isolate id was passed instead of isolate group id.
(VM vm) async {
final params = {
'isolateId': 'isolates/9999999999',
};
final result =
await vm.invokeRpcNoUpgrade('getIsolateGroupMemoryUsage', params);
expect(result['type'], equals('Sentinel'));
expect(result['kind'], equals('Expired'));
expect(result['valueAsString'], equals('<expired>'));
},
];
main(args) async => runVMTests(args, tests);

View file

@ -12,7 +12,7 @@ var tests = <VMTest>[
var result = await vm.invokeRpcNoUpgrade('getVersion', {});
expect(result['type'], equals('Version'));
expect(result['major'], equals(3));
expect(result['minor'], equals(27));
expect(result['minor'], equals(28));
expect(result['_privateMajor'], equals(0));
expect(result['_privateMinor'], equals(0));
},

View file

@ -24,6 +24,8 @@ var tests = <VMTest>[
expect(result['startTime'], isPositive);
expect(result['isolates'].length, isPositive);
expect(result['isolates'][0]['type'], equals('@Isolate'));
expect(result['isolateGroups'].length, isPositive);
expect(result['isolateGroups'][0]['type'], equals('@IsolateGroup'));
},
];

View file

@ -0,0 +1,83 @@
// Copyright (c) 2019, 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 'dart:convert';
import 'dart:developer';
import 'dart:io' as io;
import 'package:observatory/service_io.dart' as S;
import 'package:unittest/unittest.dart';
import 'test_helper.dart';
Future<String> getIsolateGroupId(
io.HttpClient httpClient, Uri serverUri) async {
// Build the request.
final pathSegments = <String>[]..addAll(serverUri.pathSegments);
const method = 'getVM';
if (pathSegments.isNotEmpty) {
pathSegments[pathSegments.length - 1] = method;
} else {
pathSegments.add(method);
}
final requestUri = serverUri.replace(pathSegments: pathSegments);
final request = await httpClient.getUrl(requestUri);
final Map response = await (await request.close())
.cast<List<int>>()
.transform(utf8.decoder)
.transform(json.decoder)
.first;
final result = response['result'];
return result['isolateGroups'][0]['id'];
}
Future<Null> testeeBefore() async {
print('testee before');
print(await Service.getInfo());
// Start the web server.
final ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
expect(info.serverUri, isNotNull);
final httpClient = new io.HttpClient();
// Build the request.
final params = <String, String>{
'isolateGroupId': await getIsolateGroupId(httpClient, info.serverUri),
};
const method = 'getIsolateGroup';
final pathSegments = <String>[]..addAll(info.serverUri.pathSegments);
if (pathSegments.isNotEmpty) {
pathSegments[pathSegments.length - 1] = method;
} else {
pathSegments.add(method);
}
final requestUri = info.serverUri
.replace(pathSegments: pathSegments, queryParameters: params);
try {
final request = await httpClient.getUrl(requestUri);
final response = await request.close();
final Map jsonResponse = await response
.cast<List<int>>()
.transform(utf8.decoder)
.transform(json.decoder)
.first;
final result = jsonResponse['result'];
expect(result['type'], equals('IsolateGroup'));
expect(result['id'], startsWith('isolateGroups/'));
expect(result['number'], new isInstanceOf<String>());
expect(result['isolates'].length, isPositive);
expect(result['isolates'][0]['type'], equals('@Isolate'));
} catch (e) {
fail('invalid request: $e');
}
}
var tests = <IsolateTest>[
(S.Isolate isolate) async {
await isolate.reload();
// Just getting here means that the testee enabled the service protocol
// web server.
expect(true, true);
}
];

View file

@ -0,0 +1,14 @@
// Copyright (c) 2019, 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 'http_get_isolate_group_rpc_common.dart';
import 'test_helper.dart';
main(args) {
runIsolateTests(args, tests,
testeeBefore: testeeBefore,
// the testee is responsible for starting the
// web server.
testeeControlsServer: true);
}

View file

@ -78,6 +78,7 @@ Future<Null> testeeBefore() async {
expect(result['breakpoints'].length, isZero);
expect(result['_heaps']['new']['type'], equals('HeapSpace'));
expect(result['_heaps']['old']['type'], equals('HeapSpace'));
expect(result['isolate_group']['type'], equals('@IsolateGroup'));
} catch (e) {
fail('invalid request: $e');
}

View file

@ -46,6 +46,8 @@ Future<Null> testeeBefore() async {
expect(result['startTime'], isPositive);
expect(result['isolates'].length, isPositive);
expect(result['isolates'][0]['type'], equals('@Isolate'));
expect(result['isolateGroups'].length, isPositive);
expect(result['isolateGroups'][0]['type'], equals('@IsolateGroup'));
} catch (e) {
fail('invalid request: $e');
}

View file

@ -216,6 +216,7 @@ char* Dart::Init(const uint8_t* vm_isolate_snapshot,
TimelineDurationScope tds(Timeline::GetVMStream(), "Dart::Init");
#endif
Isolate::InitVM();
IsolateGroup::Init();
PortMap::Init();
FreeListElement::Init();
ForwardingCorpse::Init();
@ -252,6 +253,7 @@ char* Dart::Init(const uint8_t* vm_isolate_snapshot,
new IsolateGroupSource(nullptr, "vm-isolate", nullptr, nullptr, nullptr,
nullptr, nullptr, -1, api_flags));
auto group = new IsolateGroup(std::move(source), /*embedder_data=*/nullptr);
IsolateGroup::RegisterIsolateGroup(group);
vm_isolate_ =
Isolate::InitIsolate("vm-isolate", group, api_flags, is_vm_isolate);
group->set_initial_spawn_successful();
@ -581,6 +583,7 @@ char* Dart::Cleanup() {
vm_isolate_ = NULL;
ASSERT(Isolate::IsolateListLength() == 0);
PortMap::Cleanup();
IsolateGroup::Cleanup();
ICData::Cleanup();
SubtypeTestCache::Cleanup();
ArgumentsDescriptor::Cleanup();

View file

@ -1236,6 +1236,7 @@ Dart_CreateIsolateGroup(const char* script_uri,
script_uri, non_null_name, snapshot_data, snapshot_instructions,
shared_data, shared_instructions, nullptr, -1, *flags));
auto group = new IsolateGroup(std::move(source), isolate_group_data);
IsolateGroup::RegisterIsolateGroup(group);
Dart_Isolate isolate =
CreateIsolate(group, non_null_name, isolate_data, error);
if (isolate != nullptr) {
@ -1266,6 +1267,7 @@ Dart_CreateIsolateGroupFromKernel(const char* script_uri,
script_uri, non_null_name, nullptr, nullptr, nullptr, nullptr,
kernel_buffer, kernel_buffer_size, *flags));
auto group = new IsolateGroup(std::move(source), isolate_group_data);
IsolateGroup::RegisterIsolateGroup(group);
Dart_Isolate isolate =
CreateIsolate(group, non_null_name, isolate_data, error);
if (isolate != nullptr) {

View file

@ -173,6 +173,7 @@ void IsolateGroup::UnregisterIsolate(Isolate* isolate) {
group_shutdown_callback(embedder_data());
}
}
UnregisterIsolateGroup(this);
delete this;
}
}
@ -316,6 +317,110 @@ void IsolateGroup::UnscheduleThread(Thread* thread,
UnscheduleThreadLocked(&ml, thread, is_mutator, bypass_safepoint);
}
#ifndef PRODUCT
void IsolateGroup::PrintJSON(JSONStream* stream, bool ref) {
if (!FLAG_support_service) {
return;
}
JSONObject jsobj(stream);
PrintToJSONObject(&jsobj, ref);
}
void IsolateGroup::PrintToJSONObject(JSONObject* jsobj, bool ref) {
if (!FLAG_support_service) {
return;
}
jsobj->AddProperty("type", (ref ? "@IsolateGroup" : "IsolateGroup"));
jsobj->AddServiceId(ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, id());
jsobj->AddProperty("name", "isolate_group");
jsobj->AddPropertyF("number", "%" Pu64 "", id());
if (ref) {
return;
}
{
JSONArray isolate_array(jsobj, "isolates");
for (auto it = isolates_.Begin(); it != isolates_.End(); ++it) {
Isolate* isolate = *it;
isolate_array.AddValue(isolate, /*ref=*/true);
}
}
}
void IsolateGroup::PrintMemoryUsageJSON(JSONStream* stream) {
if (!FLAG_support_service) {
return;
}
int64_t used = 0;
int64_t capacity = 0;
int64_t external_used = 0;
for (auto it = isolates_.Begin(); it != isolates_.End(); ++it) {
Isolate* isolate = *it;
used += isolate->heap()->TotalUsedInWords();
capacity += isolate->heap()->TotalCapacityInWords();
external_used += isolate->heap()->TotalExternalInWords();
}
JSONObject jsobj(stream);
// This is the same "MemoryUsage" that the isolate-specific "getMemoryUsage"
// rpc method returns.
// TODO(dartbug.com/36097): Once the heap moves from Isolate to IsolateGroup
// this code needs to be adjusted to not double-count memory.
jsobj.AddProperty("type", "MemoryUsage");
jsobj.AddProperty64("heapUsage", used * kWordSize);
jsobj.AddProperty64("heapCapacity", capacity * kWordSize);
jsobj.AddProperty64("externalUsage", external_used * kWordSize);
}
#endif
void IsolateGroup::ForEach(std::function<void(IsolateGroup*)> action) {
ReadRwLocker wl(Thread::Current(), isolate_groups_rwlock_);
for (auto isolate_group : *isolate_groups_) {
action(isolate_group);
}
}
void IsolateGroup::RunWithIsolateGroup(
uint64_t id,
std::function<void(IsolateGroup*)> action,
std::function<void()> not_found) {
ReadRwLocker wl(Thread::Current(), isolate_groups_rwlock_);
for (auto isolate_group : *isolate_groups_) {
if (isolate_group->id() == id) {
action(isolate_group);
return;
}
}
not_found();
}
void IsolateGroup::RegisterIsolateGroup(IsolateGroup* isolate_group) {
WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
isolate_groups_->Append(isolate_group);
}
void IsolateGroup::UnregisterIsolateGroup(IsolateGroup* isolate_group) {
WriteRwLocker wl(ThreadState::Current(), isolate_groups_rwlock_);
isolate_groups_->Remove(isolate_group);
}
void IsolateGroup::Init() {
ASSERT(isolate_groups_rwlock_ == nullptr);
isolate_groups_rwlock_ = new RwLock();
ASSERT(isolate_groups_ == nullptr);
isolate_groups_ = new IntrusiveDList<IsolateGroup>();
}
void IsolateGroup::Cleanup() {
delete isolate_groups_rwlock_;
isolate_groups_rwlock_ = nullptr;
ASSERT(isolate_groups_->IsEmpty());
delete (isolate_groups_);
isolate_groups_ = nullptr;
}
bool IsolateVisitor::IsVMInternalIsolate(Isolate* isolate) const {
return Isolate::IsVMInternalIsolate(isolate);
}
@ -2124,6 +2229,9 @@ Monitor* Isolate::isolates_list_monitor_ = nullptr;
Isolate* Isolate::isolates_list_head_ = nullptr;
bool Isolate::creation_enabled_ = false;
RwLock* IsolateGroup::isolate_groups_rwlock_ = nullptr;
IntrusiveDList<IsolateGroup>* IsolateGroup::isolate_groups_ = nullptr;
void Isolate::VisitObjectPointers(ObjectPointerVisitor* visitor,
ValidationPolicy validate_frames) {
ASSERT(visitor != nullptr);
@ -2487,6 +2595,11 @@ void Isolate::PrintJSON(JSONStream* stream, bool ref) {
}
jsobj.AddProperty("_threads", thread_registry());
{
JSONObject isolate_group(&jsobj, "isolate_group");
group()->PrintToJSONObject(&isolate_group, /*ref=*/true);
}
}
void Isolate::PrintMemoryUsageJSON(JSONStream* stream) {

View file

@ -220,7 +220,7 @@ class IsolateGroupSource {
};
// Represents an isolate group and is shared among all isolates within a group.
class IsolateGroup {
class IsolateGroup : public IntrusiveDListEntry<IsolateGroup> {
public:
IsolateGroup(std::unique_ptr<IsolateGroupSource> source, void* embedder_data);
~IsolateGroup();
@ -282,6 +282,29 @@ class IsolateGroup {
RunWithStoppedMutators(function, function);
}
#ifndef PRODUCT
void PrintJSON(JSONStream* stream, bool ref = true);
void PrintToJSONObject(JSONObject* jsobj, bool ref);
// Creates an object with the total heap memory usage statistics for this
// isolate group.
void PrintMemoryUsageJSON(JSONStream* stream);
#endif
uint64_t id() { return id_; }
static void Init();
static void Cleanup();
static void ForEach(std::function<void(IsolateGroup*)> action);
static void RunWithIsolateGroup(uint64_t id,
std::function<void(IsolateGroup*)> action,
std::function<void()> not_found);
// Manage list of existing isolate groups.
static void RegisterIsolateGroup(IsolateGroup* isolate_group);
static void UnregisterIsolateGroup(IsolateGroup* isolate_group);
private:
void* embedder_data_ = nullptr;
@ -294,6 +317,12 @@ class IsolateGroup {
std::unique_ptr<IsolateGroupSource> source_;
std::unique_ptr<ThreadRegistry> thread_registry_;
std::unique_ptr<SafepointHandler> safepoint_handler_;
static RwLock* isolate_groups_rwlock_;
static IntrusiveDList<IsolateGroup>* isolate_groups_;
Random isolate_group_random_;
uint64_t id_ = isolate_group_random_.NextUInt64();
};
class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {

View file

@ -346,6 +346,11 @@ void JSONStream::PrintValue(Isolate* isolate, bool ref) {
isolate->PrintJSON(this, ref);
}
void JSONStream::PrintValue(IsolateGroup* isolate_group, bool ref) {
PrintCommaIfNeeded();
isolate_group->PrintJSON(this, ref);
}
void JSONStream::PrintValue(ThreadRegistry* reg) {
PrintCommaIfNeeded();
reg->PrintJSON(this);

View file

@ -203,6 +203,7 @@ class JSONStream : ValueObject {
void PrintValue(Metric* metric);
void PrintValue(MessageQueue* queue);
void PrintValue(Isolate* isolate, bool ref = true);
void PrintValue(IsolateGroup* isolate, bool ref = true);
void PrintValue(ThreadRegistry* reg);
void PrintValue(Thread* thread);
void PrintValue(const TimelineEvent* timeline_event);
@ -438,6 +439,9 @@ class JSONArray : public ValueObject {
void AddValue(Isolate* isolate, bool ref = true) const {
stream_->PrintValue(isolate, ref);
}
void AddValue(IsolateGroup* isolate_group, bool ref = true) const {
stream_->PrintValue(isolate_group, ref);
}
void AddValue(ThreadRegistry* reg) const { stream_->PrintValue(reg); }
void AddValue(Thread* thread) const { stream_->PrintValue(thread); }
void AddValue(Breakpoint* bpt) const { stream_->PrintValue(bpt); }

View file

@ -556,6 +556,34 @@ class Int64Parameter : public MethodParameter {
}
};
class UInt64Parameter : public MethodParameter {
public:
UInt64Parameter(const char* name, bool required)
: MethodParameter(name, required) {}
virtual bool Validate(const char* value) const {
if (value == NULL) {
return false;
}
for (const char* cp = value; *cp != '\0'; cp++) {
if ((*cp < '0' || *cp > '9') && (*cp != '-')) {
return false;
}
}
return true;
}
static uint64_t Parse(const char* value, uint64_t default_value = 0) {
if ((value == NULL) || (*value == '\0')) {
return default_value;
}
char* end_ptr = NULL;
uint64_t result = strtoull(value, &end_ptr, 10);
ASSERT(*end_ptr == '\0'); // Parsed full string
return result;
}
};
class IdParameter : public MethodParameter {
public:
IdParameter(const char* name, bool required)
@ -591,6 +619,7 @@ class RunnableIsolateParameter : public MethodParameter {
};
#define ISOLATE_PARAMETER new IdParameter("isolateId", true)
#define ISOLATE_GROUP_PARAMETER new IdParameter("isolateGroupId", true)
#define NO_ISOLATE_PARAMETER new NoSuchParameter("isolateId")
#define RUNNABLE_ISOLATE_PARAMETER new RunnableIsolateParameter("isolateId")
@ -1370,6 +1399,65 @@ static bool GetIsolate(Thread* thread, JSONStream* js) {
return true;
}
static const MethodParameter* get_isolate_group_params[] = {
ISOLATE_GROUP_PARAMETER,
NULL,
};
enum SentinelType {
kCollectedSentinel,
kExpiredSentinel,
kFreeSentinel,
};
static void PrintSentinel(JSONStream* js, SentinelType sentinel_type) {
JSONObject jsobj(js);
jsobj.AddProperty("type", "Sentinel");
switch (sentinel_type) {
case kCollectedSentinel:
jsobj.AddProperty("kind", "Collected");
jsobj.AddProperty("valueAsString", "<collected>");
break;
case kExpiredSentinel:
jsobj.AddProperty("kind", "Expired");
jsobj.AddProperty("valueAsString", "<expired>");
break;
case kFreeSentinel:
jsobj.AddProperty("kind", "Free");
jsobj.AddProperty("valueAsString", "<free>");
break;
default:
UNIMPLEMENTED();
break;
}
}
static void ActOnIsolateGroup(JSONStream* js,
std::function<void(IsolateGroup*)> visitor) {
const String& prefix =
String::Handle(String::New(ISOLATE_GROUP_SERVICE_ID_PREFIX));
const String& s =
String::Handle(String::New(js->LookupParam("isolateGroupId")));
if (!s.StartsWith(prefix)) {
PrintInvalidParamError(js, "isolateGroupId");
return;
}
uint64_t isolate_group_id = UInt64Parameter::Parse(
String::Handle(String::SubString(s, prefix.Length())).ToCString());
IsolateGroup::RunWithIsolateGroup(
isolate_group_id,
[&visitor](IsolateGroup* isolate_group) { visitor(isolate_group); },
/*if_not_found=*/[&js]() { PrintSentinel(js, kExpiredSentinel); });
}
static bool GetIsolateGroup(Thread* thread, JSONStream* js) {
ActOnIsolateGroup(js, [&](IsolateGroup* isolate_group) {
isolate_group->PrintJSON(js, false);
});
return true;
}
static const MethodParameter* get_memory_usage_params[] = {
ISOLATE_PARAMETER,
NULL,
@ -1380,6 +1468,18 @@ static bool GetMemoryUsage(Thread* thread, JSONStream* js) {
return true;
}
static const MethodParameter* get_isolate_group_memory_usage_params[] = {
ISOLATE_GROUP_PARAMETER,
NULL,
};
static bool GetIsolateGroupMemoryUsage(Thread* thread, JSONStream* js) {
ActOnIsolateGroup(js, [&](IsolateGroup* isolate_group) {
isolate_group->PrintMemoryUsageJSON(js);
});
return true;
}
static const MethodParameter* get_scripts_params[] = {
RUNNABLE_ISOLATE_PARAMETER,
NULL,
@ -1936,34 +2036,6 @@ static RawObject* LookupHeapObject(Thread* thread,
return Object::sentinel().raw();
}
enum SentinelType {
kCollectedSentinel,
kExpiredSentinel,
kFreeSentinel,
};
static void PrintSentinel(JSONStream* js, SentinelType sentinel_type) {
JSONObject jsobj(js);
jsobj.AddProperty("type", "Sentinel");
switch (sentinel_type) {
case kCollectedSentinel:
jsobj.AddProperty("kind", "Collected");
jsobj.AddProperty("valueAsString", "<collected>");
break;
case kExpiredSentinel:
jsobj.AddProperty("kind", "Expired");
jsobj.AddProperty("valueAsString", "<expired>");
break;
case kFreeSentinel:
jsobj.AddProperty("kind", "Free");
jsobj.AddProperty("valueAsString", "<free>");
break;
default:
UNIMPLEMENTED();
break;
}
}
static Breakpoint* LookupBreakpoint(Isolate* isolate,
const char* id,
ObjectIdRing::LookupResult* result) {
@ -4336,12 +4408,18 @@ void Service::PrintJSONForVM(JSONStream* js, bool ref) {
"startTime", OS::GetCurrentTimeMillis() - Dart::UptimeMillis());
MallocHooks::PrintToJSONObject(&jsobj);
PrintJSONForEmbedderInformation(&jsobj);
// Construct the isolate list.
// Construct the isolate and isolate_groups list.
{
JSONArray jsarr(&jsobj, "isolates");
ServiceIsolateVisitor visitor(&jsarr);
Isolate::VisitIsolates(&visitor);
}
{
JSONArray jsarr_isolate_groups(&jsobj, "isolateGroups");
IsolateGroup::ForEach([&jsarr_isolate_groups](IsolateGroup* isolate_group) {
jsarr_isolate_groups.AddValue(isolate_group);
});
}
}
static bool GetVM(Thread* thread, JSONStream* js) {
@ -4687,8 +4765,12 @@ static const ServiceMethodDescriptor service_methods_[] = {
get_instances_params },
{ "getIsolate", GetIsolate,
get_isolate_params },
{ "getIsolateGroup", GetIsolateGroup,
get_isolate_group_params },
{ "getMemoryUsage", GetMemoryUsage,
get_memory_usage_params },
{ "getIsolateGroupMemoryUsage", GetIsolateGroupMemoryUsage,
get_isolate_group_memory_usage_params },
{ "_getIsolateMetric", GetIsolateMetric,
get_isolate_metric_params },
{ "_getIsolateMetricList", GetIsolateMetricList,

View file

@ -15,7 +15,7 @@
namespace dart {
#define SERVICE_PROTOCOL_MAJOR_VERSION 3
#define SERVICE_PROTOCOL_MINOR_VERSION 27
#define SERVICE_PROTOCOL_MINOR_VERSION 28
class Array;
class EmbedderServiceHandler;
@ -24,6 +24,7 @@ class GCEvent;
class GrowableObjectArray;
class Instance;
class Isolate;
class IsolateGroup;
class JSONStream;
class JSONObject;
class Object;
@ -44,6 +45,9 @@ class ServiceIdZone {
};
#define ISOLATE_SERVICE_ID_FORMAT_STRING "isolates/%" Pd64 ""
#define ISOLATE_GROUP_SERVICE_ID_PREFIX "isolateGroups/"
#define ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING \
ISOLATE_GROUP_SERVICE_ID_PREFIX "%" Pu64 ""
class RingServiceIdZone : public ServiceIdZone {
public:

View file

@ -37,6 +37,7 @@ The Service Protocol uses [JSON-RPC 2.0][].
- [getInstances](#getinstances)
- [getInboundReferences](#getinboundreferences)
- [getIsolate](#getisolate)
- [getIsolateGroup](#getisolategroup)
- [getMemoryUsage](#getmemoryusage)
- [getObject](#getobject)
- [getRetainingPath](#getretainingpath)
@ -92,6 +93,7 @@ The Service Protocol uses [JSON-RPC 2.0][].
- [Instance](#instance)
- [InstanceSet](#instanceset)
- [Isolate](#isolate)
- [IsolateGroup](#isolategroup)
- [Library](#library)
- [LibraryDependency](#librarydependency)
- [LogRecord](#logrecord)
@ -760,6 +762,22 @@ _Collected_ [Sentinel](#sentinel) is returned.
See [Isolate](#isolate).
### getIsolateGroup
```
IsolateGroup|Sentinel getIsolateGroup(string isolateGroupId)
```
The _getIsolateGroup_ RPC is used to lookup an _IsolateGroup_ object by its _id_.
If _isolateGroupId_ refers to an isolate group which has exited, then the
_Expired_ [Sentinel](#sentinel) is returned.
_IsolateGroup_ _id_ is an opaque identifier that can be fetched from an
_IsolateGroup_. List of active _IsolateGroup_'s, for example, is available on _VM_ object.
See [IsolateGroup](#isolategroup), [VM](#vm).
### getMemoryUsage
```
@ -774,6 +792,19 @@ _Collected_ [Sentinel](#sentinel) is returned.
See [Isolate](#isolate).
### getIsolateGroupMemoryUsage
```
MemoryUsage|Sentinel getIsolateGroupMemoryUsage(string isolateGroupId)
```
The _getIsolateGroupMemoryUsage_ RPC is used to lookup an isolate
group's memory usage statistics by its _id_.
If _isolateGroupId_ refers to an isolate group which has exited, then the _Expired_ [Sentinel](#sentinel) is returned.
See [IsolateGroup](#isolategroup).
### getScripts
```
@ -2598,6 +2629,42 @@ class Isolate extends Response {
An _Isolate_ object provides information about one isolate in the VM.
### IsolateGroup
```
class @IsolateGroup extends Response {
// The id which is passed to the getIsolateGroup RPC to load this isolate group.
string id;
// A numeric id for this isolate group, represented as a string. Unique.
string number;
// A name identifying this isolate group. Not guaranteed to be unique.
string name;
}
```
_@IsolateGroup_ is a reference to an _IsolateGroup_ object.
```
class IsolateGroup extends Response {
// The id which is passed to the getIsolate RPC to reload this
// isolate.
string id;
// A numeric id for this isolate, represented as a string. Unique.
string number;
// A name identifying this isolate. Not guaranteed to be unique.
string name;
// A list of all isolates in this isolate group.
@Isolate[] isolates;
}
```
An _Isolate_ object provides information about one isolate in the VM.
### InboundReferences
```
@ -3438,6 +3505,9 @@ class VM extends Response {
// A list of isolates running in the VM.
@Isolate[] isolates;
// A list of isolate groups running in the VM.
@IsolateGroup[] isolateGroups;
}
```