mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 23:59:47 +00:00
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:
parent
8e7fa2a35b
commit
9e636b5ab4
|
@ -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;
|
||||
}
|
||||
|
|
4
pkg/vm_service/java/.gitignore
vendored
4
pkg/vm_service/java/.gitignore
vendored
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
35
pkg/vm_service/test/get_isolate_group_memory_usage.dart
Normal file
35
pkg/vm_service/test/get_isolate_group_memory_usage.dart
Normal 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);
|
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
22
runtime/observatory/lib/src/repositories/isolate_group.dart
Normal file
22
runtime/observatory/lib/src/repositories/isolate_group.dart
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
|
@ -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));
|
||||
},
|
||||
|
|
|
@ -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'));
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
];
|
|
@ -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);
|
||||
}
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in a new issue