mirror of
https://github.com/dart-lang/sdk
synced 2024-09-18 20:11:19 +00:00
Add a new getIsolatePauseEvent method in the VM service.
In Flutter hot reload, the flutter_tools are calling the `getIsolate` to read the pause state of the isolate, which returns a full list of libraries loaded in the isolate. The size of the return object grow linearly to the number of Dart files, which can take ~500ms to transfer for a large app. This change adds a new method to only read what is needed. TEST=pkg/vm_service/test/get_isolate_pause_event_rpc_test.dart Change-Id: If19910cb3ff5d5057932551ac738afd3c3136fac Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/305362 Reviewed-by: Ben Konyi <bkonyi@google.com> Auto-Submit: Chingjun Lau <chingjun@google.com> Commit-Queue: Derek Xu <derekx@google.com>
This commit is contained in:
parent
c1243af736
commit
9304a8dfd1
1
pkg/vm_service/java/.gitignore
vendored
1
pkg/vm_service/java/.gitignore
vendored
|
@ -19,6 +19,7 @@ src/org/dartlang/vm/service/consumer/GetInstancesConsumer.java
|
||||||
src/org/dartlang/vm/service/consumer/GetIsolateConsumer.java
|
src/org/dartlang/vm/service/consumer/GetIsolateConsumer.java
|
||||||
src/org/dartlang/vm/service/consumer/GetIsolateGroupConsumer.java
|
src/org/dartlang/vm/service/consumer/GetIsolateGroupConsumer.java
|
||||||
src/org/dartlang/vm/service/consumer/GetIsolateGroupMemoryUsageConsumer.java
|
src/org/dartlang/vm/service/consumer/GetIsolateGroupMemoryUsageConsumer.java
|
||||||
|
src/org/dartlang/vm/service/consumer/GetIsolatePauseEventConsumer.java
|
||||||
src/org/dartlang/vm/service/consumer/GetMemoryUsageConsumer.java
|
src/org/dartlang/vm/service/consumer/GetMemoryUsageConsumer.java
|
||||||
src/org/dartlang/vm/service/consumer/GetObjectConsumer.java
|
src/org/dartlang/vm/service/consumer/GetObjectConsumer.java
|
||||||
src/org/dartlang/vm/service/consumer/GetPerfettoCpuSamplesConsumer.java
|
src/org/dartlang/vm/service/consumer/GetPerfettoCpuSamplesConsumer.java
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
version=4.7
|
version=4.8
|
||||||
|
|
|
@ -28,7 +28,7 @@ export 'snapshot_graph.dart'
|
||||||
HeapSnapshotObjectNoData,
|
HeapSnapshotObjectNoData,
|
||||||
HeapSnapshotObjectNullData;
|
HeapSnapshotObjectNullData;
|
||||||
|
|
||||||
const String vmServiceVersion = '4.7.0';
|
const String vmServiceVersion = '4.8.0';
|
||||||
|
|
||||||
/// @optional
|
/// @optional
|
||||||
const String optional = 'optional';
|
const String optional = 'optional';
|
||||||
|
@ -216,6 +216,7 @@ Map<String, List<String>> _methodReturnTypes = {
|
||||||
'getInstancesAsList': const ['InstanceRef'],
|
'getInstancesAsList': const ['InstanceRef'],
|
||||||
'getIsolate': const ['Isolate'],
|
'getIsolate': const ['Isolate'],
|
||||||
'getIsolateGroup': const ['IsolateGroup'],
|
'getIsolateGroup': const ['IsolateGroup'],
|
||||||
|
'getIsolatePauseEvent': const ['Event'],
|
||||||
'getMemoryUsage': const ['MemoryUsage'],
|
'getMemoryUsage': const ['MemoryUsage'],
|
||||||
'getIsolateGroupMemoryUsage': const ['MemoryUsage'],
|
'getIsolateGroupMemoryUsage': const ['MemoryUsage'],
|
||||||
'getScripts': const ['ScriptList'],
|
'getScripts': const ['ScriptList'],
|
||||||
|
@ -719,6 +720,18 @@ abstract class VmServiceInterface {
|
||||||
/// returned.
|
/// returned.
|
||||||
Future<IsolateGroup> getIsolateGroup(String isolateGroupId);
|
Future<IsolateGroup> getIsolateGroup(String isolateGroupId);
|
||||||
|
|
||||||
|
/// The `getIsolatePauseEvent` RPC is used to lookup an isolate's pause event
|
||||||
|
/// by its `id`.
|
||||||
|
///
|
||||||
|
/// If `isolateId` refers to an isolate which has exited, then the `Collected`
|
||||||
|
/// [Sentinel] is returned.
|
||||||
|
///
|
||||||
|
/// See [Isolate].
|
||||||
|
///
|
||||||
|
/// This method will throw a [SentinelException] in the case a [Sentinel] is
|
||||||
|
/// returned.
|
||||||
|
Future<Event> getIsolatePauseEvent(String isolateId);
|
||||||
|
|
||||||
/// The `getMemoryUsage` RPC is used to lookup an isolate's memory usage
|
/// The `getMemoryUsage` RPC is used to lookup an isolate's memory usage
|
||||||
/// statistics by its `id`.
|
/// statistics by its `id`.
|
||||||
///
|
///
|
||||||
|
@ -1609,6 +1622,11 @@ class VmServerConnection {
|
||||||
params!['isolateGroupId'],
|
params!['isolateGroupId'],
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case 'getIsolatePauseEvent':
|
||||||
|
response = await _serviceImplementation.getIsolatePauseEvent(
|
||||||
|
params!['isolateId'],
|
||||||
|
);
|
||||||
|
break;
|
||||||
case 'getMemoryUsage':
|
case 'getMemoryUsage':
|
||||||
response = await _serviceImplementation.getMemoryUsage(
|
response = await _serviceImplementation.getMemoryUsage(
|
||||||
params!['isolateId'],
|
params!['isolateId'],
|
||||||
|
@ -2181,6 +2199,10 @@ class VmService implements VmServiceInterface {
|
||||||
Future<IsolateGroup> getIsolateGroup(String isolateGroupId) =>
|
Future<IsolateGroup> getIsolateGroup(String isolateGroupId) =>
|
||||||
_call('getIsolateGroup', {'isolateGroupId': isolateGroupId});
|
_call('getIsolateGroup', {'isolateGroupId': isolateGroupId});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Event> getIsolatePauseEvent(String isolateId) =>
|
||||||
|
_call('getIsolatePauseEvent', {'isolateId': isolateId});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<MemoryUsage> getMemoryUsage(String isolateId) =>
|
Future<MemoryUsage> getMemoryUsage(String isolateId) =>
|
||||||
_call('getMemoryUsage', {'isolateId': isolateId});
|
_call('getMemoryUsage', {'isolateId': isolateId});
|
||||||
|
|
40
pkg/vm_service/test/get_isolate_pause_event_rpc_test.dart
Normal file
40
pkg/vm_service/test/get_isolate_pause_event_rpc_test.dart
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright (c) 2023, 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.getIsolatePauseEvent(vm.isolates!.first.id!);
|
||||||
|
expect(result.type, 'Event');
|
||||||
|
expect(result.kind, isNotNull);
|
||||||
|
},
|
||||||
|
// Plausible isolate id, not found.
|
||||||
|
(VmService service) async {
|
||||||
|
try {
|
||||||
|
await service.getIsolatePauseEvent('isolates/9999999999');
|
||||||
|
fail('successfully got isolate with bad ID');
|
||||||
|
} on SentinelException catch (e) {
|
||||||
|
expect(e.sentinel.kind, 'Collected');
|
||||||
|
expect(e.sentinel.valueAsString, '<collected>');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Verify that the returned event is the same as returned from getIsolate()
|
||||||
|
(VmService service) async {
|
||||||
|
final vm = await service.getVM();
|
||||||
|
final result = await service.getIsolatePauseEvent(vm.isolates!.first.id!);
|
||||||
|
final isolate = await service.getIsolate(vm.isolates!.first.id!);
|
||||||
|
expect(result.toJson(), isolate.pauseEvent?.toJson());
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
main(args) async => runVMTests(
|
||||||
|
args,
|
||||||
|
tests,
|
||||||
|
'get_isolate_pause_event_rpc_test.dart',
|
||||||
|
);
|
|
@ -12,7 +12,7 @@ var tests = <VMTest>[
|
||||||
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
|
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
|
||||||
expect(result['type'], 'Version');
|
expect(result['type'], 'Version');
|
||||||
expect(result['major'], 4);
|
expect(result['major'], 4);
|
||||||
expect(result['minor'], 7);
|
expect(result['minor'], 8);
|
||||||
expect(result['_privateMajor'], 0);
|
expect(result['_privateMajor'], 0);
|
||||||
expect(result['_privateMinor'], 0);
|
expect(result['_privateMinor'], 0);
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,7 @@ var tests = <VMTest>[
|
||||||
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
|
final result = await vm.invokeRpcNoUpgrade('getVersion', {});
|
||||||
expect(result['type'], equals('Version'));
|
expect(result['type'], equals('Version'));
|
||||||
expect(result['major'], equals(4));
|
expect(result['major'], equals(4));
|
||||||
expect(result['minor'], equals(7));
|
expect(result['minor'], equals(8));
|
||||||
expect(result['_privateMajor'], equals(0));
|
expect(result['_privateMajor'], equals(0));
|
||||||
expect(result['_privateMinor'], equals(0));
|
expect(result['_privateMinor'], equals(0));
|
||||||
},
|
},
|
||||||
|
|
|
@ -2836,6 +2836,51 @@ static const char* ExceptionPauseInfoToServiceEnum(Dart_ExceptionPauseInfo pi) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ServiceEvent IsolatePauseEvent(Isolate* isolate) {
|
||||||
|
if (!isolate->is_runnable()) {
|
||||||
|
// Isolate is not yet runnable.
|
||||||
|
ASSERT((isolate->debugger() == nullptr) ||
|
||||||
|
(isolate->debugger()->PauseEvent() == nullptr));
|
||||||
|
return ServiceEvent(isolate, ServiceEvent::kNone);
|
||||||
|
} else if (isolate->message_handler()->should_pause_on_start()) {
|
||||||
|
if (isolate->message_handler()->is_paused_on_start()) {
|
||||||
|
ASSERT((isolate->debugger() == nullptr) ||
|
||||||
|
(isolate->debugger()->PauseEvent() == nullptr));
|
||||||
|
return ServiceEvent(isolate, ServiceEvent::kPauseStart);
|
||||||
|
} else {
|
||||||
|
// Isolate is runnable but not paused on start.
|
||||||
|
// Some service clients get confused if they see:
|
||||||
|
// NotRunnable -> Runnable -> PausedAtStart
|
||||||
|
// Treat Runnable+ShouldPauseOnStart as NotRunnable so they see:
|
||||||
|
// NonRunnable -> PausedAtStart
|
||||||
|
// The should_pause_on_start flag is set to false after resume.
|
||||||
|
ASSERT((isolate->debugger() == nullptr) ||
|
||||||
|
(isolate->debugger()->PauseEvent() == nullptr));
|
||||||
|
return ServiceEvent(isolate, ServiceEvent::kNone);
|
||||||
|
}
|
||||||
|
} else if (isolate->message_handler()->is_paused_on_exit() &&
|
||||||
|
((isolate->debugger() == nullptr) ||
|
||||||
|
(isolate->debugger()->PauseEvent() == nullptr))) {
|
||||||
|
return ServiceEvent(isolate, ServiceEvent::kPauseExit);
|
||||||
|
} else if ((isolate->debugger() != nullptr) &&
|
||||||
|
(isolate->debugger()->PauseEvent() != nullptr) &&
|
||||||
|
!isolate->ResumeRequest()) {
|
||||||
|
return *(isolate->debugger()->PauseEvent());
|
||||||
|
} else {
|
||||||
|
ServiceEvent pause_event(isolate, ServiceEvent::kResume);
|
||||||
|
|
||||||
|
if (isolate->debugger() != nullptr) {
|
||||||
|
// TODO(turnidge): Don't compute a full stack trace.
|
||||||
|
DebuggerStackTrace* stack = isolate->debugger()->StackTrace();
|
||||||
|
if (stack->Length() > 0) {
|
||||||
|
pause_event.set_top_frame(stack->FrameAt(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pause_event;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Isolate::PrintJSON(JSONStream* stream, bool ref) {
|
void Isolate::PrintJSON(JSONStream* stream, bool ref) {
|
||||||
JSONObject jsobj(stream);
|
JSONObject jsobj(stream);
|
||||||
jsobj.AddProperty("type", (ref ? "@Isolate" : "Isolate"));
|
jsobj.AddProperty("type", (ref ? "@Isolate" : "Isolate"));
|
||||||
|
@ -2887,47 +2932,8 @@ void Isolate::PrintJSON(JSONStream* stream, bool ref) {
|
||||||
jsobj.AddProperty("_isReloading", group()->IsReloading());
|
jsobj.AddProperty("_isReloading", group()->IsReloading());
|
||||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||||
|
|
||||||
if (!is_runnable()) {
|
ServiceEvent pause_event = IsolatePauseEvent(this);
|
||||||
// Isolate is not yet runnable.
|
jsobj.AddProperty("pauseEvent", &pause_event);
|
||||||
ASSERT((debugger() == nullptr) || (debugger()->PauseEvent() == nullptr));
|
|
||||||
ServiceEvent pause_event(this, ServiceEvent::kNone);
|
|
||||||
jsobj.AddProperty("pauseEvent", &pause_event);
|
|
||||||
} else if (message_handler()->should_pause_on_start()) {
|
|
||||||
if (message_handler()->is_paused_on_start()) {
|
|
||||||
ASSERT((debugger() == nullptr) || (debugger()->PauseEvent() == nullptr));
|
|
||||||
ServiceEvent pause_event(this, ServiceEvent::kPauseStart);
|
|
||||||
jsobj.AddProperty("pauseEvent", &pause_event);
|
|
||||||
} else {
|
|
||||||
// Isolate is runnable but not paused on start.
|
|
||||||
// Some service clients get confused if they see:
|
|
||||||
// NotRunnable -> Runnable -> PausedAtStart
|
|
||||||
// Treat Runnable+ShouldPauseOnStart as NotRunnable so they see:
|
|
||||||
// NonRunnable -> PausedAtStart
|
|
||||||
// The should_pause_on_start flag is set to false after resume.
|
|
||||||
ASSERT((debugger() == nullptr) || (debugger()->PauseEvent() == nullptr));
|
|
||||||
ServiceEvent pause_event(this, ServiceEvent::kNone);
|
|
||||||
jsobj.AddProperty("pauseEvent", &pause_event);
|
|
||||||
}
|
|
||||||
} else if (message_handler()->is_paused_on_exit() &&
|
|
||||||
((debugger() == nullptr) ||
|
|
||||||
(debugger()->PauseEvent() == nullptr))) {
|
|
||||||
ServiceEvent pause_event(this, ServiceEvent::kPauseExit);
|
|
||||||
jsobj.AddProperty("pauseEvent", &pause_event);
|
|
||||||
} else if ((debugger() != nullptr) && (debugger()->PauseEvent() != nullptr) &&
|
|
||||||
!ResumeRequest()) {
|
|
||||||
jsobj.AddProperty("pauseEvent", debugger()->PauseEvent());
|
|
||||||
} else {
|
|
||||||
ServiceEvent pause_event(this, ServiceEvent::kResume);
|
|
||||||
|
|
||||||
if (debugger() != nullptr) {
|
|
||||||
// TODO(turnidge): Don't compute a full stack trace.
|
|
||||||
DebuggerStackTrace* stack = debugger()->StackTrace();
|
|
||||||
if (stack->Length() > 0) {
|
|
||||||
pause_event.set_top_frame(stack->FrameAt(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
jsobj.AddProperty("pauseEvent", &pause_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Library& lib = Library::Handle(group()->object_store()->root_library());
|
const Library& lib = Library::Handle(group()->object_store()->root_library());
|
||||||
if (!lib.IsNull()) {
|
if (!lib.IsNull()) {
|
||||||
|
@ -3003,6 +3009,10 @@ void Isolate::PrintMemoryUsageJSON(JSONStream* stream) {
|
||||||
group()->heap()->PrintMemoryUsageJSON(stream);
|
group()->heap()->PrintMemoryUsageJSON(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Isolate::PrintPauseEventJSON(JSONStream* stream) {
|
||||||
|
IsolatePauseEvent(this).PrintJSON(stream);
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Isolate::set_tag_table(const GrowableObjectArray& value) {
|
void Isolate::set_tag_table(const GrowableObjectArray& value) {
|
||||||
|
|
|
@ -1268,6 +1268,8 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
||||||
// Creates an object with the total heap memory usage statistics for this
|
// Creates an object with the total heap memory usage statistics for this
|
||||||
// isolate.
|
// isolate.
|
||||||
void PrintMemoryUsageJSON(JSONStream* stream);
|
void PrintMemoryUsageJSON(JSONStream* stream);
|
||||||
|
|
||||||
|
void PrintPauseEventJSON(JSONStream* stream);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(PRODUCT)
|
#if !defined(PRODUCT)
|
||||||
|
|
|
@ -1623,6 +1623,15 @@ static void GetIsolateGroupMemoryUsage(Thread* thread, JSONStream* js) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const MethodParameter* const get_isolate_pause_event_params[] = {
|
||||||
|
ISOLATE_PARAMETER,
|
||||||
|
nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void GetIsolatePauseEvent(Thread* thread, JSONStream* js) {
|
||||||
|
thread->isolate()->PrintPauseEventJSON(js);
|
||||||
|
}
|
||||||
|
|
||||||
static const MethodParameter* const get_scripts_params[] = {
|
static const MethodParameter* const get_scripts_params[] = {
|
||||||
RUNNABLE_ISOLATE_PARAMETER,
|
RUNNABLE_ISOLATE_PARAMETER,
|
||||||
nullptr,
|
nullptr,
|
||||||
|
@ -5821,6 +5830,8 @@ static const ServiceMethodDescriptor service_methods_[] = {
|
||||||
get_isolate_metric_params },
|
get_isolate_metric_params },
|
||||||
{ "_getIsolateMetricList", GetIsolateMetricList,
|
{ "_getIsolateMetricList", GetIsolateMetricList,
|
||||||
get_isolate_metric_list_params },
|
get_isolate_metric_list_params },
|
||||||
|
{ "getIsolatePauseEvent", GetIsolatePauseEvent,
|
||||||
|
get_isolate_pause_event_params },
|
||||||
{ "getObject", GetObject,
|
{ "getObject", GetObject,
|
||||||
get_object_params },
|
get_object_params },
|
||||||
{ "_getObjectStore", GetObjectStore,
|
{ "_getObjectStore", GetObjectStore,
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
namespace dart {
|
namespace dart {
|
||||||
|
|
||||||
#define SERVICE_PROTOCOL_MAJOR_VERSION 4
|
#define SERVICE_PROTOCOL_MAJOR_VERSION 4
|
||||||
#define SERVICE_PROTOCOL_MINOR_VERSION 7
|
#define SERVICE_PROTOCOL_MINOR_VERSION 8
|
||||||
|
|
||||||
class Array;
|
class Array;
|
||||||
class EmbedderServiceHandler;
|
class EmbedderServiceHandler;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
# Dart VM Service Protocol 4.7
|
# Dart VM Service Protocol 4.8
|
||||||
|
|
||||||
> Please post feedback to the [observatory-discuss group][discuss-list]
|
> Please post feedback to the [observatory-discuss group][discuss-list]
|
||||||
|
|
||||||
This document describes of _version 4.7_ of the Dart VM Service Protocol. This
|
This document describes of _version 4.8_ of the Dart VM Service Protocol. This
|
||||||
protocol is used to communicate with a running Dart Virtual Machine.
|
protocol is used to communicate with a running Dart Virtual Machine.
|
||||||
|
|
||||||
To use the Service Protocol, start the VM with the *--observe* flag.
|
To use the Service Protocol, start the VM with the *--observe* flag.
|
||||||
|
@ -49,6 +49,7 @@ The Service Protocol uses [JSON-RPC 2.0][].
|
||||||
- [getIsolate](#getisolate)
|
- [getIsolate](#getisolate)
|
||||||
- [getIsolateGroup](#getisolategroup)
|
- [getIsolateGroup](#getisolategroup)
|
||||||
- [getMemoryUsage](#getmemoryusage)
|
- [getMemoryUsage](#getmemoryusage)
|
||||||
|
- [getIsolatePauseEvent](#getisolatePauseEvent)
|
||||||
- [getObject](#getobject)
|
- [getObject](#getobject)
|
||||||
- [getPerfettoCpuSamples](#getperfettocpusamples)
|
- [getPerfettoCpuSamples](#getperfettocpusamples)
|
||||||
- [getPerfettoVMTimeline](#getperfettovmtimeline)
|
- [getPerfettoVMTimeline](#getperfettovmtimeline)
|
||||||
|
@ -953,6 +954,20 @@ _IsolateGroup_ _id_ is an opaque identifier that can be fetched from an
|
||||||
|
|
||||||
See [IsolateGroup](#isolategroup), [VM](#vm).
|
See [IsolateGroup](#isolategroup), [VM](#vm).
|
||||||
|
|
||||||
|
### getIsolatePauseEvent
|
||||||
|
|
||||||
|
```
|
||||||
|
Event|Sentinel getIsolatePauseEvent(string isolateId)
|
||||||
|
```
|
||||||
|
|
||||||
|
The _getIsolatePauseEvent_ RPC is used to lookup an isolate's pause event by its
|
||||||
|
_id_.
|
||||||
|
|
||||||
|
If _isolateId_ refers to an isolate which has exited, then the
|
||||||
|
_Collected_ [Sentinel](#sentinel) is returned.
|
||||||
|
|
||||||
|
See [Isolate](#isolate).
|
||||||
|
|
||||||
### getMemoryUsage
|
### getMemoryUsage
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -4676,5 +4691,6 @@ version | comments
|
||||||
4.5 | Added `getPerfettoVMTimeline` RPC.
|
4.5 | Added `getPerfettoVMTimeline` RPC.
|
||||||
4.6 | Added `getPerfettoCpuSamples` RPC. Added a deprecation notice to `InstanceKind.TypeRef`.
|
4.6 | Added `getPerfettoCpuSamples` RPC. Added a deprecation notice to `InstanceKind.TypeRef`.
|
||||||
4.7 | Added a deprecation notice to `Stack.awaiterFrames` field. Added a deprecation notice to `FrameKind.AsyncActivation`.
|
4.7 | Added a deprecation notice to `Stack.awaiterFrames` field. Added a deprecation notice to `FrameKind.AsyncActivation`.
|
||||||
|
4.8 | Added `getIsolatePauseEvent` RPC.
|
||||||
|
|
||||||
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
|
[discuss-list]: https://groups.google.com/a/dartlang.org/forum/#!forum/observatory-discuss
|
||||||
|
|
Loading…
Reference in a new issue