mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +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
|
||||
cleanup after an error during initialization.
|
||||
|
||||
# 2.1.0
|
||||
- Added getAvailableCachedCpuSamples and getCachedCpuSamples.
|
||||
|
||||
# 2.0.2
|
||||
- Fix possibility of `LateInitializationError` being thrown when trying to
|
||||
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
|
||||
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].
|
||||
|
||||
### 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
|
||||
|
||||
```
|
||||
|
@ -181,6 +204,37 @@ See [Success](#success).
|
|||
|
||||
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
|
||||
|
||||
```
|
||||
|
@ -220,10 +274,12 @@ version | comments
|
|||
1.0 | Initial revision
|
||||
1.1 | Added `getDartDevelopmentServiceVersion` 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
|
||||
[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
|
||||
[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-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,
|
||||
bool enableAuthCodes = true,
|
||||
bool ipv6 = false,
|
||||
List<String> cachedUserTags = const [],
|
||||
DevToolsConfiguration? devToolsConfiguration,
|
||||
bool logRequests = false,
|
||||
}) async {
|
||||
|
@ -79,6 +80,7 @@ abstract class DartDevelopmentService {
|
|||
remoteVmServiceUri,
|
||||
serviceUri,
|
||||
enableAuthCodes,
|
||||
cachedUserTags,
|
||||
ipv6,
|
||||
devToolsConfiguration,
|
||||
logRequests,
|
||||
|
@ -136,9 +138,13 @@ abstract class DartDevelopmentService {
|
|||
/// requests.
|
||||
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]
|
||||
/// instance.
|
||||
static const String protocolVersion = '1.2';
|
||||
static const String protocolVersion = '1.3';
|
||||
}
|
||||
|
||||
class DartDevelopmentServiceException implements Exception {
|
||||
|
|
|
@ -206,6 +206,19 @@ class DartDevelopmentServiceClient {
|
|||
return supportedProtocols;
|
||||
});
|
||||
|
||||
_clientPeer.registerMethod(
|
||||
'getAvailableCachedCpuSamples',
|
||||
(_) => {
|
||||
'type': 'AvailableCachedCpuSamples',
|
||||
'cacheNames': dds.cachedUserTags,
|
||||
},
|
||||
);
|
||||
|
||||
_clientPeer.registerMethod(
|
||||
'getCachedCpuSamples',
|
||||
dds.isolateManager.getCachedCpuSamples,
|
||||
);
|
||||
|
||||
// `evaluate` and `evaluateInFrame` actually consist of multiple RPC
|
||||
// invocations, including a call to `compileExpression` which can be
|
||||
// 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.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
@ -54,6 +55,7 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService {
|
|||
this._remoteVmServiceUri,
|
||||
this._uri,
|
||||
this._authCodesEnabled,
|
||||
this._cachedUserTags,
|
||||
this._ipv6,
|
||||
this._devToolsConfiguration,
|
||||
this.shouldLogRequests,
|
||||
|
@ -388,6 +390,9 @@ class DartDevelopmentServiceImpl implements DartDevelopmentService {
|
|||
|
||||
final DevToolsConfiguration? _devToolsConfiguration;
|
||||
|
||||
List<String> get cachedUserTags => UnmodifiableListView(_cachedUserTags);
|
||||
final List<String> _cachedUserTags;
|
||||
|
||||
Future<void> get done => _done.future;
|
||||
Completer _done = Completer<void>();
|
||||
bool _initializationComplete = false;
|
||||
|
|
|
@ -2,12 +2,14 @@
|
|||
// 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/utils/mutex.dart';
|
||||
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import 'client.dart';
|
||||
import 'constants.dart';
|
||||
import 'cpu_samples_manager.dart';
|
||||
import 'dds_impl.dart';
|
||||
import 'utils/mutex.dart';
|
||||
|
||||
/// This file contains functionality used to track the running state of
|
||||
/// all isolates in a given Dart process.
|
||||
|
@ -36,7 +38,11 @@ enum _IsolateState {
|
|||
}
|
||||
|
||||
class _RunningIsolate {
|
||||
_RunningIsolate(this.isolateManager, this.id, this.name);
|
||||
_RunningIsolate(this.isolateManager, this.id, this.name)
|
||||
: cpuSamplesManager = CpuSamplesManager(
|
||||
isolateManager.dds,
|
||||
id,
|
||||
);
|
||||
|
||||
// State setters.
|
||||
void pausedOnExit() => _state = _IsolateState.pauseExit;
|
||||
|
@ -104,6 +110,29 @@ class _RunningIsolate {
|
|||
/// Should always be called after an isolate is resumed.
|
||||
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;
|
||||
|
||||
static const isolateStateToMaskMapping = {
|
||||
|
@ -113,6 +142,7 @@ class _RunningIsolate {
|
|||
};
|
||||
|
||||
final IsolateManager isolateManager;
|
||||
final CpuSamplesManager cpuSamplesManager;
|
||||
final String name;
|
||||
final String id;
|
||||
final Set<String?> _resumeApprovalsByName = {};
|
||||
|
@ -123,20 +153,25 @@ class IsolateManager {
|
|||
IsolateManager(this.dds);
|
||||
|
||||
/// Handles state changes for isolates.
|
||||
void handleIsolateEvent(json_rpc.Parameters parameters) {
|
||||
final event = parameters['event'];
|
||||
final eventKind = event['kind'].asString;
|
||||
|
||||
void handleIsolateEvent(Event event) {
|
||||
// There's no interesting information about isolate state associated with
|
||||
// and IsolateSpawn event.
|
||||
if (eventKind == ServiceEvents.isolateSpawn) {
|
||||
// TODO(bkonyi): why isn't IsolateSpawn in package:vm_service
|
||||
if (event.kind! == ServiceEvents.isolateSpawn) {
|
||||
return;
|
||||
}
|
||||
|
||||
final isolateData = event['isolate'];
|
||||
final id = isolateData['id'].asString;
|
||||
final name = isolateData['name'].asString;
|
||||
_updateIsolateState(id, name, eventKind);
|
||||
final isolateData = event.isolate!;
|
||||
final id = isolateData.id!;
|
||||
final name = isolateData.name!;
|
||||
_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) {
|
||||
|
@ -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.
|
||||
Future<Map<String, dynamic>> _sendResumeRequest(
|
||||
String isolateId,
|
||||
|
|
|
@ -2,17 +2,16 @@
|
|||
// 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';
|
||||
|
||||
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||
|
||||
import 'client.dart';
|
||||
import 'common/ring_buffer.dart';
|
||||
|
||||
/// [LoggingRepository] is used to store historical log messages from the
|
||||
/// target VM service. Clients which connect to DDS and subscribe to the
|
||||
/// `Logging` stream will be sent all messages contained within this repository
|
||||
/// upon initial subscription.
|
||||
class LoggingRepository extends _RingBuffer<Map<String, dynamic>> {
|
||||
class LoggingRepository extends RingBuffer<Map<String, dynamic>> {
|
||||
LoggingRepository([int logHistoryLength = 10000]) : super(logHistoryLength) {
|
||||
// TODO(bkonyi): enforce log history limit when DartDevelopmentService
|
||||
// allows for this to be set via Dart code.
|
||||
|
@ -45,54 +44,3 @@ class LoggingRepository extends _RingBuffer<Map<String, dynamic>> {
|
|||
final Set<DartDevelopmentServiceClient> _sentHistoricLogsClientSet = {};
|
||||
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 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import 'client.dart';
|
||||
import 'dds_impl.dart';
|
||||
|
@ -108,18 +109,31 @@ class StreamManager {
|
|||
// Stdout and Stderr streams may not exist.
|
||||
}
|
||||
}
|
||||
if (dds.cachedUserTags.isNotEmpty) {
|
||||
await streamListen(null, EventStreams.kProfiler);
|
||||
}
|
||||
dds.vmServiceClient.registerMethod(
|
||||
'streamNotify',
|
||||
(parameters) {
|
||||
(json_rpc.Parameters parameters) {
|
||||
final streamId = parameters['streamId'].asString;
|
||||
final event =
|
||||
Event.parse(parameters['event'].asMap.cast<String, dynamic>())!;
|
||||
|
||||
// Forward events from the streams IsolateManager subscribes to.
|
||||
if (isolateManagerStreams.contains(streamId)) {
|
||||
dds.isolateManager.handleIsolateEvent(parameters);
|
||||
dds.isolateManager.handleIsolateEvent(event);
|
||||
}
|
||||
// Keep a history of messages to send to clients when they first
|
||||
// subscribe to a stream with an event history.
|
||||
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);
|
||||
},
|
||||
|
@ -262,6 +276,7 @@ class StreamManager {
|
|||
static const kExtensionStream = 'Extension';
|
||||
static const kIsolateStream = 'Isolate';
|
||||
static const kLoggingStream = 'Logging';
|
||||
static const kProfilerStream = 'Profiler';
|
||||
static const kStderrStream = 'Stderr';
|
||||
static const kStdoutStream = 'Stdout';
|
||||
|
||||
|
@ -283,6 +298,12 @@ class StreamManager {
|
|||
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.
|
||||
static final ddsCoreStreams = <String>{
|
||||
...isolateManagerStreams,
|
||||
|
|
|
@ -13,18 +13,46 @@ extension DdsExtension on VmService {
|
|||
static bool _factoriesRegistered = false;
|
||||
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 result of this call is cached for subsequent invocations.
|
||||
Future<Version> getDartDevelopmentServiceVersion() async {
|
||||
if (_ddsVersion == null) {
|
||||
_ddsVersion =
|
||||
await _callHelper<Version>('getDartDevelopmentServiceVersion');
|
||||
_ddsVersion = await _callHelper<Version>(
|
||||
'getDartDevelopmentServiceVersion',
|
||||
);
|
||||
}
|
||||
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`.
|
||||
///
|
||||
/// If `stream` does not have event history collected, a parameter error is
|
||||
|
@ -126,6 +154,11 @@ extension DdsExtension on VmService {
|
|||
|
||||
static void _registerFactories() {
|
||||
addTypeFactory('StreamHistory', StreamHistory.parse);
|
||||
addTypeFactory(
|
||||
'AvailableCachedCpuSamples',
|
||||
AvailableCachedCpuSamples.parse,
|
||||
);
|
||||
addTypeFactory('CachedCpuSamples', CachedCpuSamples.parse);
|
||||
_factoriesRegistered = true;
|
||||
}
|
||||
}
|
||||
|
@ -154,3 +187,86 @@ class StreamHistory extends Response {
|
|||
List<Event> get history => UnmodifiableListView(_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
|
||||
sse: ^4.0.0
|
||||
stream_channel: ^2.0.0
|
||||
vm_service: ^7.0.0
|
||||
vm_service: ^7.2.0
|
||||
web_socket_channel: ^2.0.0
|
||||
|
||||
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
|
||||
evaluate_activation_test/instance: 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_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
|
||||
evaluate_activation_test/instance: 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_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_state_bits_offset = 28;
|
||||
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_default_tag_offset = 24;
|
||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 28;
|
||||
static constexpr dart::compiler::target::word Isolate_current_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 = 32;
|
||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||
20;
|
||||
static constexpr dart::compiler::target::word
|
||||
IsolateGroup_shared_class_table_offset = 8;
|
||||
static constexpr dart::compiler::target::word
|
||||
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_user_tag_offset = 16;
|
||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 40;
|
||||
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_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_state_bits_offset = 52;
|
||||
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_default_tag_offset = 48;
|
||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
|
||||
static constexpr dart::compiler::target::word Isolate_current_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 = 64;
|
||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||
40;
|
||||
static constexpr dart::compiler::target::word
|
||||
IsolateGroup_shared_class_table_offset = 16;
|
||||
static constexpr dart::compiler::target::word
|
||||
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_user_tag_offset = 32;
|
||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 80;
|
||||
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_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_state_bits_offset = 28;
|
||||
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_default_tag_offset = 24;
|
||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 28;
|
||||
static constexpr dart::compiler::target::word Isolate_current_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 = 32;
|
||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||
20;
|
||||
static constexpr dart::compiler::target::word
|
||||
IsolateGroup_shared_class_table_offset = 8;
|
||||
static constexpr dart::compiler::target::word
|
||||
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_user_tag_offset = 16;
|
||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 40;
|
||||
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_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_state_bits_offset = 52;
|
||||
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_default_tag_offset = 48;
|
||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
|
||||
static constexpr dart::compiler::target::word Isolate_current_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 = 64;
|
||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||
40;
|
||||
static constexpr dart::compiler::target::word
|
||||
IsolateGroup_shared_class_table_offset = 16;
|
||||
static constexpr dart::compiler::target::word
|
||||
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_user_tag_offset = 32;
|
||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 80;
|
||||
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_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_state_bits_offset = 52;
|
||||
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_default_tag_offset = 48;
|
||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
|
||||
static constexpr dart::compiler::target::word Isolate_current_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 = 64;
|
||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||
40;
|
||||
static constexpr dart::compiler::target::word
|
||||
IsolateGroup_shared_class_table_offset = 16;
|
||||
static constexpr dart::compiler::target::word
|
||||
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_user_tag_offset = 32;
|
||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 80;
|
||||
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_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_state_bits_offset = 52;
|
||||
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_default_tag_offset = 48;
|
||||
static constexpr dart::compiler::target::word Isolate_ic_miss_code_offset = 56;
|
||||
static constexpr dart::compiler::target::word Isolate_current_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 = 64;
|
||||
static constexpr dart::compiler::target::word IsolateGroup_object_store_offset =
|
||||
40;
|
||||
static constexpr dart::compiler::target::word
|
||||
IsolateGroup_shared_class_table_offset = 16;
|
||||
static constexpr dart::compiler::target::word
|
||||
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_user_tag_offset = 32;
|
||||
static constexpr dart::compiler::target::word Isolate_single_step_offset = 80;
|
||||
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_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_Int32x4_value_offset = 8;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||
20;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||
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;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||
32;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_object_store_offset = 20;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -6732,8 +6732,8 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_cached_class_table_table_offset = 16;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||
36;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 16;
|
||||
40;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 20;
|
||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||
12;
|
||||
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_Int32x4_value_offset = 8;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||
40;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||
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;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||
64;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_object_store_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -7339,8 +7339,8 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||
72;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
|
||||
80;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 40;
|
||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||
24;
|
||||
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_Int32x4_value_offset = 8;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||
40;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||
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;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||
64;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_object_store_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -7952,8 +7952,8 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||
72;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
|
||||
80;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 40;
|
||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||
24;
|
||||
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_Int32x4_value_offset = 8;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||
40;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||
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;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||
64;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_object_store_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -8562,8 +8562,8 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||
72;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
|
||||
80;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 40;
|
||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||
16;
|
||||
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_Int32x4_value_offset = 8;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_current_tag_offset =
|
||||
40;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_default_tag_offset =
|
||||
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;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_ic_miss_code_offset =
|
||||
64;
|
||||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_object_store_offset = 40;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
@ -9171,8 +9171,8 @@ static constexpr dart::compiler::target::word
|
|||
static constexpr dart::compiler::target::word
|
||||
AOT_IsolateGroup_cached_class_table_table_offset = 32;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_single_step_offset =
|
||||
72;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 32;
|
||||
80;
|
||||
static constexpr dart::compiler::target::word AOT_Isolate_user_tag_offset = 40;
|
||||
static constexpr dart::compiler::target::word AOT_LinkedHashBase_data_offset =
|
||||
16;
|
||||
static constexpr dart::compiler::target::word
|
||||
|
|
|
@ -2364,6 +2364,55 @@ void Isolate::set_current_allocation_sample_block(SampleBlock* 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)
|
||||
|
||||
// static
|
||||
|
@ -2464,6 +2513,25 @@ void Isolate::Shutdown() {
|
|||
ServiceIsolate::SendIsolateShutdownMessage();
|
||||
#if !defined(PRODUCT)
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -2522,26 +2590,6 @@ void Isolate::LowLevelCleanup(Isolate* isolate) {
|
|||
// requests anymore.
|
||||
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.
|
||||
delete isolate;
|
||||
|
||||
|
|
|
@ -1099,6 +1099,13 @@ class Isolate : public BaseIsolate, public IntrusiveDListEntry<Isolate> {
|
|||
SampleBlock* current_sample_block() const { return current_sample_block_; }
|
||||
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.
|
||||
//
|
||||
// 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 {
|
||||
ASSERT(Thread::Current()->IsMutatorThread());
|
||||
// We compute the list of loaded scripts lazily. The result is
|
||||
// cached in loaded_scripts_.
|
||||
if (loaded_scripts() == Array::null()) {
|
||||
|
|
|
@ -66,6 +66,13 @@ SampleBlockBuffer* Profiler::sample_block_buffer_ = nullptr;
|
|||
AllocationSampleBuffer* Profiler::allocation_sample_buffer_ = nullptr;
|
||||
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() {
|
||||
// Place some sane restrictions on user controlled flags.
|
||||
SetSampleDepth(FLAG_max_profile_depth);
|
||||
|
@ -83,6 +90,8 @@ void Profiler::Init() {
|
|||
}
|
||||
ThreadInterrupter::Init();
|
||||
ThreadInterrupter::Startup();
|
||||
SampleBlockProcessor::Init();
|
||||
SampleBlockProcessor::Startup();
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
|
@ -113,6 +122,7 @@ void Profiler::Cleanup() {
|
|||
}
|
||||
ASSERT(initialized_);
|
||||
ThreadInterrupter::Cleanup();
|
||||
SampleBlockProcessor::Cleanup();
|
||||
SampleBlockCleanupVisitor visitor;
|
||||
Isolate::VisitIsolates(&visitor);
|
||||
initialized_ = false;
|
||||
|
@ -223,21 +233,9 @@ SampleBlock* SampleBlockBuffer::ReserveSampleBlock() {
|
|||
|
||||
void SampleBlockBuffer::ProcessCompletedBlocks() {
|
||||
Thread* thread = Thread::Current();
|
||||
DisableThreadInterruptsScope dtis(thread);
|
||||
int64_t start = Dart_TimelineGetMicros();
|
||||
for (intptr_t i = 0; i < capacity_; ++i) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
thread->isolate()->ProcessFreeSampleBlocks(thread);
|
||||
int64_t end = Dart_TimelineGetMicros();
|
||||
Dart_TimelineEvent("SampleBlockBuffer::ProcessCompletedBlocks", start, end,
|
||||
Dart_Timeline_Event_Duration, 0, nullptr, nullptr);
|
||||
|
@ -306,12 +304,6 @@ Sample* SampleBlockBuffer::ReserveSampleImpl(Isolate* isolate,
|
|||
Sample* sample = nullptr;
|
||||
if (block != nullptr) {
|
||||
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) {
|
||||
return sample;
|
||||
|
@ -326,6 +318,7 @@ Sample* SampleBlockBuffer::ReserveSampleImpl(Isolate* isolate,
|
|||
return nullptr;
|
||||
}
|
||||
isolate->set_current_allocation_sample_block(next);
|
||||
isolate->FreeSampleBlock(block);
|
||||
} else {
|
||||
MutexLocker locker(isolate->current_sample_block_lock());
|
||||
next = ReserveSampleBlock();
|
||||
|
@ -334,13 +327,14 @@ Sample* SampleBlockBuffer::ReserveSampleImpl(Isolate* isolate,
|
|||
return nullptr;
|
||||
}
|
||||
isolate->set_current_sample_block(next);
|
||||
isolate->FreeSampleBlock(block);
|
||||
}
|
||||
next->set_is_allocation_block(allocation_sample);
|
||||
|
||||
can_process_block_.store(true);
|
||||
// TODO(bkonyi): re-enable after block streaming is fixed.
|
||||
// See https://github.com/dart-lang/sdk/issues/46825
|
||||
// isolate->mutator_thread()->ScheduleInterrupts(Thread::kVMInterrupt);
|
||||
bool scheduled = can_process_block_.exchange(true);
|
||||
if (!scheduled) {
|
||||
isolate->mutator_thread()->ScheduleInterrupts(Thread::kVMInterrupt);
|
||||
}
|
||||
return ReserveSampleImpl(isolate, allocation_sample);
|
||||
}
|
||||
|
||||
|
@ -1825,6 +1819,94 @@ ProcessedSampleBuffer::ProcessedSampleBuffer()
|
|||
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
|
||||
|
||||
} // namespace dart
|
||||
|
|
|
@ -73,6 +73,9 @@ class Profiler : public AllStatic {
|
|||
static SampleBlockBuffer* sample_block_buffer() {
|
||||
return sample_block_buffer_;
|
||||
}
|
||||
static void set_sample_block_buffer(SampleBlockBuffer* buffer) {
|
||||
sample_block_buffer_ = buffer;
|
||||
}
|
||||
static AllocationSampleBuffer* allocation_sample_buffer() {
|
||||
return allocation_sample_buffer_;
|
||||
}
|
||||
|
@ -408,9 +411,9 @@ class Sample {
|
|||
kTruncatedTraceBit = 5,
|
||||
kClassAllocationSampleBit = 6,
|
||||
kContinuationSampleBit = 7,
|
||||
kThreadTaskBit = 8, // 6 bits.
|
||||
kMetadataBit = 14, // 16 bits.
|
||||
kNextFreeBit = 30,
|
||||
kThreadTaskBit = 8, // 7 bits.
|
||||
kMetadataBit = 15, // 16 bits.
|
||||
kNextFreeBit = 31,
|
||||
};
|
||||
class HeadSampleBit : public BitField<uint32_t, bool, kHeadSampleBit, 1> {};
|
||||
class LeafFrameIsDart
|
||||
|
@ -426,7 +429,7 @@ class Sample {
|
|||
class ContinuationSampleBit
|
||||
: public BitField<uint32_t, bool, kContinuationSampleBit, 1> {};
|
||||
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> {};
|
||||
|
||||
int64_t timestamp_;
|
||||
|
@ -710,7 +713,7 @@ class SampleBuffer : public ProcessedSampleBufferBuilder {
|
|||
class SampleBlock : public SampleBuffer {
|
||||
public:
|
||||
// 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;
|
||||
virtual ~SampleBlock() = default;
|
||||
|
@ -765,13 +768,14 @@ class SampleBlock : public SampleBuffer {
|
|||
|
||||
private:
|
||||
friend class SampleBlockBuffer;
|
||||
friend class Isolate;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(SampleBlock);
|
||||
};
|
||||
|
||||
class SampleBlockBuffer : public ProcessedSampleBufferBuilder {
|
||||
public:
|
||||
static const intptr_t kDefaultBlockCount = 60;
|
||||
static const intptr_t kDefaultBlockCount = 600;
|
||||
|
||||
// Creates a SampleBlockBuffer with a predetermined number of blocks.
|
||||
//
|
||||
|
@ -864,6 +868,8 @@ class SampleBlockBuffer : public ProcessedSampleBufferBuilder {
|
|||
// Sample buffer management.
|
||||
VirtualMemory* memory_;
|
||||
Sample* sample_buffer_;
|
||||
|
||||
friend class Isolate;
|
||||
DISALLOW_COPY_AND_ASSIGN(SampleBlockBuffer);
|
||||
};
|
||||
|
||||
|
@ -1037,6 +1043,24 @@ class ProcessedSampleBuffer : public ZoneAllocated {
|
|||
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
|
||||
|
||||
#endif // RUNTIME_VM_PROFILER_H_
|
||||
|
|
|
@ -1450,9 +1450,8 @@ class ProfileBuilder : public ValueObject {
|
|||
ProfileInfoKind info_kind_;
|
||||
}; // ProfileBuilder.
|
||||
|
||||
Profile::Profile(Isolate* isolate)
|
||||
: isolate_(isolate),
|
||||
zone_(Thread::Current()->zone()),
|
||||
Profile::Profile()
|
||||
: zone_(Thread::Current()->zone()),
|
||||
samples_(NULL),
|
||||
live_code_(NULL),
|
||||
dead_code_(NULL),
|
||||
|
@ -1461,9 +1460,7 @@ Profile::Profile(Isolate* isolate)
|
|||
dead_code_index_offset_(-1),
|
||||
tag_code_index_offset_(-1),
|
||||
min_time_(kMaxInt64),
|
||||
max_time_(0) {
|
||||
ASSERT(isolate_ != NULL);
|
||||
}
|
||||
max_time_(0) {}
|
||||
|
||||
void Profile::Build(Thread* thread,
|
||||
SampleFilter* filter,
|
||||
|
@ -1765,14 +1762,12 @@ void ProfilerService::PrintJSONImpl(Thread* thread,
|
|||
SampleFilter* filter,
|
||||
ProcessedSampleBufferBuilder* buffer,
|
||||
bool include_code_samples) {
|
||||
Isolate* isolate = thread->isolate();
|
||||
|
||||
// We should bail out in service.cc if the profiler is disabled.
|
||||
ASSERT(buffer != nullptr);
|
||||
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
profile.Build(thread, filter, buffer);
|
||||
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.
|
||||
class Profile : public ValueObject {
|
||||
public:
|
||||
explicit Profile(Isolate* isolate);
|
||||
Profile();
|
||||
|
||||
// Build a filtered model using |filter|.
|
||||
void Build(Thread* thread,
|
||||
|
@ -403,7 +403,6 @@ class Profile : public ValueObject {
|
|||
intptr_t frame_index);
|
||||
void PrintSamplesJSON(JSONObject* obj, bool code_samples);
|
||||
|
||||
Isolate* isolate_;
|
||||
Zone* zone_;
|
||||
ProcessedSampleBuffer* samples_;
|
||||
ProfileCodeTable* live_code_;
|
||||
|
|
|
@ -102,9 +102,30 @@ void VisitSamples(SampleBlockBuffer* buffer, SampleVisitor* 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) {
|
||||
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;
|
||||
ProfileSampleBufferTestHelper visitor(i);
|
||||
|
||||
|
@ -142,12 +163,14 @@ TEST_CASE(Profiler_SampleBufferWrapTest) {
|
|||
MutexLocker ml(isolate->current_sample_block_lock());
|
||||
isolate->set_current_sample_block(nullptr);
|
||||
}
|
||||
delete sample_buffer;
|
||||
}
|
||||
|
||||
TEST_CASE(Profiler_SampleBufferIterateTest) {
|
||||
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;
|
||||
ProfileSampleBufferTestHelper visitor(i);
|
||||
|
||||
|
@ -182,7 +205,6 @@ TEST_CASE(Profiler_SampleBufferIterateTest) {
|
|||
MutexLocker ml(isolate->current_sample_block_lock());
|
||||
isolate->set_current_sample_block(nullptr);
|
||||
}
|
||||
delete sample_buffer;
|
||||
}
|
||||
|
||||
TEST_CASE(Profiler_AllocationSampleTest) {
|
||||
|
@ -480,7 +502,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TrivialRecordAllocation) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
// Filter for the class in the time range.
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id(),
|
||||
before_allocations_micros,
|
||||
|
@ -510,7 +532,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TrivialRecordAllocation) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id(),
|
||||
Dart_TimelineGetMicros(), 16000);
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
|
@ -555,10 +577,9 @@ ISOLATE_UNIT_TEST_CASE(Profiler_NativeAllocation) {
|
|||
// with each node.
|
||||
{
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
|
||||
// Filter for the class in the time range.
|
||||
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.
|
||||
{
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
|
||||
// Filter for the class in the time range.
|
||||
NativeAllocationSampleFilter filter(before_allocations_micros,
|
||||
|
@ -611,10 +631,9 @@ ISOLATE_UNIT_TEST_CASE(Profiler_NativeAllocation) {
|
|||
// Query with a time filter where no allocations occurred.
|
||||
{
|
||||
Thread* thread = Thread::Current();
|
||||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
NativeAllocationSampleFilter filter(Dart_TimelineGetMicros(), 16000);
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples because none occured within
|
||||
|
@ -660,7 +679,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ToggleRecordAllocation) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -677,7 +696,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ToggleRecordAllocation) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation sample.
|
||||
|
@ -707,7 +726,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ToggleRecordAllocation) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -745,7 +764,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_CodeTicks) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -765,7 +784,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_CodeTicks) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have three allocation samples.
|
||||
|
@ -820,7 +839,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_FunctionTicks) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -840,7 +859,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_FunctionTicks) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have three allocation samples.
|
||||
|
@ -890,7 +909,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_IntrinsicAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), double_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -903,7 +922,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_IntrinsicAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), double_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation sample.
|
||||
|
@ -925,7 +944,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_IntrinsicAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), double_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -952,7 +971,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), array_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -965,7 +984,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), array_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation sample.
|
||||
|
@ -987,7 +1006,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), array_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -1009,7 +1028,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ArrayAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), array_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples, since empty
|
||||
|
@ -1039,7 +1058,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ContextAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), context_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -1052,7 +1071,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ContextAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), context_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation sample.
|
||||
|
@ -1072,7 +1091,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ContextAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), context_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -1112,7 +1131,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ClosureAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), closure_class.id());
|
||||
filter.set_enable_vm_ticks(true);
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
|
@ -1136,7 +1155,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ClosureAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), closure_class.id());
|
||||
filter.set_enable_vm_ticks(true);
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
|
@ -1167,7 +1186,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -1180,7 +1199,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation sample.
|
||||
|
@ -1202,7 +1221,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -1215,7 +1234,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_TypedArrayAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), float32_list_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should now have two allocation samples.
|
||||
|
@ -1247,7 +1266,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -1260,7 +1279,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -1280,7 +1299,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -1293,7 +1312,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringAllocation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should now have two allocation samples.
|
||||
|
@ -1325,7 +1344,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -1338,7 +1357,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -1364,7 +1383,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should still only have one allocation sample.
|
||||
|
@ -1377,7 +1396,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_StringInterpolation) {
|
|||
{
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), one_byte_string_class.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should now have two allocation samples.
|
||||
|
@ -1433,7 +1452,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_FunctionInline) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -1451,7 +1470,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_FunctionInline) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have 50,000 allocation samples.
|
||||
|
@ -1580,7 +1599,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_InliningIntervalBoundry) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have no allocation samples.
|
||||
|
@ -1597,7 +1616,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_InliningIntervalBoundry) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
EXPECT_EQ(1, profile.sample_count());
|
||||
|
@ -1673,7 +1692,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_ChainedSamples) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have 1 allocation sample.
|
||||
|
@ -1768,7 +1787,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_BasicSourcePosition) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation samples.
|
||||
|
@ -1850,7 +1869,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_BasicSourcePositionOptimized) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation samples.
|
||||
|
@ -1928,7 +1947,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_SourcePosition) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation samples.
|
||||
|
@ -2038,7 +2057,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_SourcePositionOptimized) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation samples.
|
||||
|
@ -2133,7 +2152,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_BinaryOperatorSourcePosition) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation samples.
|
||||
|
@ -2251,7 +2270,7 @@ ISOLATE_UNIT_TEST_CASE(Profiler_BinaryOperatorSourcePositionOptimized) {
|
|||
Isolate* isolate = thread->isolate();
|
||||
StackZone zone(thread);
|
||||
HANDLESCOPE(thread);
|
||||
Profile profile(isolate);
|
||||
Profile profile;
|
||||
AllocationFilter filter(isolate->main_port(), class_a.id());
|
||||
profile.Build(thread, &filter, Profiler::sample_block_buffer());
|
||||
// We should have one allocation samples.
|
||||
|
|
|
@ -29,7 +29,6 @@ SourceReport::SourceReport(intptr_t report_set, CompileMode compile_mode)
|
|||
script_(NULL),
|
||||
start_pos_(TokenPosition::kMinSource),
|
||||
end_pos_(TokenPosition::kMaxSource),
|
||||
profile_(Isolate::Current()),
|
||||
next_script_index_(0) {}
|
||||
|
||||
SourceReport::~SourceReport() {
|
||||
|
|
|
@ -450,11 +450,15 @@ ErrorPtr Thread::HandleInterrupts() {
|
|||
}
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
// Processes completed SampleBlocks and sends CPU sample events over the
|
||||
// service protocol when applicable.
|
||||
SampleBlockBuffer* sample_buffer = Profiler::sample_block_buffer();
|
||||
if (sample_buffer != nullptr && sample_buffer->process_blocks()) {
|
||||
sample_buffer->ProcessCompletedBlocks();
|
||||
// Don't block system isolates to process CPU samples to avoid blocking
|
||||
// them during critical tasks (e.g., initial compilation).
|
||||
if (!Isolate::IsSystemIsolate(isolate())) {
|
||||
// Processes completed SampleBlocks and sends CPU sample events over the
|
||||
// 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)
|
||||
}
|
||||
|
|
|
@ -273,6 +273,7 @@ class Thread : public ThreadState {
|
|||
kSweeperTask = 0x8,
|
||||
kCompactorTask = 0x10,
|
||||
kScavengerTask = 0x20,
|
||||
kSampleBlockTask = 0x40,
|
||||
};
|
||||
// Converts a TaskKind to its corresponding C-String name.
|
||||
static const char* TaskKindToCString(TaskKind kind);
|
||||
|
|
Loading…
Reference in a new issue