667fc8dbe9
BUG= R=vsm@google.com Review URL: https://codereview.chromium.org/2534263002 .
329 lines
9.6 KiB
Dart
329 lines
9.6 KiB
Dart
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
part of dart.developer;
|
|
|
|
const bool _isProduct = const bool.fromEnvironment("dart.vm.product");
|
|
|
|
typedef dynamic TimelineSyncFunction();
|
|
typedef Future TimelineAsyncFunction();
|
|
|
|
/// Add to the timeline.
|
|
class Timeline {
|
|
/// Start a synchronous operation labeled [name]. Optionally takes
|
|
/// a [Map] of [arguments]. This operation must be finished before
|
|
/// returning to the event queue.
|
|
static void startSync(String name, {Map arguments}) {
|
|
if (_isProduct) {
|
|
return;
|
|
}
|
|
if (name is! String) {
|
|
throw new ArgumentError.value(name,
|
|
'name',
|
|
'Must be a String');
|
|
}
|
|
if (!_isDartStreamEnabled()) {
|
|
// Push a null onto the stack and return.
|
|
_stack.add(null);
|
|
return;
|
|
}
|
|
var block = new _SyncBlock._(name, _getTraceClock(), _getThreadCpuClock());
|
|
if (arguments is Map) {
|
|
block._appendArguments(arguments);
|
|
}
|
|
_stack.add(block);
|
|
}
|
|
|
|
/// Finish the last synchronous operation that was started.
|
|
static void finishSync() {
|
|
if (_isProduct) {
|
|
return;
|
|
}
|
|
if (_stack.length == 0) {
|
|
throw new StateError(
|
|
'Uneven calls to startSync and finishSync');
|
|
}
|
|
// Pop top item off of stack.
|
|
var block = _stack.removeLast();
|
|
if (block == null) {
|
|
// Dart stream was disabled when startSync was called.
|
|
return;
|
|
}
|
|
// Finish it.
|
|
block.finish();
|
|
}
|
|
|
|
/// Emit an instant event.
|
|
static void instantSync(String name, {Map arguments}) {
|
|
if (_isProduct) {
|
|
return;
|
|
}
|
|
if (name is! String) {
|
|
throw new ArgumentError.value(name,
|
|
'name',
|
|
'Must be a String');
|
|
}
|
|
if (!_isDartStreamEnabled()) {
|
|
// Stream is disabled.
|
|
return;
|
|
}
|
|
Map instantArguments;
|
|
if (arguments is Map) {
|
|
instantArguments = new Map.from(arguments);
|
|
}
|
|
_reportInstantEvent(_getTraceClock(),
|
|
'Dart',
|
|
name,
|
|
_argumentsAsJson(instantArguments));
|
|
}
|
|
|
|
|
|
/// A utility method to time a synchronous [function]. Internally calls
|
|
/// [function] bracketed by calls to [startSync] and [finishSync].
|
|
static dynamic timeSync(String name,
|
|
TimelineSyncFunction function,
|
|
{Map arguments}) {
|
|
startSync(name, arguments: arguments);
|
|
try {
|
|
return function();
|
|
} finally {
|
|
finishSync();
|
|
}
|
|
}
|
|
|
|
/// The current time stamp from the clock used by the timeline. Units are
|
|
/// microseconds.
|
|
static int get now => _getTraceClock();
|
|
static final List<_SyncBlock> _stack = new List<_SyncBlock>();
|
|
static final int _isolateId = _getIsolateNum();
|
|
static final String _isolateIdString = _isolateId.toString();
|
|
}
|
|
|
|
/// An asynchronous task on the timeline. An asynchronous task can have many
|
|
/// (nested) synchronous operations. Synchronous operations can live longer than
|
|
/// the current isolate event. To pass a [TimelineTask] to another isolate,
|
|
/// you must first call [pass] to get the task id and then construct a new
|
|
/// [TimelineTask] in the other isolate.
|
|
class TimelineTask {
|
|
/// Create a task. [taskId] will be set by the system.
|
|
TimelineTask()
|
|
: _taskId = _getNextAsyncId() {
|
|
}
|
|
|
|
/// Create a task with an explicit [taskId]. This is useful if you are
|
|
/// passing a task from one isolate to another.
|
|
TimelineTask.withTaskId(int taskId)
|
|
: _taskId = taskId {
|
|
if (taskId is! int) {
|
|
throw new ArgumentError.value(taskId,
|
|
'taskId',
|
|
'Must be an int');
|
|
}
|
|
}
|
|
|
|
/// Start a synchronous operation within this task named [name].
|
|
/// Optionally takes a [Map] of [arguments].
|
|
void start(String name, {Map arguments}) {
|
|
if (_isProduct) {
|
|
return;
|
|
}
|
|
if (name is! String) {
|
|
throw new ArgumentError.value(name,
|
|
'name',
|
|
'Must be a String');
|
|
}
|
|
var block = new _AsyncBlock._(name, _taskId);
|
|
if (arguments is Map) {
|
|
block._appendArguments(arguments);
|
|
}
|
|
_stack.add(block);
|
|
block._start();
|
|
}
|
|
|
|
/// Emit an instant event for this task.
|
|
void instant(String name, {Map arguments}) {
|
|
if (_isProduct) {
|
|
return;
|
|
}
|
|
if (name is! String) {
|
|
throw new ArgumentError.value(name,
|
|
'name',
|
|
'Must be a String');
|
|
}
|
|
Map instantArguments;
|
|
if (arguments is Map) {
|
|
instantArguments = new Map.from(arguments);
|
|
}
|
|
_reportTaskEvent(_getTraceClock(),
|
|
_taskId,
|
|
'n',
|
|
'Dart',
|
|
name,
|
|
_argumentsAsJson(instantArguments));
|
|
}
|
|
|
|
/// Finish the last synchronous operation that was started.
|
|
void finish() {
|
|
if (_isProduct) {
|
|
return;
|
|
}
|
|
if (_stack.length == 0) {
|
|
throw new StateError(
|
|
'Uneven calls to start and finish');
|
|
}
|
|
// Pop top item off of stack.
|
|
var block = _stack.removeLast();
|
|
block._finish();
|
|
}
|
|
|
|
/// Retrieve the [TimelineTask]'s task id. Will throw an exception if the
|
|
/// stack is not empty.
|
|
int pass() {
|
|
if (_stack.length > 0) {
|
|
throw new StateError(
|
|
'You cannot pass a TimelineTask without finishing all started '
|
|
'operations');
|
|
}
|
|
int r = _taskId;
|
|
return r;
|
|
}
|
|
|
|
final int _taskId;
|
|
final List<_AsyncBlock> _stack = [];
|
|
}
|
|
|
|
/// An asynchronous block of time on the timeline. This block can be kept
|
|
/// open across isolate messages.
|
|
class _AsyncBlock {
|
|
/// The category this block belongs to.
|
|
final String category = 'Dart';
|
|
/// The name of this block.
|
|
final String name;
|
|
/// The asynchronous task id.
|
|
final int _taskId;
|
|
/// An (optional) set of arguments which will be serialized to JSON and
|
|
/// associated with this block.
|
|
Map _arguments;
|
|
|
|
_AsyncBlock._(this.name, this._taskId);
|
|
|
|
// Emit the start event.
|
|
void _start() {
|
|
_reportTaskEvent(_getTraceClock(),
|
|
_taskId,
|
|
'b',
|
|
category,
|
|
name,
|
|
_argumentsAsJson(_arguments));
|
|
}
|
|
|
|
// Emit the finish event.
|
|
void _finish() {
|
|
_reportTaskEvent(_getTraceClock(),
|
|
_taskId,
|
|
'e',
|
|
category,
|
|
name,
|
|
_argumentsAsJson(null));
|
|
}
|
|
|
|
void _appendArguments(Map arguments) {
|
|
if (_arguments == null) {
|
|
_arguments = {};
|
|
}
|
|
_arguments.addAll(arguments);
|
|
}
|
|
}
|
|
|
|
/// A synchronous block of time on the timeline. This block should not be
|
|
/// kept open across isolate messages.
|
|
class _SyncBlock {
|
|
/// The category this block belongs to.
|
|
final String category = 'Dart';
|
|
/// The name of this block.
|
|
final String name;
|
|
/// An (optional) set of arguments which will be serialized to JSON and
|
|
/// associated with this block.
|
|
Map _arguments;
|
|
// The start time stamp.
|
|
final int _start;
|
|
// The start time stamp of the thread cpu clock.
|
|
final int _startCpu;
|
|
|
|
_SyncBlock._(this.name,
|
|
this._start,
|
|
this._startCpu);
|
|
|
|
/// Finish this block of time. At this point, this block can no longer be
|
|
/// used.
|
|
void finish() {
|
|
// Report event to runtime.
|
|
_reportCompleteEvent(_start,
|
|
_startCpu,
|
|
category,
|
|
name,
|
|
_argumentsAsJson(_arguments));
|
|
}
|
|
|
|
void _appendArguments(Map arguments) {
|
|
if (arguments == null) {
|
|
return;
|
|
}
|
|
if (_arguments == null) {
|
|
_arguments = {};
|
|
}
|
|
_arguments.addAll(arguments);
|
|
}
|
|
}
|
|
|
|
String _fastPathArguments;
|
|
String _argumentsAsJson(Map arguments) {
|
|
if ((arguments == null) || (arguments.length == 0)) {
|
|
// Fast path no arguments. Avoid calling JSON.encode.
|
|
if (_fastPathArguments == null) {
|
|
_fastPathArguments = '{"isolateNumber":"${Timeline._isolateId}"}';
|
|
}
|
|
return _fastPathArguments;
|
|
}
|
|
// Add isolateNumber to arguments map.
|
|
arguments['isolateNumber'] = Timeline._isolateIdString;
|
|
return JSON.encode(arguments);
|
|
}
|
|
|
|
/// Returns true if the Dart Timeline stream is enabled.
|
|
external bool _isDartStreamEnabled();
|
|
|
|
/// Returns the next async task id.
|
|
external int _getNextAsyncId();
|
|
|
|
/// Returns the current value from the trace clock.
|
|
external int _getTraceClock();
|
|
|
|
/// Returns the current value from the thread CPU usage clock.
|
|
external int _getThreadCpuClock();
|
|
|
|
/// Returns the isolate's main port number.
|
|
external int _getIsolateNum();
|
|
|
|
/// Reports an event for a task.
|
|
external void _reportTaskEvent(int start,
|
|
int taskId,
|
|
String phase,
|
|
String category,
|
|
String name,
|
|
String argumentsAsJson);
|
|
|
|
/// Reports a complete synchronous event.
|
|
external void _reportCompleteEvent(int start,
|
|
int startCpu,
|
|
String category,
|
|
String name,
|
|
String argumentsAsJson);
|
|
|
|
/// Reports an instant event.
|
|
external void _reportInstantEvent(int start,
|
|
String category,
|
|
String name,
|
|
String argumentsAsJson);
|