| // 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); |