mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
Reland "[ package:dds ] Add support for caching CPU samples based on UserTag"
This reverts commit ada4278fd5
.
TEST=pkg/dds/test/get_cached_cpu_samples_test.dart
Change-Id: I771410c8647fc1eb721c5aeb325c7a595430435c
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/209120
Commit-Queue: Ben Konyi <bkonyi@google.com>
Reviewed-by: Siva Annamalai <asiva@google.com>
This commit is contained in:
parent
3aab52f498
commit
c7fe9ada2f
|
@ -2,6 +2,9 @@
|
||||||
- Fix another possibility of `LateInitializationError` being thrown when trying to
|
- Fix another possibility of `LateInitializationError` being thrown when trying to
|
||||||
cleanup after an error during initialization.
|
cleanup after an error during initialization.
|
||||||
|
|
||||||
|
# 2.1.0
|
||||||
|
- Added getAvailableCachedCpuSamples and getCachedCpuSamples.
|
||||||
|
|
||||||
# 2.0.2
|
# 2.0.2
|
||||||
- Fix possibility of `LateInitializationError` being thrown when trying to
|
- Fix possibility of `LateInitializationError` being thrown when trying to
|
||||||
cleanup after an error during initialization.
|
cleanup after an error during initialization.
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Dart Development Service Protocol 1.2
|
# Dart Development Service Protocol 1.3
|
||||||
|
|
||||||
This document describes _version 1.2_ of the Dart Development Service Protocol.
|
This document describes _version 1.3_ of the Dart Development Service Protocol.
|
||||||
This protocol is an extension of the Dart VM Service Protocol and implements it
|
This protocol is an extension of the Dart VM Service Protocol and implements it
|
||||||
in it's entirety. For details on the VM Service Protocol, see the [Dart VM Service Protocol Specification][service-protocol].
|
in it's entirety. For details on the VM Service Protocol, see the [Dart VM Service Protocol Specification][service-protocol].
|
||||||
|
|
||||||
|
@ -67,6 +67,29 @@ event being sent to the subscribing client for each existing service extension.
|
||||||
|
|
||||||
The DDS Protocol supports all [public RPCs defined in the VM Service protocol][service-protocol-public-rpcs].
|
The DDS Protocol supports all [public RPCs defined in the VM Service protocol][service-protocol-public-rpcs].
|
||||||
|
|
||||||
|
### getAvailableCachedCpuSamples
|
||||||
|
|
||||||
|
```
|
||||||
|
AvailableCachedCpuSamples getAvailableCachedCpuSamples();
|
||||||
|
```
|
||||||
|
|
||||||
|
The _getAvailableCachedCpuSamples_ RPC is used to determine which caches of CPU samples
|
||||||
|
are available. Caches are associated with individual _UserTag_ names and are specified
|
||||||
|
when DDS is started via the _cachedUserTags_ parameter.
|
||||||
|
|
||||||
|
See [AvailableCachedCpuSamples](#availablecachedcpusamples).
|
||||||
|
|
||||||
|
### getCachedCpuSamples
|
||||||
|
|
||||||
|
```
|
||||||
|
CachedCpuSamples getCachedCpuSamples(string isolateId, string userTag);
|
||||||
|
```
|
||||||
|
|
||||||
|
The _getCachedCpuSamples_ RPC is used to retrieve a cache of CPU samples collected
|
||||||
|
under a _UserTag_ with name _userTag_.
|
||||||
|
|
||||||
|
See [CachedCpuSamples](#cachedcpusamples).
|
||||||
|
|
||||||
### getClientName
|
### getClientName
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -181,6 +204,37 @@ See [Success](#success).
|
||||||
|
|
||||||
The DDS Protocol supports all [public types defined in the VM Service protocol][service-protocol-public-types].
|
The DDS Protocol supports all [public types defined in the VM Service protocol][service-protocol-public-types].
|
||||||
|
|
||||||
|
### AvailableCachedCpuSamples
|
||||||
|
|
||||||
|
```
|
||||||
|
class AvailableCachedCpuSamples extends Response {
|
||||||
|
// A list of UserTag names associated with CPU sample caches.
|
||||||
|
string[] cacheNames;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
A collection of [UserTag] names associated with caches of CPU samples.
|
||||||
|
|
||||||
|
See [getAvailableCachedCpuSamples](#getavailablecachedcpusamples).
|
||||||
|
|
||||||
|
### CachedCpuSamples
|
||||||
|
|
||||||
|
```
|
||||||
|
class CachedCpuSamples extends CpuSamples {
|
||||||
|
// The name of the UserTag associated with this cache of samples.
|
||||||
|
string userTag;
|
||||||
|
|
||||||
|
// Provided if the CPU sample cache has filled and older samples have been
|
||||||
|
// dropped.
|
||||||
|
bool truncated [optional];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
An extension of [CpuSamples](#cpu-samples) which represents a set of cached
|
||||||
|
samples, associated with a particular [UserTag] name.
|
||||||
|
|
||||||
|
See [getCachedCpuSamples](#getcachedcpusamples).
|
||||||
|
|
||||||
### ClientName
|
### ClientName
|
||||||
|
|
||||||
```
|
```
|
||||||
|
@ -220,10 +274,12 @@ version | comments
|
||||||
1.0 | Initial revision
|
1.0 | Initial revision
|
||||||
1.1 | Added `getDartDevelopmentServiceVersion` RPC.
|
1.1 | Added `getDartDevelopmentServiceVersion` RPC.
|
||||||
1.2 | Added `getStreamHistory` RPC.
|
1.2 | Added `getStreamHistory` RPC.
|
||||||
|
1.3 | Added `getAvailableCachedCpuSamples` and `getCachedCpuSamples` RPCs.
|
||||||
|
|
||||||
[resume]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#resume
|
[resume]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#resume
|
||||||
[success]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#success
|
[success]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#success
|
||||||
[version]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#version
|
[version]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#version
|
||||||
|
[cpu-samples]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#cpusamples
|
||||||
|
|
||||||
[service-protocol]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md
|
[service-protocol]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md
|
||||||
[service-protocol-rpcs-requests-and-responses]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#rpcs-requests-and-responses
|
[service-protocol-rpcs-requests-and-responses]: https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#rpcs-requests-and-responses
|
||||||
|
|
|
@ -42,6 +42,7 @@ abstract class DartDevelopmentService {
|
||||||
Uri? serviceUri,
|
Uri? serviceUri,
|
||||||
bool enableAuthCodes = true,
|
bool enableAuthCodes = true,
|
||||||
bool ipv6 = false,
|
bool ipv6 = false,
|
||||||
|
List<String> cachedUserTags = const [],
|
||||||
DevToolsConfiguration? devToolsConfiguration,
|
DevToolsConfiguration? devToolsConfiguration,
|
||||||
bool logRequests = false,
|
bool logRequests = false,
|
||||||
}) async {
|
}) async {
|
||||||
|
@ -79,6 +80,7 @@ abstract class DartDevelopmentService {
|
||||||
remoteVmServiceUri,
|
remoteVmServiceUri,
|
||||||
serviceUri,
|
serviceUri,
|
||||||
enableAuthCodes,
|
enableAuthCodes,
|
||||||
|
cachedUserTags,
|
||||||
ipv6,
|
ipv6,
|
||||||
devToolsConfiguration,
|
devToolsConfiguration,
|
||||||
logRequests,
|
logRequests,
|
||||||
|
@ -136,9 +138,13 @@ abstract class DartDevelopmentService {
|
||||||
/// requests.
|
/// requests.
|
||||||
bool get isRunning;
|
bool get isRunning;
|
||||||
|
|
||||||
|
/// The list of [UserTag]s used to determine which CPU samples are cached by
|
||||||
|
/// DDS.
|
||||||
|
List<String> get cachedUserTags;
|
||||||
|
|
||||||
/// The version of the DDS protocol supported by this [DartDevelopmentService]
|
/// The version of the DDS protocol supported by this [DartDevelopmentService]
|
||||||
/// instance.
|
/// instance.
|
||||||
static const String protocolVersion = '1.2';
|
static const String protocolVersion = '1.3';
|
||||||
}
|
}
|
||||||
|
|
||||||
class DartDevelopmentServiceException implements Exception {
|
class DartDevelopmentServiceException implements Exception {
|
||||||
|
|
|
@ -206,6 +206,19 @@ class DartDevelopmentServiceClient {
|
||||||
return supportedProtocols;
|
return supportedProtocols;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_clientPeer.registerMethod(
|
||||||
|
'getAvailableCachedCpuSamples',
|
||||||
|
(_) => {
|
||||||
|
'type': 'AvailableCachedCpuSamples',
|
||||||
|
'cacheNames': dds.cachedUserTags,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
_clientPeer.registerMethod(
|
||||||
|
'getCachedCpuSamples',
|
||||||
|
dds.isolateManager.getCachedCpuSamples,
|
||||||
|
);
|
||||||
|
|
||||||
// `evaluate` and `evaluateInFrame` actually consist of multiple RPC
|
// `evaluate` and `evaluateInFrame` actually consist of multiple RPC
|
||||||
// invocations, including a call to `compileExpression` which can be
|
// invocations, including a call to `compileExpression` which can be
|
||||||
// overridden by clients which provide their own implementation (e.g.,
|
// overridden by clients which provide their own implementation (e.g.,
|
||||||
|
|
68
pkg/dds/lib/src/common/ring_buffer.dart
Normal file
68
pkg/dds/lib/src/common/ring_buffer.dart
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
class RingBuffer<T> {
|
||||||
|
RingBuffer(this._bufferSize) {
|
||||||
|
_buffer = List<T?>.filled(
|
||||||
|
_bufferSize,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterable<T> call() sync* {
|
||||||
|
for (int i = _size - 1; i >= 0; --i) {
|
||||||
|
yield _buffer[(_count - i - 1) % _bufferSize]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Inserts a new element into the [RingBuffer].
|
||||||
|
///
|
||||||
|
/// Returns the element evicted as a result of adding the new element if the
|
||||||
|
/// buffer is as max capacity, null otherwise.
|
||||||
|
T? add(T e) {
|
||||||
|
if (_buffer.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
T? evicted;
|
||||||
|
final index = _count % _bufferSize;
|
||||||
|
if (index < _count) {
|
||||||
|
evicted = _buffer[index];
|
||||||
|
}
|
||||||
|
_buffer[index] = e;
|
||||||
|
_count++;
|
||||||
|
return evicted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize(int size) {
|
||||||
|
assert(size >= 0);
|
||||||
|
if (size == _bufferSize) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final resized = List<T?>.filled(
|
||||||
|
size,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
int count = 0;
|
||||||
|
if (size > 0) {
|
||||||
|
for (final e in this()) {
|
||||||
|
resized[count++ % size] = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_count = count;
|
||||||
|
_bufferSize = size;
|
||||||
|
_buffer = resized;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isTruncated => _count % bufferSize < _count;
|
||||||
|
|
||||||
|
int get bufferSize => _bufferSize;
|
||||||
|
|
||||||
|
int get _size => min(_count, _bufferSize);
|
||||||
|
|
||||||
|
int _bufferSize;
|
||||||
|
int _count = 0;
|
||||||
|
late List<T?> _buffer;
|
||||||
|
}
|
201
pkg/dds/lib/src/cpu_samples_manager.dart
Normal file
201
pkg/dds/lib/src/cpu_samples_manager.dart
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:dds/src/common/ring_buffer.dart';
|
||||||
|
import 'package:vm_service/vm_service.dart';
|
||||||
|
|
||||||
|
import 'dds_impl.dart';
|
||||||
|
|
||||||
|
/// Manages CPU sample caches for an individual [Isolate].
|
||||||
|
class CpuSamplesManager {
|
||||||
|
CpuSamplesManager(this.dds, this.isolateId) {
|
||||||
|
for (final userTag in dds.cachedUserTags) {
|
||||||
|
cpuSamplesCaches[userTag] = CpuSamplesRepository(userTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleUserTagEvent(Event event) {
|
||||||
|
assert(event.kind! == EventKind.kUserTagChanged);
|
||||||
|
_currentTag = event.updatedTag!;
|
||||||
|
final previousTag = event.previousTag!;
|
||||||
|
if (cpuSamplesCaches.containsKey(previousTag)) {
|
||||||
|
_lastCachedTag = previousTag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleCpuSamplesEvent(Event event) {
|
||||||
|
assert(event.kind! == EventKind.kCpuSamples);
|
||||||
|
// There might be some samples left in the buffer for the previously set
|
||||||
|
// user tag. We'll check for them here and then close out the cache.
|
||||||
|
if (_lastCachedTag != null) {
|
||||||
|
cpuSamplesCaches[_lastCachedTag]!.cacheSamples(
|
||||||
|
event.cpuSamples!,
|
||||||
|
);
|
||||||
|
_lastCachedTag = null;
|
||||||
|
}
|
||||||
|
cpuSamplesCaches[_currentTag]?.cacheSamples(event.cpuSamples!);
|
||||||
|
}
|
||||||
|
|
||||||
|
final DartDevelopmentServiceImpl dds;
|
||||||
|
final String isolateId;
|
||||||
|
final cpuSamplesCaches = <String, CpuSamplesRepository>{};
|
||||||
|
|
||||||
|
String _currentTag = '';
|
||||||
|
String? _lastCachedTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CpuSamplesRepository extends RingBuffer<CpuSample> {
|
||||||
|
// TODO(#46978): math to figure out proper buffer sizes.
|
||||||
|
CpuSamplesRepository(
|
||||||
|
this.tag, [
|
||||||
|
int bufferSize = 1000000,
|
||||||
|
]) : super(bufferSize);
|
||||||
|
|
||||||
|
void cacheSamples(CpuSamples samples) {
|
||||||
|
String getFunctionId(ProfileFunction function) {
|
||||||
|
final functionObject = function.function;
|
||||||
|
if (functionObject is NativeFunction) {
|
||||||
|
return 'native/${functionObject.name}';
|
||||||
|
}
|
||||||
|
return functionObject.id!;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize upon seeing our first samples.
|
||||||
|
if (functions.isEmpty) {
|
||||||
|
samplePeriod = samples.samplePeriod!;
|
||||||
|
maxStackDepth = samples.maxStackDepth!;
|
||||||
|
pid = samples.pid!;
|
||||||
|
functions.addAll(samples.functions!);
|
||||||
|
|
||||||
|
// Build the initial id to function index mapping. This allows for us to
|
||||||
|
// lookup a ProfileFunction in the global function list stored in this
|
||||||
|
// cache. This works since most ProfileFunction objects will have an
|
||||||
|
// associated function with a *typically* stable service ID that we can
|
||||||
|
// use as a key.
|
||||||
|
//
|
||||||
|
// TODO(bkonyi): investigate creating some form of stable ID for
|
||||||
|
// Functions tied to closures.
|
||||||
|
for (int i = 0; i < functions.length; ++i) {
|
||||||
|
idToFunctionIndex[getFunctionId(functions[i])] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear tick information as we'll need to recalculate these values later
|
||||||
|
// when a request for samples from this repository is received.
|
||||||
|
for (final f in functions) {
|
||||||
|
f.inclusiveTicks = 0;
|
||||||
|
f.exclusiveTicks = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_firstSampleTimestamp = samples.timeOriginMicros!;
|
||||||
|
} else {
|
||||||
|
final newFunctions = samples.functions!;
|
||||||
|
final indexMapping = <int, int>{};
|
||||||
|
|
||||||
|
// Check to see if we've got a function object we've never seen before.
|
||||||
|
for (int i = 0; i < newFunctions.length; ++i) {
|
||||||
|
final key = getFunctionId(newFunctions[i]);
|
||||||
|
if (!idToFunctionIndex.containsKey(key)) {
|
||||||
|
idToFunctionIndex[key] = functions.length;
|
||||||
|
// Keep track of the original index and the location of the function
|
||||||
|
// in the master function list so we can update the function indicies
|
||||||
|
// for each sample in this batch.
|
||||||
|
indexMapping[i] = functions.length;
|
||||||
|
functions.add(newFunctions[i]);
|
||||||
|
|
||||||
|
// Reset tick state as we'll recalculate later.
|
||||||
|
functions.last.inclusiveTicks = 0;
|
||||||
|
functions.last.exclusiveTicks = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the indicies into the function table for functions that were
|
||||||
|
// newly processed in the most recent event.
|
||||||
|
for (final sample in samples.samples!) {
|
||||||
|
final stack = sample.stack!;
|
||||||
|
for (int i = 0; i < stack.length; ++i) {
|
||||||
|
if (indexMapping.containsKey(stack[i])) {
|
||||||
|
stack[i] = indexMapping[stack[i]]!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final relevantSamples = samples.samples!.where((s) => s.userTag == tag);
|
||||||
|
for (final sample in relevantSamples) {
|
||||||
|
add(sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
CpuSample? add(CpuSample sample) {
|
||||||
|
final evicted = super.add(sample);
|
||||||
|
|
||||||
|
void updateTicksForSample(CpuSample sample, int increment) {
|
||||||
|
final stack = sample.stack!;
|
||||||
|
for (int i = 0; i < stack.length; ++i) {
|
||||||
|
final function = functions[stack[i]];
|
||||||
|
function.inclusiveTicks = function.inclusiveTicks! + increment;
|
||||||
|
if (i + 1 == stack.length) {
|
||||||
|
function.exclusiveTicks = function.exclusiveTicks! + increment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (evicted != null) {
|
||||||
|
// If a sample is evicted from the cache, we need to decrement the tick
|
||||||
|
// counters for each function in the sample's stack.
|
||||||
|
updateTicksForSample(sample, -1);
|
||||||
|
|
||||||
|
// We also need to change the first timestamp to that of the next oldest
|
||||||
|
// sample.
|
||||||
|
_firstSampleTimestamp = call().first.timestamp!;
|
||||||
|
}
|
||||||
|
_lastSampleTimestamp = sample.timestamp!;
|
||||||
|
|
||||||
|
// Update function ticks to include the new sample.
|
||||||
|
updateTicksForSample(sample, 1);
|
||||||
|
|
||||||
|
return evicted;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'type': 'CachedCpuSamples',
|
||||||
|
'userTag': tag,
|
||||||
|
'truncated': isTruncated,
|
||||||
|
if (functions.isNotEmpty) ...{
|
||||||
|
'samplePeriod': samplePeriod,
|
||||||
|
'maxStackDepth': maxStackDepth,
|
||||||
|
},
|
||||||
|
'timeOriginMicros': _firstSampleTimestamp,
|
||||||
|
'timeExtentMicros': _lastSampleTimestamp - _firstSampleTimestamp,
|
||||||
|
'functions': [
|
||||||
|
// TODO(bkonyi): remove functions with no ticks and update sample stacks.
|
||||||
|
for (final f in functions) f.toJson(),
|
||||||
|
],
|
||||||
|
'sampleCount': call().length,
|
||||||
|
'samples': [
|
||||||
|
for (final s in call()) s.toJson(),
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The UserTag associated with all samples stored in this repository.
|
||||||
|
final String tag;
|
||||||
|
|
||||||
|
/// The list of function references with corresponding profiler tick data.
|
||||||
|
/// ** NOTE **: The tick values here need to be updated as new CpuSamples
|
||||||
|
/// events are delivered.
|
||||||
|
final functions = <ProfileFunction>[];
|
||||||
|
final idToFunctionIndex = <String, int>{};
|
||||||
|
|
||||||
|
/// Assume sample period and max stack depth won't change.
|
||||||
|
late final int samplePeriod;
|
||||||
|
late final int maxStackDepth;
|
||||||
|
|
||||||
|
late final int pid;
|
||||||
|
|
||||||
|
int _firstSampleTimestamp = 0;
|
||||||
|
int _lastSampleTimestamp = 0;
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:collection';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
@ -54,6 +55,7 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService {
|
||||||
this._remoteVmServiceUri,
|
this._remoteVmServiceUri,
|
||||||
this._uri,
|
this._uri,
|
||||||
this._authCodesEnabled,
|
this._authCodesEnabled,
|
||||||
|
this._cachedUserTags,
|
||||||
this._ipv6,
|
this._ipv6,
|
||||||
this._devToolsConfiguration,
|
this._devToolsConfiguration,
|
||||||
this.shouldLogRequests,
|
this.shouldLogRequests,
|
||||||
|
@ -388,6 +390,9 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService {
|
||||||
|
|
||||||
final DevToolsConfiguration? _devToolsConfiguration;
|
final DevToolsConfiguration? _devToolsConfiguration;
|
||||||
|
|
||||||
|
List<String> get cachedUserTags => UnmodifiableListView(_cachedUserTags);
|
||||||
|
final List<String> _cachedUserTags;
|
||||||
|
|
||||||
Future<void> get done => _done.future;
|
Future<void> get done => _done.future;
|
||||||
Completer _done = Completer<void>();
|
Completer _done = Completer<void>();
|
||||||
bool _initializationComplete = false;
|
bool _initializationComplete = false;
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// 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.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:dds/src/utils/mutex.dart';
|
|
||||||
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||||
|
import 'package:vm_service/vm_service.dart';
|
||||||
|
|
||||||
import 'client.dart';
|
import 'client.dart';
|
||||||
import 'constants.dart';
|
import 'constants.dart';
|
||||||
|
import 'cpu_samples_manager.dart';
|
||||||
import 'dds_impl.dart';
|
import 'dds_impl.dart';
|
||||||
|
import 'utils/mutex.dart';
|
||||||
|
|
||||||
/// This file contains functionality used to track the running state of
|
/// This file contains functionality used to track the running state of
|
||||||
/// all isolates in a given Dart process.
|
/// all isolates in a given Dart process.
|
||||||
|
@ -36,7 +38,11 @@ enum _IsolateState {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _RunningIsolate {
|
class _RunningIsolate {
|
||||||
_RunningIsolate(this.isolateManager, this.id, this.name);
|
_RunningIsolate(this.isolateManager, this.id, this.name)
|
||||||
|
: cpuSamplesManager = CpuSamplesManager(
|
||||||
|
isolateManager.dds,
|
||||||
|
id,
|
||||||
|
);
|
||||||
|
|
||||||
// State setters.
|
// State setters.
|
||||||
void pausedOnExit() => _state = _IsolateState.pauseExit;
|
void pausedOnExit() => _state = _IsolateState.pauseExit;
|
||||||
|
@ -104,6 +110,29 @@ class _RunningIsolate {
|
||||||
/// Should always be called after an isolate is resumed.
|
/// Should always be called after an isolate is resumed.
|
||||||
void clearResumeApprovals() => _resumeApprovalsByName.clear();
|
void clearResumeApprovals() => _resumeApprovalsByName.clear();
|
||||||
|
|
||||||
|
Map<String, dynamic> getCachedCpuSamples(String userTag) {
|
||||||
|
final repo = cpuSamplesManager.cpuSamplesCaches[userTag];
|
||||||
|
if (repo == null) {
|
||||||
|
throw json_rpc.RpcException.invalidParams(
|
||||||
|
'CPU sample caching is not enabled for tag: "$userTag"',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return repo.toJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEvent(Event event) {
|
||||||
|
switch (event.kind) {
|
||||||
|
case EventKind.kUserTagChanged:
|
||||||
|
cpuSamplesManager.handleUserTagEvent(event);
|
||||||
|
return;
|
||||||
|
case EventKind.kCpuSamples:
|
||||||
|
cpuSamplesManager.handleCpuSamplesEvent(event);
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int get _isolateStateMask => isolateStateToMaskMapping[_state] ?? 0;
|
int get _isolateStateMask => isolateStateToMaskMapping[_state] ?? 0;
|
||||||
|
|
||||||
static const isolateStateToMaskMapping = {
|
static const isolateStateToMaskMapping = {
|
||||||
|
@ -113,6 +142,7 @@ class _RunningIsolate {
|
||||||
};
|
};
|
||||||
|
|
||||||
final IsolateManager isolateManager;
|
final IsolateManager isolateManager;
|
||||||
|
final CpuSamplesManager cpuSamplesManager;
|
||||||
final String name;
|
final String name;
|
||||||
final String id;
|
final String id;
|
||||||
final Set<String?> _resumeApprovalsByName = {};
|
final Set<String?> _resumeApprovalsByName = {};
|
||||||
|
@ -123,20 +153,25 @@ class IsolateManager {
|
||||||
IsolateManager(this.dds);
|
IsolateManager(this.dds);
|
||||||
|
|
||||||
/// Handles state changes for isolates.
|
/// Handles state changes for isolates.
|
||||||
void handleIsolateEvent(json_rpc.Parameters parameters) {
|
void handleIsolateEvent(Event event) {
|
||||||
final event = parameters['event'];
|
|
||||||
final eventKind = event['kind'].asString;
|
|
||||||
|
|
||||||
// There's no interesting information about isolate state associated with
|
// There's no interesting information about isolate state associated with
|
||||||
// and IsolateSpawn event.
|
// and IsolateSpawn event.
|
||||||
if (eventKind == ServiceEvents.isolateSpawn) {
|
// TODO(bkonyi): why isn't IsolateSpawn in package:vm_service
|
||||||
|
if (event.kind! == ServiceEvents.isolateSpawn) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final isolateData = event['isolate'];
|
final isolateData = event.isolate!;
|
||||||
final id = isolateData['id'].asString;
|
final id = isolateData.id!;
|
||||||
final name = isolateData['name'].asString;
|
final name = isolateData.name!;
|
||||||
_updateIsolateState(id, name, eventKind);
|
_updateIsolateState(id, name, event.kind!);
|
||||||
|
}
|
||||||
|
|
||||||
|
void routeEventToIsolate(Event event) {
|
||||||
|
final isolateId = event.isolate!.id!;
|
||||||
|
if (isolates.containsKey(isolateId)) {
|
||||||
|
isolates[isolateId]!.handleEvent(event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _updateIsolateState(String id, String name, String eventKind) {
|
void _updateIsolateState(String id, String name, String eventKind) {
|
||||||
|
@ -248,6 +283,16 @@ class IsolateManager {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> getCachedCpuSamples(json_rpc.Parameters parameters) {
|
||||||
|
final isolateId = parameters['isolateId'].asString;
|
||||||
|
if (!isolates.containsKey(isolateId)) {
|
||||||
|
return RPCResponses.collectedSentinel;
|
||||||
|
}
|
||||||
|
final isolate = isolates[isolateId]!;
|
||||||
|
final userTag = parameters['userTag'].asString;
|
||||||
|
return isolate.getCachedCpuSamples(userTag);
|
||||||
|
}
|
||||||
|
|
||||||
/// Forwards a `resume` request to the VM service.
|
/// Forwards a `resume` request to the VM service.
|
||||||
Future<Map<String, dynamic>> _sendResumeRequest(
|
Future<Map<String, dynamic>> _sendResumeRequest(
|
||||||
String isolateId,
|
String isolateId,
|
||||||
|
|
|
@ -2,17 +2,16 @@
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
// 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.
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:math';
|
|
||||||
|
|
||||||
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||||
|
|
||||||
import 'client.dart';
|
import 'client.dart';
|
||||||
|
import 'common/ring_buffer.dart';
|
||||||
|
|
||||||
/// [LoggingRepository] is used to store historical log messages from the
|
/// [LoggingRepository] is used to store historical log messages from the
|
||||||
/// target VM service. Clients which connect to DDS and subscribe to the
|
/// target VM service. Clients which connect to DDS and subscribe to the
|
||||||
/// `Logging` stream will be sent all messages contained within this repository
|
/// `Logging` stream will be sent all messages contained within this repository
|
||||||
/// upon initial subscription.
|
/// upon initial subscription.
|
||||||
class LoggingRepository extends _RingBuffer<Map<String, dynamic>> {
|
class LoggingRepository extends RingBuffer<Map<String, dynamic>> {
|
||||||
LoggingRepository([int logHistoryLength = 10000]) : super(logHistoryLength) {
|
LoggingRepository([int logHistoryLength = 10000]) : super(logHistoryLength) {
|
||||||
// TODO(bkonyi): enforce log history limit when DartDevelopmentService
|
// TODO(bkonyi): enforce log history limit when DartDevelopmentService
|
||||||
// allows for this to be set via Dart code.
|
// allows for this to be set via Dart code.
|
||||||
|
@ -45,54 +44,3 @@ class LoggingRepository extends _RingBuffer<Map<String, dynamic>> {
|
||||||
final Set<DartDevelopmentServiceClient> _sentHistoricLogsClientSet = {};
|
final Set<DartDevelopmentServiceClient> _sentHistoricLogsClientSet = {};
|
||||||
static const int _kMaxLogBufferSize = 100000;
|
static const int _kMaxLogBufferSize = 100000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(bkonyi): move to standalone file if we decide to use this elsewhere.
|
|
||||||
class _RingBuffer<T> {
|
|
||||||
_RingBuffer(this._bufferSize) {
|
|
||||||
_buffer = List<T?>.filled(
|
|
||||||
_bufferSize,
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterable<T> call() sync* {
|
|
||||||
for (int i = _size - 1; i >= 0; --i) {
|
|
||||||
yield _buffer[(_count - i - 1) % _bufferSize]!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(T e) {
|
|
||||||
if (_buffer.isEmpty) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_buffer[_count++ % _bufferSize] = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
void resize(int size) {
|
|
||||||
assert(size >= 0);
|
|
||||||
if (size == _bufferSize) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final resized = List<T?>.filled(
|
|
||||||
size,
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
int count = 0;
|
|
||||||
if (size > 0) {
|
|
||||||
for (final e in this()) {
|
|
||||||
resized[count++ % size] = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_count = count;
|
|
||||||
_bufferSize = size;
|
|
||||||
_buffer = resized;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get bufferSize => _bufferSize;
|
|
||||||
|
|
||||||
int get _size => min(_count, _bufferSize);
|
|
||||||
|
|
||||||
int _bufferSize;
|
|
||||||
int _count = 0;
|
|
||||||
late List<T?> _buffer;
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||||
|
import 'package:vm_service/vm_service.dart';
|
||||||
|
|
||||||
import 'client.dart';
|
import 'client.dart';
|
||||||
import 'dds_impl.dart';
|
import 'dds_impl.dart';
|
||||||
|
@ -108,18 +109,31 @@ class StreamManager {
|
||||||
// Stdout and Stderr streams may not exist.
|
// Stdout and Stderr streams may not exist.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (dds.cachedUserTags.isNotEmpty) {
|
||||||
|
await streamListen(null, EventStreams.kProfiler);
|
||||||
|
}
|
||||||
dds.vmServiceClient.registerMethod(
|
dds.vmServiceClient.registerMethod(
|
||||||
'streamNotify',
|
'streamNotify',
|
||||||
(parameters) {
|
(json_rpc.Parameters parameters) {
|
||||||
final streamId = parameters['streamId'].asString;
|
final streamId = parameters['streamId'].asString;
|
||||||
|
final event =
|
||||||
|
Event.parse(parameters['event'].asMap.cast<String, dynamic>())!;
|
||||||
|
|
||||||
// Forward events from the streams IsolateManager subscribes to.
|
// Forward events from the streams IsolateManager subscribes to.
|
||||||
if (isolateManagerStreams.contains(streamId)) {
|
if (isolateManagerStreams.contains(streamId)) {
|
||||||
dds.isolateManager.handleIsolateEvent(parameters);
|
dds.isolateManager.handleIsolateEvent(event);
|
||||||
}
|
}
|
||||||
// Keep a history of messages to send to clients when they first
|
// Keep a history of messages to send to clients when they first
|
||||||
// subscribe to a stream with an event history.
|
// subscribe to a stream with an event history.
|
||||||
if (loggingRepositories.containsKey(streamId)) {
|
if (loggingRepositories.containsKey(streamId)) {
|
||||||
loggingRepositories[streamId]!.add(parameters.asMap);
|
loggingRepositories[streamId]!.add(
|
||||||
|
parameters.asMap.cast<String, dynamic>(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// If the event contains an isolate, forward the event to the
|
||||||
|
// corresponding isolate to be handled.
|
||||||
|
if (event.isolate != null) {
|
||||||
|
dds.isolateManager.routeEventToIsolate(event);
|
||||||
}
|
}
|
||||||
streamNotify(streamId, parameters.value);
|
streamNotify(streamId, parameters.value);
|
||||||
},
|
},
|
||||||
|
@ -262,6 +276,7 @@ class StreamManager {
|
||||||
static const kExtensionStream = 'Extension';
|
static const kExtensionStream = 'Extension';
|
||||||
static const kIsolateStream = 'Isolate';
|
static const kIsolateStream = 'Isolate';
|
||||||
static const kLoggingStream = 'Logging';
|
static const kLoggingStream = 'Logging';
|
||||||
|
static const kProfilerStream = 'Profiler';
|
||||||
static const kStderrStream = 'Stderr';
|
static const kStderrStream = 'Stderr';
|
||||||
static const kStdoutStream = 'Stdout';
|
static const kStdoutStream = 'Stdout';
|
||||||
|
|
||||||
|
@ -283,6 +298,12 @@ class StreamManager {
|
||||||
kStdoutStream,
|
kStdoutStream,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Never cancel the profiler stream as `CpuSampleRepository` requires
|
||||||
|
// `UserTagChanged` events to enable/disable sample caching.
|
||||||
|
static const cpuSampleRepositoryStreams = <String>{
|
||||||
|
kProfilerStream,
|
||||||
|
};
|
||||||
|
|
||||||
// The set of streams that DDS requires to function.
|
// The set of streams that DDS requires to function.
|
||||||
static final ddsCoreStreams = <String>{
|
static final ddsCoreStreams = <String>{
|
||||||
...isolateManagerStreams,
|
...isolateManagerStreams,
|
||||||
|
|
|
@ -13,18 +13,46 @@ extension DdsExtension on VmService {
|
||||||
static bool _factoriesRegistered = false;
|
static bool _factoriesRegistered = false;
|
||||||
static Version? _ddsVersion;
|
static Version? _ddsVersion;
|
||||||
|
|
||||||
/// The _getDartDevelopmentServiceVersion_ RPC is used to determine what version of
|
/// The [getDartDevelopmentServiceVersion] RPC is used to determine what version of
|
||||||
/// the Dart Development Service Protocol is served by a DDS instance.
|
/// the Dart Development Service Protocol is served by a DDS instance.
|
||||||
///
|
///
|
||||||
/// The result of this call is cached for subsequent invocations.
|
/// The result of this call is cached for subsequent invocations.
|
||||||
Future<Version> getDartDevelopmentServiceVersion() async {
|
Future<Version> getDartDevelopmentServiceVersion() async {
|
||||||
if (_ddsVersion == null) {
|
if (_ddsVersion == null) {
|
||||||
_ddsVersion =
|
_ddsVersion = await _callHelper<Version>(
|
||||||
await _callHelper<Version>('getDartDevelopmentServiceVersion');
|
'getDartDevelopmentServiceVersion',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return _ddsVersion!;
|
return _ddsVersion!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The [getCachedCpuSamples] RPC is used to retrieve a cache of CPU samples
|
||||||
|
/// collected under a [UserTag] with name `userTag`.
|
||||||
|
Future<CachedCpuSamples> getCachedCpuSamples(
|
||||||
|
String isolateId, String userTag) async {
|
||||||
|
if (!(await _versionCheck(1, 3))) {
|
||||||
|
throw UnimplementedError('getCachedCpuSamples requires DDS version 1.3');
|
||||||
|
}
|
||||||
|
return _callHelper<CachedCpuSamples>('getCachedCpuSamples', args: {
|
||||||
|
'isolateId': isolateId,
|
||||||
|
'userTag': userTag,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [getAvailableCachedCpuSamples] RPC is used to determine which caches of CPU samples
|
||||||
|
/// are available. Caches are associated with individual [UserTag] names and are specified
|
||||||
|
/// when DDS is started via the `cachedUserTags` parameter.
|
||||||
|
Future<AvailableCachedCpuSamples> getAvailableCachedCpuSamples() async {
|
||||||
|
if (!(await _versionCheck(1, 3))) {
|
||||||
|
throw UnimplementedError(
|
||||||
|
'getAvailableCachedCpuSamples requires DDS version 1.3',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _callHelper<AvailableCachedCpuSamples>(
|
||||||
|
'getAvailableCachedCpuSamples',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieve the event history for `stream`.
|
/// Retrieve the event history for `stream`.
|
||||||
///
|
///
|
||||||
/// If `stream` does not have event history collected, a parameter error is
|
/// If `stream` does not have event history collected, a parameter error is
|
||||||
|
@ -126,6 +154,11 @@ extension DdsExtension on VmService {
|
||||||
|
|
||||||
static void _registerFactories() {
|
static void _registerFactories() {
|
||||||
addTypeFactory('StreamHistory', StreamHistory.parse);
|
addTypeFactory('StreamHistory', StreamHistory.parse);
|
||||||
|
addTypeFactory(
|
||||||
|
'AvailableCachedCpuSamples',
|
||||||
|
AvailableCachedCpuSamples.parse,
|
||||||
|
);
|
||||||
|
addTypeFactory('CachedCpuSamples', CachedCpuSamples.parse);
|
||||||
_factoriesRegistered = true;
|
_factoriesRegistered = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,3 +187,86 @@ class StreamHistory extends Response {
|
||||||
List<Event> get history => UnmodifiableListView(_history);
|
List<Event> get history => UnmodifiableListView(_history);
|
||||||
final List<Event> _history;
|
final List<Event> _history;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An extension of [CpuSamples] which represents a set of cached samples,
|
||||||
|
/// associated with a particular [UserTag] name.
|
||||||
|
class CachedCpuSamples extends CpuSamples {
|
||||||
|
static CachedCpuSamples? parse(Map<String, dynamic>? json) =>
|
||||||
|
json == null ? null : CachedCpuSamples._fromJson(json);
|
||||||
|
|
||||||
|
CachedCpuSamples({
|
||||||
|
required this.userTag,
|
||||||
|
this.truncated,
|
||||||
|
required int? samplePeriod,
|
||||||
|
required int? maxStackDepth,
|
||||||
|
required int? sampleCount,
|
||||||
|
required int? timeSpan,
|
||||||
|
required int? timeOriginMicros,
|
||||||
|
required int? timeExtentMicros,
|
||||||
|
required int? pid,
|
||||||
|
required List<ProfileFunction>? functions,
|
||||||
|
required List<CpuSample>? samples,
|
||||||
|
}) : super(
|
||||||
|
samplePeriod: samplePeriod,
|
||||||
|
maxStackDepth: maxStackDepth,
|
||||||
|
sampleCount: sampleCount,
|
||||||
|
timeSpan: timeSpan,
|
||||||
|
timeOriginMicros: timeOriginMicros,
|
||||||
|
timeExtentMicros: timeExtentMicros,
|
||||||
|
pid: pid,
|
||||||
|
functions: functions,
|
||||||
|
samples: samples,
|
||||||
|
);
|
||||||
|
|
||||||
|
CachedCpuSamples._fromJson(Map<String, dynamic> json)
|
||||||
|
: userTag = json['userTag']!,
|
||||||
|
truncated = json['truncated'],
|
||||||
|
super(
|
||||||
|
samplePeriod: json['samplePeriod'] ?? -1,
|
||||||
|
maxStackDepth: json['maxStackDepth'] ?? -1,
|
||||||
|
sampleCount: json['sampleCount'] ?? -1,
|
||||||
|
timeSpan: json['timeSpan'] ?? -1,
|
||||||
|
timeOriginMicros: json['timeOriginMicros'] ?? -1,
|
||||||
|
timeExtentMicros: json['timeExtentMicros'] ?? -1,
|
||||||
|
pid: json['pid'] ?? -1,
|
||||||
|
functions: List<ProfileFunction>.from(
|
||||||
|
createServiceObject(json['functions'], const ['ProfileFunction'])
|
||||||
|
as List? ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
samples: List<CpuSample>.from(
|
||||||
|
createServiceObject(json['samples'], const ['CpuSample'])
|
||||||
|
as List? ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get type => 'CachedCpuSamples';
|
||||||
|
|
||||||
|
/// The name of the [UserTag] associated with this cache of [CpuSamples].
|
||||||
|
final String userTag;
|
||||||
|
|
||||||
|
/// Provided if the CPU sample cache has filled and older samples have been
|
||||||
|
/// dropped.
|
||||||
|
final bool? truncated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A collection of [UserTag] names associated with caches of CPU samples.
|
||||||
|
class AvailableCachedCpuSamples extends Response {
|
||||||
|
static AvailableCachedCpuSamples? parse(Map<String, dynamic>? json) =>
|
||||||
|
json == null ? null : AvailableCachedCpuSamples._fromJson(json);
|
||||||
|
|
||||||
|
AvailableCachedCpuSamples({
|
||||||
|
required this.cacheNames,
|
||||||
|
});
|
||||||
|
|
||||||
|
AvailableCachedCpuSamples._fromJson(Map<String, dynamic> json)
|
||||||
|
: cacheNames = List<String>.from(json['cacheNames']);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get type => 'AvailableCachedUserTagCpuSamples';
|
||||||
|
|
||||||
|
/// A [List] of [UserTag] names associated with CPU sample caches.
|
||||||
|
final List<String> cacheNames;
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ dependencies:
|
||||||
shelf_web_socket: ^1.0.0
|
shelf_web_socket: ^1.0.0
|
||||||
sse: ^4.0.0
|
sse: ^4.0.0
|
||||||
stream_channel: ^2.0.0
|
stream_channel: ^2.0.0
|
||||||
vm_service: ^7.0.0
|
vm_service: ^7.2.0
|
||||||
web_socket_channel: ^2.0.0
|
web_socket_channel: ^2.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|
21
pkg/dds/test/get_cached_cpu_samples_script.dart
Normal file
21
pkg/dds/test/get_cached_cpu_samples_script.dart
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
fib(int n) {
|
||||||
|
if (n <= 1) {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return fib(n - 1) + fib(n - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
UserTag('Testing').makeCurrent();
|
||||||
|
int i = 5;
|
||||||
|
while (true) {
|
||||||
|
++i;
|
||||||
|
fib(i);
|
||||||
|
}
|
||||||
|
}
|
124
pkg/dds/test/get_cached_cpu_samples_test.dart
Normal file
124
pkg/dds/test/get_cached_cpu_samples_test.dart
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
// Copyright (c) 2021, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dds/dds.dart';
|
||||||
|
import 'package:dds/src/utils/mutex.dart';
|
||||||
|
import 'package:dds/vm_service_extensions.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
import 'package:vm_service/vm_service.dart';
|
||||||
|
import 'package:vm_service/vm_service_io.dart';
|
||||||
|
import 'common/test_helper.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
late Process process;
|
||||||
|
late DartDevelopmentService dds;
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
process = await spawnDartProcess(
|
||||||
|
'get_cached_cpu_samples_script.dart',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
await dds.shutdown();
|
||||||
|
process.kill();
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'No UserTags to cache',
|
||||||
|
() async {
|
||||||
|
dds = await DartDevelopmentService.startDartDevelopmentService(
|
||||||
|
remoteVmServiceUri,
|
||||||
|
);
|
||||||
|
expect(dds.isRunning, true);
|
||||||
|
final service = await vmServiceConnectUri(dds.wsUri.toString());
|
||||||
|
|
||||||
|
// We didn't provide `cachedUserTags` when starting DDS, so we shouldn't
|
||||||
|
// be caching anything.
|
||||||
|
final availableCaches = await service.getAvailableCachedCpuSamples();
|
||||||
|
expect(availableCaches.cacheNames.length, 0);
|
||||||
|
|
||||||
|
final isolate = (await service.getVM()).isolates!.first;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await service.getCachedCpuSamples(isolate.id!, 'Fake');
|
||||||
|
fail('Invalid userTag did not cause an exception');
|
||||||
|
} on RPCError catch (e) {
|
||||||
|
expect(
|
||||||
|
e.message,
|
||||||
|
'CPU sample caching is not enabled for tag: "Fake"',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
timeout: Timeout.none,
|
||||||
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Cache CPU samples for provided UserTag name',
|
||||||
|
() async {
|
||||||
|
const kUserTag = 'Testing';
|
||||||
|
dds = await DartDevelopmentService.startDartDevelopmentService(
|
||||||
|
remoteVmServiceUri,
|
||||||
|
cachedUserTags: [kUserTag],
|
||||||
|
);
|
||||||
|
expect(dds.isRunning, true);
|
||||||
|
final service = await vmServiceConnectUri(dds.wsUri.toString());
|
||||||
|
|
||||||
|
// Ensure we're caching results for samples under the 'Testing' UserTag.
|
||||||
|
final availableCaches = await service.getAvailableCachedCpuSamples();
|
||||||
|
expect(availableCaches.cacheNames.length, 1);
|
||||||
|
expect(availableCaches.cacheNames.first, kUserTag);
|
||||||
|
|
||||||
|
final isolate = (await service.getVM()).isolates!.first;
|
||||||
|
|
||||||
|
final completer = Completer<void>();
|
||||||
|
int i = 0;
|
||||||
|
int count = 0;
|
||||||
|
final mutex = Mutex();
|
||||||
|
|
||||||
|
late StreamSubscription sub;
|
||||||
|
sub = service.onProfilerEvent.listen(
|
||||||
|
(event) async {
|
||||||
|
// Process one event at a time to prevent racey updates to count.
|
||||||
|
await mutex.runGuarded(
|
||||||
|
() async {
|
||||||
|
if (event.kind == EventKind.kCpuSamples &&
|
||||||
|
event.isolate!.id! == isolate.id!) {
|
||||||
|
++i;
|
||||||
|
if (i > 3) {
|
||||||
|
if (!completer.isCompleted) {
|
||||||
|
await sub.cancel();
|
||||||
|
completer.complete();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Ensure the number of CPU samples in the CpuSample event is
|
||||||
|
// is consistent with the number of samples in the cache.
|
||||||
|
expect(event.cpuSamples, isNotNull);
|
||||||
|
count += event.cpuSamples!.samples!
|
||||||
|
.where((e) => e.userTag == kUserTag)
|
||||||
|
.length;
|
||||||
|
final cache = await service.getCachedCpuSamples(
|
||||||
|
isolate.id!,
|
||||||
|
availableCaches.cacheNames.first,
|
||||||
|
);
|
||||||
|
// DDS may have processed more sample blocks than we've had a chance
|
||||||
|
// to, so just ensure we have at least as many samples in the cache
|
||||||
|
// as we've seen.
|
||||||
|
expect(cache.sampleCount! >= count, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await service.streamListen(EventStreams.kProfiler);
|
||||||
|
await service.resume(isolate.id!);
|
||||||
|
await completer.future;
|
||||||
|
},
|
||||||
|
timeout: Timeout.none,
|
||||||
|
);
|
||||||
|
}
|
|
@ -27,7 +27,6 @@ sigquit_starts_service_test: SkipByDesign # Spawns a secondary process using Pla
|
||||||
coverage_optimized_function_test: Pass, Slow
|
coverage_optimized_function_test: Pass, Slow
|
||||||
evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
|
evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
|
||||||
evaluate_activation_test/scope: RuntimeError # http://dartbug.com/20047
|
evaluate_activation_test/scope: RuntimeError # http://dartbug.com/20047
|
||||||
get_source_report_test: RuntimeError # Should pass again when constant evaluation is relanded, see http://dartbug.com/36600
|
|
||||||
pause_on_exception_from_slow_path_test: Pass, Slow
|
pause_on_exception_from_slow_path_test: Pass, Slow
|
||||||
pause_on_unhandled_async_exceptions2_test: Pass, Slow
|
pause_on_unhandled_async_exceptions2_test: Pass, Slow
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ sigquit_starts_service_test: SkipByDesign # Spawns a secondary process using Pla
|
||||||
coverage_optimized_function_test: Pass, Slow
|
coverage_optimized_function_test: Pass, Slow
|
||||||
evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
|
evaluate_activation_test/instance: RuntimeError # http://dartbug.com/20047
|
||||||
evaluate_activation_test/scope: RuntimeError # http://dartbug.com/20047
|
evaluate_activation_test/scope: RuntimeError # http://dartbug.com/20047
|
||||||
get_source_report_test: RuntimeError # Should pass again when constant evaluation is relanded, see http://dartbug.com/36600
|
|
||||||
pause_on_exception_from_slow_path_test: Pass, Slow
|
pause_on_exception_from_slow_path_test: Pass, Slow
|
||||||
pause_on_unhandled_async_exceptions2_test: Pass, Slow
|
pause_on_unhandled_async_exceptions2_test: Pass, Slow
|
||||||
|
|
||||||
|
|
|
@ -184,17 +184,17 @@ static constexpr dart::compiler::target::word ICData_entries_offset = 12;
|
||||||
static constexpr dart::compiler::target::word ICData_owner_offset = 20;
|
static constexpr dart::compiler::target::word ICData_owner_offset = 20;
|
||||||
static constexpr dart::compiler::target::word ICData_state_bits_offset = 28;
|
static constexpr dart::compiler::target::word ICData_state_bits_offset = 28;
|
||||||
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 20;
|
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 24;
|
||||||
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 24;
|
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 28;
|
||||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 28;
|
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 32;
|
||||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||||
20;
|
20;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_shared_class_table_offset = 8;
|
IsolateGroup_shared_class_table_offset = 8;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_cached_class_table_table_offset = 16;
|
IsolateGroup_cached_class_table_table_offset = 16;
|
||||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 36;
|
static constexpr dart::compiler::target::word Isolate_single_step_offset = 40;
|
||||||
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 16;
|
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 20;
|
||||||
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 12;
|
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 12;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
LinkedHashBase_deleted_keys_offset = 20;
|
LinkedHashBase_deleted_keys_offset = 20;
|
||||||
|
@ -727,17 +727,17 @@ static constexpr dart::compiler::target::word ICData_entries_offset = 24;
|
||||||
static constexpr dart::compiler::target::word ICData_owner_offset = 40;
|
static constexpr dart::compiler::target::word ICData_owner_offset = 40;
|
||||||
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
|
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
|
||||||
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 40;
|
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 48;
|
||||||
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 48;
|
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 56;
|
||||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
|
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 64;
|
||||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||||
40;
|
40;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_shared_class_table_offset = 16;
|
IsolateGroup_shared_class_table_offset = 16;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_cached_class_table_table_offset = 32;
|
IsolateGroup_cached_class_table_table_offset = 32;
|
||||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 72;
|
static constexpr dart::compiler::target::word Isolate_single_step_offset = 80;
|
||||||
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 32;
|
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 40;
|
||||||
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 24;
|
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 24;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
LinkedHashBase_deleted_keys_offset = 40;
|
LinkedHashBase_deleted_keys_offset = 40;
|
||||||
|
@ -1275,17 +1275,17 @@ static constexpr dart::compiler::target::word ICData_entries_offset = 12;
|
||||||
static constexpr dart::compiler::target::word ICData_owner_offset = 20;
|
static constexpr dart::compiler::target::word ICData_owner_offset = 20;
|
||||||
static constexpr dart::compiler::target::word ICData_state_bits_offset = 28;
|
static constexpr dart::compiler::target::word ICData_state_bits_offset = 28;
|
||||||
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 20;
|
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 24;
|
||||||
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 24;
|
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 28;
|
||||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 28;
|
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 32;
|
||||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||||
20;
|
20;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_shared_class_table_offset = 8;
|
IsolateGroup_shared_class_table_offset = 8;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_cached_class_table_table_offset = 16;
|
IsolateGroup_cached_class_table_table_offset = 16;
|
||||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 36;
|
static constexpr dart::compiler::target::word Isolate_single_step_offset = 40;
|
||||||
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 16;
|
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 20;
|
||||||
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 12;
|
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 12;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
LinkedHashBase_deleted_keys_offset = 20;
|
LinkedHashBase_deleted_keys_offset = 20;
|
||||||
|
@ -1815,17 +1815,17 @@ static constexpr dart::compiler::target::word ICData_entries_offset = 24;
|
||||||
static constexpr dart::compiler::target::word ICData_owner_offset = 40;
|
static constexpr dart::compiler::target::word ICData_owner_offset = 40;
|
||||||
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
|
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
|
||||||
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 40;
|
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 48;
|
||||||
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 48;
|
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 56;
|
||||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
|
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 64;
|
||||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||||
40;
|
40;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_shared_class_table_offset = 16;
|
IsolateGroup_shared_class_table_offset = 16;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_cached_class_table_table_offset = 32;
|
IsolateGroup_cached_class_table_table_offset = 32;
|
||||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 72;
|
static constexpr dart::compiler::target::word Isolate_single_step_offset = 80;
|
||||||
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 32;
|
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 40;
|
||||||
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 24;
|
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 24;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
LinkedHashBase_deleted_keys_offset = 40;
|
LinkedHashBase_deleted_keys_offset = 40;
|
||||||
|
@ -2364,17 +2364,17 @@ static constexpr dart::compiler::target::word ICData_entries_offset = 24;
|
||||||
static constexpr dart::compiler::target::word ICData_owner_offset = 40;
|
static constexpr dart::compiler::target::word ICData_owner_offset = 40;
|
||||||
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
|
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
|
||||||
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 40;
|
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 48;
|
||||||
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 48;
|
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 56;
|
||||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
|
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 64;
|
||||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||||
40;
|
40;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_shared_class_table_offset = 16;
|
IsolateGroup_shared_class_table_offset = 16;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_cached_class_table_table_offset = 32;
|
IsolateGroup_cached_class_table_table_offset = 32;
|
||||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 72;
|
static constexpr dart::compiler::target::word Isolate_single_step_offset = 80;
|
||||||
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 32;
|
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 40;
|
||||||
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 16;
|
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 16;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
LinkedHashBase_deleted_keys_offset = 24;
|
LinkedHashBase_deleted_keys_offset = 24;
|
||||||
|
@ -2912,17 +2912,17 @@ static constexpr dart::compiler::target::word ICData_entries_offset = 24;
|
||||||
static constexpr dart::compiler::target::word ICData_owner_offset = 40;
|
static constexpr dart::compiler::target::word ICData_owner_offset = 40;
|
||||||
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
|
static constexpr dart::compiler::target::word ICData_state_bits_offset = 52;
|
||||||
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 40;
|
static constexpr dart::compiler::target::word Isolate_current_tag_offset = 48;
|
||||||
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 48;
|
static constexpr dart::compiler::target::word Isolate_default_tag_offset = 56;
|
||||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
|
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 64;
|
||||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||||
40;
|
40;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_shared_class_table_offset = 16;
|
IsolateGroup_shared_class_table_offset = 16;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
IsolateGroup_cached_class_table_table_offset = 32;
|
IsolateGroup_cached_class_table_table_offset = 32;
|
||||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 72;
|
static constexpr dart::compiler::target::word Isolate_single_step_offset = 80;
|
||||||
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 32;
|
static constexpr dart::compiler::target::word Isolate_user_tag_offset = 40;
|
||||||
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 16;
|
static constexpr dart::compiler::target::word LinkedHashBase_data_offset = 16;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
LinkedHashBase_deleted_keys_offset = 24;
|
LinkedHashBase_deleted_keys_offset = 24;
|
||||||
|
@ -6720,11 +6720,11 @@ static constexpr dart::compiler::target::word AOT_ICData_owner_offset = 16;
|
||||||
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 20;
|
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 20;
|
||||||
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||||
20;
|
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
|
||||||
24;
|
24;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||||
28;
|
28;
|
||||||
|
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||||
|
32;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_object_store_offset = 20;
|
AOT_IsolateGroup_object_store_offset = 20;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -6732,8 +6732,8 @@ static constexpr dart::compiler::target::word
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_cached_class_table_table_offset = 16;
|
AOT_IsolateGroup_cached_class_table_table_offset = 16;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||||
36;
|
40;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 16;
|
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 20;
|
||||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||||
12;
|
12;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -7327,11 +7327,11 @@ static constexpr dart::compiler::target::word AOT_ICData_owner_offset = 32;
|
||||||
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 40;
|
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 40;
|
||||||
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||||
40;
|
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
|
||||||
48;
|
48;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||||
56;
|
56;
|
||||||
|
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||||
|
64;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_object_store_offset = 40;
|
AOT_IsolateGroup_object_store_offset = 40;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -7339,8 +7339,8 @@ static constexpr dart::compiler::target::word
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||||
72;
|
80;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
|
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 40;
|
||||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||||
24;
|
24;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -7940,11 +7940,11 @@ static constexpr dart::compiler::target::word AOT_ICData_owner_offset = 32;
|
||||||
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 40;
|
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 40;
|
||||||
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||||
40;
|
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
|
||||||
48;
|
48;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||||
56;
|
56;
|
||||||
|
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||||
|
64;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_object_store_offset = 40;
|
AOT_IsolateGroup_object_store_offset = 40;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -7952,8 +7952,8 @@ static constexpr dart::compiler::target::word
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||||
72;
|
80;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
|
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 40;
|
||||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||||
24;
|
24;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -8550,11 +8550,11 @@ static constexpr dart::compiler::target::word AOT_ICData_owner_offset = 32;
|
||||||
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 40;
|
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 40;
|
||||||
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||||
40;
|
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
|
||||||
48;
|
48;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||||
56;
|
56;
|
||||||
|
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||||
|
64;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_object_store_offset = 40;
|
AOT_IsolateGroup_object_store_offset = 40;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -8562,8 +8562,8 @@ static constexpr dart::compiler::target::word
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||||
72;
|
80;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
|
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 40;
|
||||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||||
16;
|
16;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -9159,11 +9159,11 @@ static constexpr dart::compiler::target::word AOT_ICData_owner_offset = 32;
|
||||||
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 40;
|
static constexpr dart::compiler::target::word AOT_ICData_state_bits_offset = 40;
|
||||||
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
static constexpr dart::compiler::target::word AOT_Int32x4_value_offset = 8;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||||
40;
|
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
|
||||||
48;
|
48;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||||
56;
|
56;
|
||||||
|
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||||
|
64;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_object_store_offset = 40;
|
AOT_IsolateGroup_object_store_offset = 40;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
@ -9171,8 +9171,8 @@ static constexpr dart::compiler::target::word
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||||
72;
|
80;
|
||||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
|
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 40;
|
||||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||||
16;
|
16;
|
||||||
static constexpr dart::compiler::target::word
|
static constexpr dart::compiler::target::word
|
||||||
|
|
|
@ -2364,6 +2364,55 @@ void Isolate::set_current_allocation_sample_block(SampleBlock* current) {
|
||||||
}
|
}
|
||||||
current_allocation_sample_block_ = current;
|
current_allocation_sample_block_ = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Isolate::FreeSampleBlock(SampleBlock* block) {
|
||||||
|
if (block == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SampleBlock* head;
|
||||||
|
// We're pushing the freed sample block to the front of the free_block_list_,
|
||||||
|
// which means the last element of the list will be the oldest freed sample.
|
||||||
|
do {
|
||||||
|
head = free_block_list_.load(std::memory_order_acquire);
|
||||||
|
block->next_free_ = head;
|
||||||
|
} while (!free_block_list_.compare_exchange_weak(head, block,
|
||||||
|
std::memory_order_release));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Isolate::ProcessFreeSampleBlocks(Thread* thread) {
|
||||||
|
SampleBlock* head = free_block_list_.exchange(nullptr);
|
||||||
|
// Reverse the list before processing so older blocks are streamed and reused
|
||||||
|
// first.
|
||||||
|
SampleBlock* reversed_head = nullptr;
|
||||||
|
while (head != nullptr) {
|
||||||
|
SampleBlock* next = head->next_free_;
|
||||||
|
if (reversed_head == nullptr) {
|
||||||
|
reversed_head = head;
|
||||||
|
reversed_head->next_free_ = nullptr;
|
||||||
|
} else {
|
||||||
|
head->next_free_ = reversed_head;
|
||||||
|
reversed_head = head;
|
||||||
|
}
|
||||||
|
head = next;
|
||||||
|
}
|
||||||
|
head = reversed_head;
|
||||||
|
while (head != nullptr) {
|
||||||
|
if (Service::profiler_stream.enabled() && !IsSystemIsolate(this)) {
|
||||||
|
StackZone zone(thread);
|
||||||
|
HandleScope handle_scope(thread);
|
||||||
|
Profile profile;
|
||||||
|
profile.Build(thread, nullptr, head);
|
||||||
|
ServiceEvent event(this, ServiceEvent::kCpuSamples);
|
||||||
|
event.set_cpu_profile(&profile);
|
||||||
|
Service::HandleEvent(&event);
|
||||||
|
}
|
||||||
|
SampleBlock* next = head->next_free_;
|
||||||
|
head->next_free_ = nullptr;
|
||||||
|
head->evictable_ = true;
|
||||||
|
Profiler::sample_block_buffer()->FreeBlock(head);
|
||||||
|
head = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // !defined(PRODUCT)
|
#endif // !defined(PRODUCT)
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -2464,6 +2513,25 @@ void Isolate::Shutdown() {
|
||||||
ServiceIsolate::SendIsolateShutdownMessage();
|
ServiceIsolate::SendIsolateShutdownMessage();
|
||||||
#if !defined(PRODUCT)
|
#if !defined(PRODUCT)
|
||||||
debugger()->Shutdown();
|
debugger()->Shutdown();
|
||||||
|
// Cleanup profiler state.
|
||||||
|
SampleBlock* cpu_block = current_sample_block();
|
||||||
|
if (cpu_block != nullptr) {
|
||||||
|
cpu_block->release_block();
|
||||||
|
}
|
||||||
|
SampleBlock* allocation_block = current_allocation_sample_block();
|
||||||
|
if (allocation_block != nullptr) {
|
||||||
|
allocation_block->release_block();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the previously assigned sample blocks if we're using the
|
||||||
|
// profiler's sample buffer. Some tests create their own SampleBlockBuffer
|
||||||
|
// and handle block processing themselves.
|
||||||
|
if ((cpu_block != nullptr || allocation_block != nullptr) &&
|
||||||
|
Profiler::sample_block_buffer() != nullptr) {
|
||||||
|
StackZone zone(thread);
|
||||||
|
HandleScope handle_scope(thread);
|
||||||
|
Profiler::sample_block_buffer()->ProcessCompletedBlocks();
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2522,26 +2590,6 @@ void Isolate::LowLevelCleanup(Isolate* isolate) {
|
||||||
// requests anymore.
|
// requests anymore.
|
||||||
Thread::ExitIsolate();
|
Thread::ExitIsolate();
|
||||||
|
|
||||||
#if !defined(PRODUCT)
|
|
||||||
// Cleanup profiler state.
|
|
||||||
SampleBlock* cpu_block = isolate->current_sample_block();
|
|
||||||
if (cpu_block != nullptr) {
|
|
||||||
cpu_block->release_block();
|
|
||||||
}
|
|
||||||
SampleBlock* allocation_block = isolate->current_allocation_sample_block();
|
|
||||||
if (allocation_block != nullptr) {
|
|
||||||
allocation_block->release_block();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process the previously assigned sample blocks if we're using the
|
|
||||||
// profiler's sample buffer. Some tests create their own SampleBlockBuffer
|
|
||||||
// and handle block processing themselves.
|
|
||||||
if ((cpu_block != nullptr || allocation_block != nullptr) &&
|
|
||||||
Profiler::sample_block_buffer() != nullptr) {
|
|
||||||
Profiler::sample_block_buffer()->ProcessCompletedBlocks();
|
|
||||||
}
|
|
||||||
#endif // !defined(PRODUCT)
|
|
||||||
|
|
||||||
// Now it's safe to delete the isolate.
|
// Now it's safe to delete the isolate.
|
||||||
delete isolate;
|
delete isolate;
|
||||||
|
|
||||||
|
|
|
@ -1099,6 +1099,13 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
||||||
SampleBlock* current_sample_block() const { return current_sample_block_; }
|
SampleBlock* current_sample_block() const { return current_sample_block_; }
|
||||||
void set_current_sample_block(SampleBlock* current);
|
void set_current_sample_block(SampleBlock* current);
|
||||||
|
|
||||||
|
void FreeSampleBlock(SampleBlock* block);
|
||||||
|
void ProcessFreeSampleBlocks(Thread* thread);
|
||||||
|
bool should_process_blocks() const {
|
||||||
|
return free_block_list_.load(std::memory_order_relaxed) != nullptr;
|
||||||
|
}
|
||||||
|
std::atomic<SampleBlock*> free_block_list_ = nullptr;
|
||||||
|
|
||||||
// Returns the current SampleBlock used to track Dart allocation samples.
|
// Returns the current SampleBlock used to track Dart allocation samples.
|
||||||
//
|
//
|
||||||
// Allocations should only occur on the mutator thread for an isolate, so we
|
// Allocations should only occur on the mutator thread for an isolate, so we
|
||||||
|
|
|
@ -12788,7 +12788,6 @@ static void AddScriptIfUnique(const GrowableObjectArray& scripts,
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayPtr Library::LoadedScripts() const {
|
ArrayPtr Library::LoadedScripts() const {
|
||||||
ASSERT(Thread::Current()->IsMutatorThread());
|
|
||||||
// We compute the list of loaded scripts lazily. The result is
|
// We compute the list of loaded scripts lazily. The result is
|
||||||
// cached in loaded_scripts_.
|
// cached in loaded_scripts_.
|
||||||
if (loaded_scripts() == Array::null()) {
|
if (loaded_scripts() == Array::null()) {
|
||||||
|
|
|
@ -66,6 +66,13 @@ SampleBlockBuffer* Profiler::sample_block_buffer_ = nullptr;
|
||||||
AllocationSampleBuffer* Profiler::allocation_sample_buffer_ = nullptr;
|
AllocationSampleBuffer* Profiler::allocation_sample_buffer_ = nullptr;
|
||||||
ProfilerCounters Profiler::counters_ = {};
|
ProfilerCounters Profiler::counters_ = {};
|
||||||
|
|
||||||
|
bool SampleBlockProcessor::initialized_ = false;
|
||||||
|
bool SampleBlockProcessor::shutdown_ = false;
|
||||||
|
bool SampleBlockProcessor::thread_running_ = false;
|
||||||
|
ThreadJoinId SampleBlockProcessor::processor_thread_id_ =
|
||||||
|
OSThread::kInvalidThreadJoinId;
|
||||||
|
Monitor* SampleBlockProcessor::monitor_ = nullptr;
|
||||||
|
|
||||||
void Profiler::Init() {
|
void Profiler::Init() {
|
||||||
// Place some sane restrictions on user controlled flags.
|
// Place some sane restrictions on user controlled flags.
|
||||||
SetSampleDepth(FLAG_max_profile_depth);
|
SetSampleDepth(FLAG_max_profile_depth);
|
||||||
|
@ -83,6 +90,8 @@ void Profiler::Init() {
|
||||||
}
|
}
|
||||||
ThreadInterrupter::Init();
|
ThreadInterrupter::Init();
|
||||||
ThreadInterrupter::Startup();
|
ThreadInterrupter::Startup();
|
||||||
|
SampleBlockProcessor::Init();
|
||||||
|
SampleBlockProcessor::Startup();
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +122,7 @@ void Profiler::Cleanup() {
|
||||||
}
|
}
|
||||||
ASSERT(initialized_);
|
ASSERT(initialized_);
|
||||||
ThreadInterrupter::Cleanup();
|
ThreadInterrupter::Cleanup();
|
||||||
|
SampleBlockProcessor::Cleanup();
|
||||||
SampleBlockCleanupVisitor visitor;
|
SampleBlockCleanupVisitor visitor;
|
||||||
Isolate::VisitIsolates(&visitor);
|
Isolate::VisitIsolates(&visitor);
|
||||||
initialized_ = false;
|
initialized_ = false;
|
||||||
|
@ -223,21 +233,9 @@ SampleBlock* SampleBlockBuffer::ReserveSampleBlock() {
|
||||||
|
|
||||||
void SampleBlockBuffer::ProcessCompletedBlocks() {
|
void SampleBlockBuffer::ProcessCompletedBlocks() {
|
||||||
Thread* thread = Thread::Current();
|
Thread* thread = Thread::Current();
|
||||||
|
DisableThreadInterruptsScope dtis(thread);
|
||||||
int64_t start = Dart_TimelineGetMicros();
|
int64_t start = Dart_TimelineGetMicros();
|
||||||
for (intptr_t i = 0; i < capacity_; ++i) {
|
thread->isolate()->ProcessFreeSampleBlocks(thread);
|
||||||
SampleBlock* block = &blocks_[i];
|
|
||||||
if (block->is_full() && !block->evictable()) {
|
|
||||||
if (Service::profiler_stream.enabled()) {
|
|
||||||
Profile profile(block->owner());
|
|
||||||
profile.Build(thread, nullptr, block);
|
|
||||||
ServiceEvent event(block->owner(), ServiceEvent::kCpuSamples);
|
|
||||||
event.set_cpu_profile(&profile);
|
|
||||||
Service::HandleEvent(&event);
|
|
||||||
}
|
|
||||||
block->evictable_ = true;
|
|
||||||
FreeBlock(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int64_t end = Dart_TimelineGetMicros();
|
int64_t end = Dart_TimelineGetMicros();
|
||||||
Dart_TimelineEvent("SampleBlockBuffer::ProcessCompletedBlocks", start, end,
|
Dart_TimelineEvent("SampleBlockBuffer::ProcessCompletedBlocks", start, end,
|
||||||
Dart_Timeline_Event_Duration, 0, nullptr, nullptr);
|
Dart_Timeline_Event_Duration, 0, nullptr, nullptr);
|
||||||
|
@ -306,12 +304,6 @@ Sample* SampleBlockBuffer::ReserveSampleImpl(Isolate* isolate,
|
||||||
Sample* sample = nullptr;
|
Sample* sample = nullptr;
|
||||||
if (block != nullptr) {
|
if (block != nullptr) {
|
||||||
sample = block->ReserveSample();
|
sample = block->ReserveSample();
|
||||||
if (sample != nullptr && block->is_full()) {
|
|
||||||
// TODO(bkonyi): remove once streaming is re-enabled.
|
|
||||||
// https://github.com/dart-lang/sdk/issues/46825
|
|
||||||
block->evictable_ = true;
|
|
||||||
FreeBlock(block);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (sample != nullptr) {
|
if (sample != nullptr) {
|
||||||
return sample;
|
return sample;
|
||||||
|
@ -326,6 +318,7 @@ Sample* SampleBlockBuffer::ReserveSampleImpl(Isolate* isolate,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
isolate->set_current_allocation_sample_block(next);
|
isolate->set_current_allocation_sample_block(next);
|
||||||
|
isolate->FreeSampleBlock(block);
|
||||||
} else {
|
} else {
|
||||||
MutexLocker locker(isolate->current_sample_block_lock());
|
MutexLocker locker(isolate->current_sample_block_lock());
|
||||||
next = ReserveSampleBlock();
|
next = ReserveSampleBlock();
|
||||||
|
@ -334,13 +327,14 @@ Sample* SampleBlockBuffer::ReserveSampleImpl(Isolate* isolate,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
isolate->set_current_sample_block(next);
|
isolate->set_current_sample_block(next);
|
||||||
|
isolate->FreeSampleBlock(block);
|
||||||
}
|
}
|
||||||
next->set_is_allocation_block(allocation_sample);
|
next->set_is_allocation_block(allocation_sample);
|
||||||
|
|
||||||
can_process_block_.store(true);
|
bool scheduled = can_process_block_.exchange(true);
|
||||||
// TODO(bkonyi): re-enable after block streaming is fixed.
|
if (!scheduled) {
|
||||||
// See https://github.com/dart-lang/sdk/issues/46825
|
isolate->mutator_thread()->ScheduleInterrupts(Thread::kVMInterrupt);
|
||||||
// isolate->mutator_thread()->ScheduleInterrupts(Thread::kVMInterrupt);
|
}
|
||||||
return ReserveSampleImpl(isolate, allocation_sample);
|
return ReserveSampleImpl(isolate, allocation_sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1825,6 +1819,94 @@ ProcessedSampleBuffer::ProcessedSampleBuffer()
|
||||||
ASSERT(code_lookup_table_ != NULL);
|
ASSERT(code_lookup_table_ != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SampleBlockProcessor::Init() {
|
||||||
|
ASSERT(!initialized_);
|
||||||
|
if (monitor_ == nullptr) {
|
||||||
|
monitor_ = new Monitor();
|
||||||
|
}
|
||||||
|
ASSERT(monitor_ != nullptr);
|
||||||
|
initialized_ = true;
|
||||||
|
shutdown_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SampleBlockProcessor::Startup() {
|
||||||
|
ASSERT(initialized_);
|
||||||
|
ASSERT(processor_thread_id_ == OSThread::kInvalidThreadJoinId);
|
||||||
|
MonitorLocker startup_ml(monitor_);
|
||||||
|
OSThread::Start("Dart Profiler SampleBlockProcessor", ThreadMain, 0);
|
||||||
|
while (!thread_running_) {
|
||||||
|
startup_ml.Wait();
|
||||||
|
}
|
||||||
|
ASSERT(processor_thread_id_ != OSThread::kInvalidThreadJoinId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SampleBlockProcessor::Cleanup() {
|
||||||
|
{
|
||||||
|
MonitorLocker shutdown_ml(monitor_);
|
||||||
|
if (shutdown_) {
|
||||||
|
// Already shutdown.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shutdown_ = true;
|
||||||
|
// Notify.
|
||||||
|
shutdown_ml.Notify();
|
||||||
|
ASSERT(initialized_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Join the thread.
|
||||||
|
ASSERT(processor_thread_id_ != OSThread::kInvalidThreadJoinId);
|
||||||
|
OSThread::Join(processor_thread_id_);
|
||||||
|
processor_thread_id_ = OSThread::kInvalidThreadJoinId;
|
||||||
|
initialized_ = false;
|
||||||
|
ASSERT(!thread_running_);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SampleBlockProcessorVisitor : public IsolateVisitor {
|
||||||
|
public:
|
||||||
|
SampleBlockProcessorVisitor() = default;
|
||||||
|
virtual ~SampleBlockProcessorVisitor() = default;
|
||||||
|
|
||||||
|
void VisitIsolate(Isolate* isolate) {
|
||||||
|
if (!isolate->should_process_blocks()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Thread::EnterIsolateAsHelper(isolate, Thread::kSampleBlockTask);
|
||||||
|
Thread* thread = Thread::Current();
|
||||||
|
{
|
||||||
|
DisableThreadInterruptsScope dtis(thread);
|
||||||
|
isolate->ProcessFreeSampleBlocks(thread);
|
||||||
|
}
|
||||||
|
Thread::ExitIsolateAsHelper();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void SampleBlockProcessor::ThreadMain(uword parameters) {
|
||||||
|
ASSERT(initialized_);
|
||||||
|
{
|
||||||
|
// Signal to main thread we are ready.
|
||||||
|
MonitorLocker startup_ml(monitor_);
|
||||||
|
OSThread* os_thread = OSThread::Current();
|
||||||
|
ASSERT(os_thread != NULL);
|
||||||
|
processor_thread_id_ = OSThread::GetCurrentThreadJoinId(os_thread);
|
||||||
|
thread_running_ = true;
|
||||||
|
startup_ml.Notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleBlockProcessorVisitor visitor;
|
||||||
|
MonitorLocker wait_ml(monitor_);
|
||||||
|
// Wakeup every 100ms.
|
||||||
|
const int64_t wakeup_interval = 1000 * 100;
|
||||||
|
while (true) {
|
||||||
|
wait_ml.WaitMicros(wakeup_interval);
|
||||||
|
if (shutdown_) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Isolate::VisitIsolates(&visitor);
|
||||||
|
}
|
||||||
|
// Signal to main thread we are exiting.
|
||||||
|
thread_running_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // !PRODUCT
|
#endif // !PRODUCT
|
||||||
|
|
||||||
} // namespace dart
|
} // namespace dart
|
||||||
|
|
|
@ -73,6 +73,9 @@ class Profiler : public AllStatic {
|
||||||
static SampleBlockBuffer* sample_block_buffer() {
|
static SampleBlockBuffer* sample_block_buffer() {
|
||||||
return sample_block_buffer_;
|
return sample_block_buffer_;
|
||||||
}
|
}
|
||||||
|
static void set_sample_block_buffer(SampleBlockBuffer* buffer) {
|
||||||
|
sample_block_buffer_ = buffer;
|
||||||
|
}
|
||||||
static AllocationSampleBuffer* allocation_sample_buffer() {
|
static AllocationSampleBuffer* allocation_sample_buffer() {
|
||||||
return allocation_sample_buffer_;
|
return allocation_sample_buffer_;
|
||||||
}
|
}
|
||||||
|
@ -408,9 +411,9 @@ class Sample {
|
||||||
kTruncatedTraceBit = 5,
|
kTruncatedTraceBit = 5,
|
||||||
kClassAllocationSampleBit = 6,
|
kClassAllocationSampleBit = 6,
|
||||||
kContinuationSampleBit = 7,
|
kContinuationSampleBit = 7,
|
||||||
kThreadTaskBit = 8, // 6 bits.
|
kThreadTaskBit = 8, // 7 bits.
|
||||||
kMetadataBit = 14, // 16 bits.
|
kMetadataBit = 15, // 16 bits.
|
||||||
kNextFreeBit = 30,
|
kNextFreeBit = 31,
|
||||||
};
|
};
|
||||||
class HeadSampleBit : public BitField<uint32_t, bool, kHeadSampleBit, 1> {};
|
class HeadSampleBit : public BitField<uint32_t, bool, kHeadSampleBit, 1> {};
|
||||||
class LeafFrameIsDart
|
class LeafFrameIsDart
|
||||||
|
@ -426,7 +429,7 @@ class Sample {
|
||||||
class ContinuationSampleBit
|
class ContinuationSampleBit
|
||||||
: public BitField<uint32_t, bool, kContinuationSampleBit, 1> {};
|
: public BitField<uint32_t, bool, kContinuationSampleBit, 1> {};
|
||||||
class ThreadTaskBit
|
class ThreadTaskBit
|
||||||
: public BitField<uint32_t, Thread::TaskKind, kThreadTaskBit, 6> {};
|
: public BitField<uint32_t, Thread::TaskKind, kThreadTaskBit, 7> {};
|
||||||
class MetadataBits : public BitField<uint32_t, intptr_t, kMetadataBit, 16> {};
|
class MetadataBits : public BitField<uint32_t, intptr_t, kMetadataBit, 16> {};
|
||||||
|
|
||||||
int64_t timestamp_;
|
int64_t timestamp_;
|
||||||
|
@ -710,7 +713,7 @@ class SampleBuffer : public ProcessedSampleBufferBuilder {
|
||||||
class SampleBlock : public SampleBuffer {
|
class SampleBlock : public SampleBuffer {
|
||||||
public:
|
public:
|
||||||
// The default number of samples per block. Overridden by some tests.
|
// The default number of samples per block. Overridden by some tests.
|
||||||
static const intptr_t kSamplesPerBlock = 1000;
|
static const intptr_t kSamplesPerBlock = 100;
|
||||||
|
|
||||||
SampleBlock() = default;
|
SampleBlock() = default;
|
||||||
virtual ~SampleBlock() = default;
|
virtual ~SampleBlock() = default;
|
||||||
|
@ -765,13 +768,14 @@ class SampleBlock : public SampleBuffer {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class SampleBlockBuffer;
|
friend class SampleBlockBuffer;
|
||||||
|
friend class Isolate;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(SampleBlock);
|
DISALLOW_COPY_AND_ASSIGN(SampleBlock);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SampleBlockBuffer : public ProcessedSampleBufferBuilder {
|
class SampleBlockBuffer : public ProcessedSampleBufferBuilder {
|
||||||
public:
|
public:
|
||||||
static const intptr_t kDefaultBlockCount = 60;
|
static const intptr_t kDefaultBlockCount = 600;
|
||||||
|
|
||||||
// Creates a SampleBlockBuffer with a predetermined number of blocks.
|
// Creates a SampleBlockBuffer with a predetermined number of blocks.
|
||||||
//
|
//
|
||||||
|
@ -864,6 +868,8 @@ class SampleBlockBuffer : public ProcessedSampleBufferBuilder {
|
||||||
// Sample buffer management.
|
// Sample buffer management.
|
||||||
VirtualMemory* memory_;
|
VirtualMemory* memory_;
|
||||||
Sample* sample_buffer_;
|
Sample* sample_buffer_;
|
||||||
|
|
||||||
|
friend class Isolate;
|
||||||
DISALLOW_COPY_AND_ASSIGN(SampleBlockBuffer);
|
DISALLOW_COPY_AND_ASSIGN(SampleBlockBuffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1037,6 +1043,24 @@ class ProcessedSampleBuffer : public ZoneAllocated {
|
||||||
DISALLOW_COPY_AND_ASSIGN(ProcessedSampleBuffer);
|
DISALLOW_COPY_AND_ASSIGN(ProcessedSampleBuffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SampleBlockProcessor : public AllStatic {
|
||||||
|
public:
|
||||||
|
static void Init();
|
||||||
|
|
||||||
|
static void Startup();
|
||||||
|
static void Cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const intptr_t kMaxThreads = 4096;
|
||||||
|
static bool initialized_;
|
||||||
|
static bool shutdown_;
|
||||||
|
static bool thread_running_;
|
||||||
|
static ThreadJoinId processor_thread_id_;
|
||||||
|
static Monitor* monitor_;
|
||||||
|
|
||||||
|
static void ThreadMain(uword parameters);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace dart
|
} // namespace dart
|
||||||
|
|
||||||
#endif // RUNTIME_VM_PROFILER_H_
|
#endif // RUNTIME_VM_PROFILER_H_
|
||||||
|
|
|
@ -1450,9 +1450,8 @@ class ProfileBuilder : public ValueObject {
|
||||||
ProfileInfoKind info_kind_;
|
ProfileInfoKind info_kind_;
|
||||||
}; // ProfileBuilder.
|
}; // ProfileBuilder.
|
||||||
|
|
||||||
Profile::Profile(Isolate* isolate)
|
Profile::Profile()
|
||||||
: isolate_(isolate),
|
: zone_(Thread::Current()->zone()),
|
||||||
zone_(Thread::Current()->zone()),
|
|
||||||
samples_(NULL),
|
samples_(NULL),
|
||||||
live_code_(NULL),
|
live_code_(NULL),
|
||||||
dead_code_(NULL),
|
dead_code_(NULL),
|
||||||
|
@ -1461,9 +1460,7 @@ Profile::Profile(Isolate* isolate)
|
||||||
dead_code_index_offset_(-1),
|
dead_code_index_offset_(-1),
|
||||||
tag_code_index_offset_(-1),
|
tag_code_index_offset_(-1),
|
||||||
min_time_(kMaxInt64),
|
min_time_(kMaxInt64),
|
||||||
max_time_(0) {
|
max_time_(0) {}
|
||||||
ASSERT(isolate_ != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Profile::Build(Thread* thread,
|
void Profile::Build(Thread* thread,
|
||||||
SampleFilter* filter,
|
SampleFilter* filter,
|
||||||
|
@ -1765,14 +1762,12 @@ void ProfilerService::PrintJSONImpl(Thread* thread,
|
||||||
SampleFilter* filter,
|
SampleFilter* filter,
|
||||||
ProcessedSampleBufferBuilder* buffer,
|
ProcessedSampleBufferBuilder* buffer,
|
||||||
bool include_code_samples) {
|
bool include_code_samples) {
|
||||||
Isolate* isolate = thread->isolate();
|
|
||||||
|
|
||||||
// We should bail out in service.cc if the profiler is disabled.
|
// We should bail out in service.cc if the profiler is disabled.
|
||||||
ASSERT(buffer != nullptr);
|
ASSERT(buffer != nullptr);
|
||||||
|
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
profile.Build(thread, filter, buffer);
|
profile.Build(thread, filter, buffer);
|
||||||
profile.PrintProfileJSON(stream, include_code_samples);
|
profile.PrintProfileJSON(stream, include_code_samples);
|
||||||
}
|
}
|
||||||
|
|
|
@ -364,7 +364,7 @@ class ProfileCodeTable : public ZoneAllocated {
|
||||||
// a zone must be created that lives longer than this object.
|
// a zone must be created that lives longer than this object.
|
||||||
class Profile : public ValueObject {
|
class Profile : public ValueObject {
|
||||||
public:
|
public:
|
||||||
explicit Profile(Isolate* isolate);
|
Profile();
|
||||||
|
|
||||||
// Build a filtered model using |filter|.
|
// Build a filtered model using |filter|.
|
||||||
void Build(Thread* thread,
|
void Build(Thread* thread,
|
||||||
|
@ -403,7 +403,6 @@ class Profile : public ValueObject {
|
||||||
intptr_t frame_index);
|
intptr_t frame_index);
|
||||||
void PrintSamplesJSON(JSONObject* obj, bool code_samples);
|
void PrintSamplesJSON(JSONObject* obj, bool code_samples);
|
||||||
|
|
||||||
Isolate* isolate_;
|
|
||||||
Zone* zone_;
|
Zone* zone_;
|
||||||
ProcessedSampleBuffer* samples_;
|
ProcessedSampleBuffer* samples_;
|
||||||
ProfileCodeTable* live_code_;
|
ProfileCodeTable* live_code_;
|
||||||
|
|
|
@ -102,9 +102,30 @@ void VisitSamples(SampleBlockBuffer* buffer, SampleVisitor* visitor) {
|
||||||
buffer->VisitSamples(visitor);
|
buffer->VisitSamples(visitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SampleBlockBufferOverrideScope {
|
||||||
|
public:
|
||||||
|
explicit SampleBlockBufferOverrideScope(SampleBlockBuffer* buffer)
|
||||||
|
: override_(buffer) {
|
||||||
|
orig_ = Profiler::sample_block_buffer();
|
||||||
|
Profiler::set_sample_block_buffer(override_);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SampleBlockBufferOverrideScope() {
|
||||||
|
Profiler::set_sample_block_buffer(orig_);
|
||||||
|
delete override_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
SampleBlockBuffer* orig_;
|
||||||
|
SampleBlockBuffer* override_;
|
||||||
|
};
|
||||||
|
|
||||||
TEST_CASE(Profiler_SampleBufferWrapTest) {
|
TEST_CASE(Profiler_SampleBufferWrapTest) {
|
||||||
Isolate* isolate = Isolate::Current();
|
Isolate* isolate = Isolate::Current();
|
||||||
SampleBlockBuffer* sample_buffer = new SampleBlockBuffer(3, 1);
|
|
||||||
|
SampleBlockBufferOverrideScope sbbos(new SampleBlockBuffer(3, 1));
|
||||||
|
SampleBlockBuffer* sample_buffer = Profiler::sample_block_buffer();
|
||||||
|
|
||||||
Dart_Port i = 123;
|
Dart_Port i = 123;
|
||||||
ProfileSampleBufferTestHelper visitor(i);
|
ProfileSampleBufferTestHelper visitor(i);
|
||||||
|
|
||||||
|
@ -142,12 +163,14 @@ TEST_CASE(Profiler_SampleBufferWrapTest) {
|
||||||
MutexLocker ml(isolate->current_sample_block_lock());
|
MutexLocker ml(isolate->current_sample_block_lock());
|
||||||
isolate->set_current_sample_block(nullptr);
|
isolate->set_current_sample_block(nullptr);
|
||||||
}
|
}
|
||||||
delete sample_buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(Profiler_SampleBufferIterateTest) {
|
TEST_CASE(Profiler_SampleBufferIterateTest) {
|
||||||
Isolate* isolate = Isolate::Current();
|
Isolate* isolate = Isolate::Current();
|
||||||
SampleBlockBuffer* sample_buffer = new SampleBlockBuffer(3, 1);
|
|
||||||
|
SampleBlockBufferOverrideScope sbbos(new SampleBlockBuffer(3, 1));
|
||||||
|
SampleBlockBuffer* sample_buffer = Profiler::sample_block_buffer();
|
||||||
|
|
||||||
Dart_Port i = 123;
|
Dart_Port i = 123;
|
||||||
ProfileSampleBufferTestHelper visitor(i);
|
ProfileSampleBufferTestHelper visitor(i);
|
||||||
|
|
||||||
|
@ -182,7 +205,6 @@ TEST_CASE(Profiler_SampleBufferIterateTest) {
|
||||||
MutexLocker ml(isolate->current_sample_block_lock());
|
MutexLocker ml(isolate->current_sample_block_lock());
|
||||||
isolate->set_current_sample_block(nullptr);
|
isolate->set_current_sample_block(nullptr);
|
||||||
}
|
}
|
||||||
delete sample_buffer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE(Profiler_AllocationSampleTest) {
|
TEST_CASE(Profiler_AllocationSampleTest) {
|
||||||
|
@ -480,7 +502,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TrivialRecordAllocation) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
// Filter for the class in the time range.
|
// Filter for the class in the time range.
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id(),
|
AllocationFilter filter(isolate->main_port(), class_a.id(),
|
||||||
before_allocations_micros,
|
before_allocations_micros,
|
||||||
|
@ -510,7 +532,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TrivialRecordAllocation) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id(),
|
AllocationFilter filter(isolate->main_port(), class_a.id(),
|
||||||
Dart_TimelineGetMicros(), 16000);
|
Dart_TimelineGetMicros(), 16000);
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
|
@ -555,10 +577,9 @@ ISOLATE_UNIT_TEST_CASE(Profiler_NativeAllocation) {
|
||||||
// with each node.
|
// with each node.
|
||||||
{
|
{
|
||||||
Thread* thread = Thread::Current();
|
Thread* thread = Thread::Current();
|
||||||
Isolate* isolate = thread->isolate();
|
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
|
|
||||||
// Filter for the class in the time range.
|
// Filter for the class in the time range.
|
||||||
NativeAllocationSampleFilter filter(before_allocations_micros,
|
NativeAllocationSampleFilter filter(before_allocations_micros,
|
||||||
|
@ -595,10 +616,9 @@ ISOLATE_UNIT_TEST_CASE(Profiler_NativeAllocation) {
|
||||||
// freed above is marked as free and is no longer reported.
|
// freed above is marked as free and is no longer reported.
|
||||||
{
|
{
|
||||||
Thread* thread = Thread::Current();
|
Thread* thread = Thread::Current();
|
||||||
Isolate* isolate = thread->isolate();
|
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
|
|
||||||
// Filter for the class in the time range.
|
// Filter for the class in the time range.
|
||||||
NativeAllocationSampleFilter filter(before_allocations_micros,
|
NativeAllocationSampleFilter filter(before_allocations_micros,
|
||||||
|
@ -611,10 +631,9 @@ ISOLATE_UNIT_TEST_CASE(Profiler_NativeAllocation) {
|
||||||
// Query with a time filter where no allocations occurred.
|
// Query with a time filter where no allocations occurred.
|
||||||
{
|
{
|
||||||
Thread* thread = Thread::Current();
|
Thread* thread = Thread::Current();
|
||||||
Isolate* isolate = thread->isolate();
|
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
NativeAllocationSampleFilter filter(Dart_TimelineGetMicros(), 16000);
|
NativeAllocationSampleFilter filter(Dart_TimelineGetMicros(), 16000);
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples because none occured within
|
// We should have no allocation samples because none occured within
|
||||||
|
@ -660,7 +679,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ToggleRecordAllocation) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -677,7 +696,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ToggleRecordAllocation) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation sample.
|
// We should have one allocation sample.
|
||||||
|
@ -707,7 +726,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ToggleRecordAllocation) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -745,7 +764,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_CodeTicks) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -765,7 +784,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_CodeTicks) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have three allocation samples.
|
// We should have three allocation samples.
|
||||||
|
@ -820,7 +839,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_FunctionTicks) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -840,7 +859,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_FunctionTicks) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have three allocation samples.
|
// We should have three allocation samples.
|
||||||
|
@ -890,7 +909,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_IntrinsicAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), double_class.id());
|
AllocationFilter filter(isolate->main_port(), double_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -903,7 +922,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_IntrinsicAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), double_class.id());
|
AllocationFilter filter(isolate->main_port(), double_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation sample.
|
// We should have one allocation sample.
|
||||||
|
@ -925,7 +944,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_IntrinsicAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), double_class.id());
|
AllocationFilter filter(isolate->main_port(), double_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -952,7 +971,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), array_class.id());
|
AllocationFilter filter(isolate->main_port(), array_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -965,7 +984,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), array_class.id());
|
AllocationFilter filter(isolate->main_port(), array_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation sample.
|
// We should have one allocation sample.
|
||||||
|
@ -987,7 +1006,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), array_class.id());
|
AllocationFilter filter(isolate->main_port(), array_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -1009,7 +1028,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), array_class.id());
|
AllocationFilter filter(isolate->main_port(), array_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples, since empty
|
// We should have no allocation samples, since empty
|
||||||
|
@ -1039,7 +1058,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ContextAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), context_class.id());
|
AllocationFilter filter(isolate->main_port(), context_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -1052,7 +1071,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ContextAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), context_class.id());
|
AllocationFilter filter(isolate->main_port(), context_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation sample.
|
// We should have one allocation sample.
|
||||||
|
@ -1072,7 +1091,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ContextAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), context_class.id());
|
AllocationFilter filter(isolate->main_port(), context_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -1112,7 +1131,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ClosureAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), closure_class.id());
|
AllocationFilter filter(isolate->main_port(), closure_class.id());
|
||||||
filter.set_enable_vm_ticks(true);
|
filter.set_enable_vm_ticks(true);
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
|
@ -1136,7 +1155,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ClosureAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), closure_class.id());
|
AllocationFilter filter(isolate->main_port(), closure_class.id());
|
||||||
filter.set_enable_vm_ticks(true);
|
filter.set_enable_vm_ticks(true);
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
|
@ -1167,7 +1186,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -1180,7 +1199,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation sample.
|
// We should have one allocation sample.
|
||||||
|
@ -1202,7 +1221,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -1215,7 +1234,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should now have two allocation samples.
|
// We should now have two allocation samples.
|
||||||
|
@ -1247,7 +1266,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -1260,7 +1279,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -1280,7 +1299,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -1293,7 +1312,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should now have two allocation samples.
|
// We should now have two allocation samples.
|
||||||
|
@ -1325,7 +1344,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -1338,7 +1357,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -1364,7 +1383,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should still only have one allocation sample.
|
// We should still only have one allocation sample.
|
||||||
|
@ -1377,7 +1396,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
||||||
{
|
{
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should now have two allocation samples.
|
// We should now have two allocation samples.
|
||||||
|
@ -1433,7 +1452,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_FunctionInline) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -1451,7 +1470,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_FunctionInline) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have 50,000 allocation samples.
|
// We should have 50,000 allocation samples.
|
||||||
|
@ -1580,7 +1599,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_InliningIntervalBoundry) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have no allocation samples.
|
// We should have no allocation samples.
|
||||||
|
@ -1597,7 +1616,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_InliningIntervalBoundry) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
EXPECT_EQ(1, profile.sample_count());
|
EXPECT_EQ(1, profile.sample_count());
|
||||||
|
@ -1673,7 +1692,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ChainedSamples) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have 1 allocation sample.
|
// We should have 1 allocation sample.
|
||||||
|
@ -1768,7 +1787,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_BasicSourcePosition) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation samples.
|
// We should have one allocation samples.
|
||||||
|
@ -1850,7 +1869,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_BasicSourcePositionOptimized) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation samples.
|
// We should have one allocation samples.
|
||||||
|
@ -1928,7 +1947,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_SourcePosition) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation samples.
|
// We should have one allocation samples.
|
||||||
|
@ -2038,7 +2057,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_SourcePositionOptimized) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation samples.
|
// We should have one allocation samples.
|
||||||
|
@ -2133,7 +2152,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_BinaryOperatorSourcePosition) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation samples.
|
// We should have one allocation samples.
|
||||||
|
@ -2251,7 +2270,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_BinaryOperatorSourcePositionOptimized) {
|
||||||
Isolate* isolate = thread->isolate();
|
Isolate* isolate = thread->isolate();
|
||||||
StackZone zone(thread);
|
StackZone zone(thread);
|
||||||
HANDLESCOPE(thread);
|
HANDLESCOPE(thread);
|
||||||
Profile profile(isolate);
|
Profile profile;
|
||||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||||
// We should have one allocation samples.
|
// We should have one allocation samples.
|
||||||
|
|
|
@ -29,7 +29,6 @@ SourceReport::SourceReport(intptr_t report_set, CompileMode compile_mode)
|
||||||
script_(NULL),
|
script_(NULL),
|
||||||
start_pos_(TokenPosition::kMinSource),
|
start_pos_(TokenPosition::kMinSource),
|
||||||
end_pos_(TokenPosition::kMaxSource),
|
end_pos_(TokenPosition::kMaxSource),
|
||||||
profile_(Isolate::Current()),
|
|
||||||
next_script_index_(0) {}
|
next_script_index_(0) {}
|
||||||
|
|
||||||
SourceReport::~SourceReport() {
|
SourceReport::~SourceReport() {
|
||||||
|
|
|
@ -450,11 +450,15 @@ ErrorPtr Thread::HandleInterrupts() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#if !defined(PRODUCT)
|
#if !defined(PRODUCT)
|
||||||
// Processes completed SampleBlocks and sends CPU sample events over the
|
// Don't block system isolates to process CPU samples to avoid blocking
|
||||||
// service protocol when applicable.
|
// them during critical tasks (e.g., initial compilation).
|
||||||
SampleBlockBuffer* sample_buffer = Profiler::sample_block_buffer();
|
if (!Isolate::IsSystemIsolate(isolate())) {
|
||||||
if (sample_buffer != nullptr && sample_buffer->process_blocks()) {
|
// Processes completed SampleBlocks and sends CPU sample events over the
|
||||||
sample_buffer->ProcessCompletedBlocks();
|
// service protocol when applicable.
|
||||||
|
SampleBlockBuffer* sample_buffer = Profiler::sample_block_buffer();
|
||||||
|
if (sample_buffer != nullptr && sample_buffer->process_blocks()) {
|
||||||
|
sample_buffer->ProcessCompletedBlocks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // !defined(PRODUCT)
|
#endif // !defined(PRODUCT)
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,6 +273,7 @@ class Thread : public ThreadState {
|
||||||
kSweeperTask = 0x8,
|
kSweeperTask = 0x8,
|
||||||
kCompactorTask = 0x10,
|
kCompactorTask = 0x10,
|
||||||
kScavengerTask = 0x20,
|
kScavengerTask = 0x20,
|
||||||
|
kSampleBlockTask = 0x40,
|
||||||
};
|
};
|
||||||
// Converts a TaskKind to its corresponding C-String name.
|
// Converts a TaskKind to its corresponding C-String name.
|
||||||
static const char* TaskKindToCString(TaskKind kind);
|
static const char* TaskKindToCString(TaskKind kind);
|
||||||
|
|
Loading…
Reference in a new issue