blob: 2753addd6c25f70a83c7c9fe70de5fd6731a48f8 [file] [log] [blame]
// 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;
typedef dynamic TimelineSyncFunction();
/// 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 (name is! String) {
throw new ArgumentError.value(name,
'name',
'Must be a String');
}
var block = new _SyncBlock._(name, _getTraceClock());
if (arguments is Map) {
block.arguments.addAll(arguments);
}
_stack.add(block);
}
/// Finish the last synchronous operation that was started.
static void finishSync() {
if (_stack.length == 0) {
throw new StateError(
'Uneven calls to startSync and finishSync');
}
// Pop top item off of stack.
var block = _stack.removeLast();
// Finish it.
block.finish();
}
/// 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();
}
}
static final List<_SyncBlock> _stack = new List<_SyncBlock>();
}
/// An asynchronous task on the timeline. Asynchronous tasks can live
/// longer than the current event and can even be shared between isolates.
/// An asynchronous task can have many (nested) blocks. To share a
/// [_TimelineTask] across isolates, you must construct a [_TimelineTask] in
/// both isolates using the same [taskId] and [category].
class _TimelineTask {
/// Create a task. [taskId] will be set by the system.
/// Optionally you can specify a [category] name.
_TimelineTask({String category: 'Dart'})
: _taskId = _getNextAsyncId(),
category = category {
if (category is! String) {
throw new ArgumentError.value(category,
'category',
'Must be a String');
}
}
/// Create a task with an explicit [taskId]. This is useful if you are
/// passing a task between isolates. Optionally you can specify a [category]
/// name.
_TimelineTask.withTaskId(int taskId, {String category: 'Dart'})
: _taskId = taskId,
category = category {
if (taskId is! int) {
throw new ArgumentError.value(taskId,
'taskId',
'Must be an int');
}
if (category is! String) {
throw new ArgumentError.value(category,
'category',
'Must be a String');
}
}
/// Start a block in this task named [name]. Optionally takes
/// a [Map] of [arguments].
/// Returns an [_AsyncBlock] which is used to finish this block.
_AsyncBlock start(String name, {Map arguments}) {
if (name is! String) {
throw new ArgumentError.value(name,
'name',
'Must be a String');
}
var block = new _AsyncBlock._(name, _taskId, category);
if (arguments is Map) {
block.arguments.addAll(arguments);
}
/// Emit start event.
block._start();
return block;
}
/// Retrieve the asynchronous task's id. Can be used to construct a
/// [_TimelineTask] in another isolate.
int get taskId => _taskId;
final int _taskId;
/// Retrieve the asynchronous task's category. Can be used to construct a
/// [_TimelineTask] in another isolate.
final String category;
}
/// 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;
/// 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.
final Map arguments = {};
bool _finished = false;
_AsyncBlock._(this.name, this._taskId, this.category);
// Emit the start event.
void _start() {
String argumentsAsJson = JSON.encode(arguments);
_reportTaskEvent(_getTraceClock(),
_taskId,
'b',
category,
name,
argumentsAsJson);
}
// Emit the finish event.
void _finish() {
_reportTaskEvent(_getTraceClock(),
_taskId,
'e',
category,
name,
JSON.encode({}));
}
/// Finish this block. Cannot be called twice.
void finish() {
if (_finished) {
throw new StateError(
'It is illegal to call finish twice on the same _AsyncBlock');
}
_finished = true;
_finish();
}
/// Finishes this block when [future] completes. Returns a [Future]
/// chained to [future].
Future finishWhenComplete(Future future) {
if (future is! Future) {
throw new ArgumentError.value(future,
'future',
'Must be a Future');
}
return future.whenComplete(() {
finish();
});
}
}
/// 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.
final Map arguments = {};
// The start time stamp.
final int _start;
_SyncBlock._(this.name,
this._start);
/// Finish this block of time. At this point, this block can no longer be
/// used.
void finish() {
var end = _getTraceClock();
// Encode arguments map as JSON before reporting.
var argumentsAsJson = JSON.encode(arguments);
// Report event to runtime.
_reportCompleteEvent(_start,
end,
category,
name,
argumentsAsJson);
}
}
/// Returns the next async task id.
external int _getNextAsyncId();
/// Returns the current value from the trace clock.
external int _getTraceClock();
/// 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 end,
String category,
String name,
String argumentsAsJson);