mirror of
https://github.com/dart-lang/sdk
synced 2024-09-06 00:59:09 +00:00
Support the dart:developer timeline APIs in dart2js and DDC.
Exposes timeline events in the Chrome DevTools performance panel using the Web APIs performance.mark() and performance.measure(): https://developer.mozilla.org/en-US/docs/Web/API/Performance CoreLibraryReviewExempt: Only change in sdk/lib is updating a comment. Bug: https://github.com/flutter/devtools/issues/4652 Change-Id: I4f934bcffeb2920ffaf9b7b3a67fc5fc3b814294 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/310974 Reviewed-by: Stephen Adams <sra@google.com> Commit-Queue: Elliott Brooks <elliottbrooks@google.com>
This commit is contained in:
parent
73160bc40d
commit
af8fb2cd73
|
@ -12,6 +12,15 @@ import 'dart:async';
|
|||
import 'dart:convert' show json;
|
||||
import 'dart:isolate';
|
||||
|
||||
// These values must be kept in sync with developer/timeline.dart.
|
||||
const int _beginPatch = 1;
|
||||
const int _endPatch = 2;
|
||||
const int _asyncBeginPatch = 5;
|
||||
const int _asyncEndPatch = 7;
|
||||
const int _flowBeginPatch = 9;
|
||||
const int _flowStepPatch = 10;
|
||||
const int _flowEndPatch = 11;
|
||||
|
||||
var _issuedRegisterExtensionWarning = false;
|
||||
var _issuedPostEventWarning = false;
|
||||
final _developerSupportWarning = 'from dart:developer is only supported in '
|
||||
|
@ -140,26 +149,110 @@ void _postEvent(String eventKind, String eventData) {
|
|||
|
||||
@patch
|
||||
bool _isDartStreamEnabled() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@patch
|
||||
int _getTraceClock() {
|
||||
// TODO.
|
||||
return _clockValue++;
|
||||
// Note: Use `millisecondsSinceEpoch` instead of `microsecondsSinceEpoch`
|
||||
// because JS isn't able to hold the value of `microsecondsSinceEpoch` without
|
||||
// rounding errors.
|
||||
return DateTime.now().millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
int _clockValue = 0;
|
||||
int _taskId = 1;
|
||||
|
||||
@patch
|
||||
int _getNextTaskId() {
|
||||
return 0;
|
||||
return _taskId++;
|
||||
}
|
||||
|
||||
bool _isBeginEvent(int type) => type == _beginPatch || type == _asyncBeginPatch;
|
||||
|
||||
bool _isEndEvent(int type) => type == _endPatch || type == _asyncEndPatch;
|
||||
|
||||
bool _isUnsupportedEvent(int type) =>
|
||||
type == _flowBeginPatch || type == _flowEndPatch || type == _flowStepPatch;
|
||||
|
||||
String _createEventName({
|
||||
required int taskId,
|
||||
required String name,
|
||||
required bool isBeginEvent,
|
||||
required bool isEndEvent,
|
||||
}) {
|
||||
if (isBeginEvent) {
|
||||
return '$taskId-$name-begin';
|
||||
}
|
||||
if (isEndEvent) {
|
||||
return '$taskId-$name-end';
|
||||
}
|
||||
// Return only the name for events that don't need measurements:
|
||||
return name;
|
||||
}
|
||||
|
||||
Map<String, int> _eventNameToCount = {};
|
||||
|
||||
String _postfixWithCount(String eventName) {
|
||||
final count = _eventNameToCount[eventName];
|
||||
if (count == null) return eventName;
|
||||
return '$eventName-$count';
|
||||
}
|
||||
|
||||
void _incrementEventCount(String eventName) {
|
||||
final currentCount = _eventNameToCount[eventName] ?? 0;
|
||||
_eventNameToCount[eventName] = currentCount + 1;
|
||||
}
|
||||
|
||||
void _decrementEventCount(String eventName) {
|
||||
if (!_eventNameToCount.containsKey(eventName)) return;
|
||||
|
||||
final newCount = _eventNameToCount[eventName]! - 1;
|
||||
if (newCount <= 0) {
|
||||
_eventNameToCount.remove(eventName);
|
||||
} else {
|
||||
_eventNameToCount[eventName] = newCount;
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
void _reportTaskEvent(
|
||||
int taskId, int flowId, int type, String name, String argumentsAsJson) {
|
||||
// TODO.
|
||||
// Ignore any unsupported events.
|
||||
if (_isUnsupportedEvent(type)) return;
|
||||
|
||||
final isBeginEvent = _isBeginEvent(type);
|
||||
final isEndEvent = _isEndEvent(type);
|
||||
var currentEventName = _createEventName(
|
||||
taskId: taskId,
|
||||
name: name,
|
||||
isBeginEvent: isBeginEvent,
|
||||
isEndEvent: isEndEvent,
|
||||
);
|
||||
// Postfix the event name with the current count of events with that name. This
|
||||
// guarantees that we are always measuring from the most recent begin event.
|
||||
if (isBeginEvent) {
|
||||
_incrementEventCount(currentEventName);
|
||||
currentEventName = _postfixWithCount(currentEventName);
|
||||
}
|
||||
final markOptions = JS('', '{detail: JSON.parse(#)}', argumentsAsJson);
|
||||
|
||||
// Start by creating a mark event.
|
||||
JS('', 'performance.mark(#, #)', currentEventName, markOptions);
|
||||
|
||||
// If it's an end event, then create a measurement from the most recent begin
|
||||
// event with the same name.
|
||||
if (isEndEvent) {
|
||||
final beginEventName = _createEventName(
|
||||
taskId: taskId, name: name, isBeginEvent: true, isEndEvent: false);
|
||||
JS(
|
||||
'',
|
||||
'performance.measure(#, #, #)',
|
||||
name,
|
||||
_postfixWithCount(beginEventName),
|
||||
currentEventName,
|
||||
);
|
||||
_decrementEventCount(beginEventName);
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
|
|
|
@ -9,6 +9,15 @@ import 'dart:_foreign_helper' show JS;
|
|||
import 'dart:async' show Zone;
|
||||
import 'dart:isolate';
|
||||
|
||||
// These values must be kept in sync with developer/timeline.dart.
|
||||
const int _beginPatch = 1;
|
||||
const int _endPatch = 2;
|
||||
const int _asyncBeginPatch = 5;
|
||||
const int _asyncEndPatch = 7;
|
||||
const int _flowBeginPatch = 9;
|
||||
const int _flowStepPatch = 10;
|
||||
const int _flowEndPatch = 11;
|
||||
|
||||
@patch
|
||||
@pragma('dart2js:tryInline')
|
||||
bool debugger({bool when = true, String? message}) {
|
||||
|
@ -60,26 +69,110 @@ void _postEvent(String eventKind, String eventData) {
|
|||
|
||||
@patch
|
||||
bool _isDartStreamEnabled() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@patch
|
||||
int _getTraceClock() {
|
||||
// TODO.
|
||||
return _clockValue++;
|
||||
// Note: Use `millisecondsSinceEpoch` instead of `microsecondsSinceEpoch`
|
||||
// because JS isn't able to hold the value of `microsecondsSinceEpoch` without
|
||||
// rounding errors.
|
||||
return DateTime.now().millisecondsSinceEpoch;
|
||||
}
|
||||
|
||||
int _clockValue = 0;
|
||||
int _taskId = 1;
|
||||
|
||||
@patch
|
||||
int _getNextTaskId() {
|
||||
return 0;
|
||||
return _taskId++;
|
||||
}
|
||||
|
||||
bool _isBeginEvent(int type) => type == _beginPatch || type == _asyncBeginPatch;
|
||||
|
||||
bool _isEndEvent(int type) => type == _endPatch || type == _asyncEndPatch;
|
||||
|
||||
bool _isUnsupportedEvent(int type) =>
|
||||
type == _flowBeginPatch || type == _flowEndPatch || type == _flowStepPatch;
|
||||
|
||||
String _createEventName({
|
||||
required int taskId,
|
||||
required String name,
|
||||
required bool isBeginEvent,
|
||||
required bool isEndEvent,
|
||||
}) {
|
||||
if (isBeginEvent) {
|
||||
return '$taskId-$name-begin';
|
||||
}
|
||||
if (isEndEvent) {
|
||||
return '$taskId-$name-end';
|
||||
}
|
||||
// Return only the name for events that don't need measurements:
|
||||
return name;
|
||||
}
|
||||
|
||||
Map<String, int> _eventNameToCount = {};
|
||||
|
||||
String _postfixWithCount(String eventName) {
|
||||
final count = _eventNameToCount[eventName];
|
||||
if (count == null) return eventName;
|
||||
return '$eventName-$count';
|
||||
}
|
||||
|
||||
void _incrementEventCount(String eventName) {
|
||||
final currentCount = _eventNameToCount[eventName] ?? 0;
|
||||
_eventNameToCount[eventName] = currentCount + 1;
|
||||
}
|
||||
|
||||
void _decrementEventCount(String eventName) {
|
||||
if (!_eventNameToCount.containsKey(eventName)) return;
|
||||
|
||||
final newCount = _eventNameToCount[eventName]! - 1;
|
||||
if (newCount <= 0) {
|
||||
_eventNameToCount.remove(eventName);
|
||||
} else {
|
||||
_eventNameToCount[eventName] = newCount;
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
void _reportTaskEvent(
|
||||
int taskId, int flowId, int type, String name, String argumentsAsJson) {
|
||||
// TODO.
|
||||
// Ignore any unsupported events.
|
||||
if (_isUnsupportedEvent(type)) return;
|
||||
|
||||
final isBeginEvent = _isBeginEvent(type);
|
||||
final isEndEvent = _isEndEvent(type);
|
||||
var currentEventName = _createEventName(
|
||||
taskId: taskId,
|
||||
name: name,
|
||||
isBeginEvent: isBeginEvent,
|
||||
isEndEvent: isEndEvent,
|
||||
);
|
||||
// Postfix the event name with the current count of events with that name. This
|
||||
// guarantees that we are always measuring from the most recent begin event.
|
||||
if (isBeginEvent) {
|
||||
_incrementEventCount(currentEventName);
|
||||
currentEventName = _postfixWithCount(currentEventName);
|
||||
}
|
||||
final markOptions = JS('', '{detail: JSON.parse(#)}', argumentsAsJson);
|
||||
|
||||
// Start by creating a mark event.
|
||||
JS('', 'performance.mark(#, #)', currentEventName, markOptions);
|
||||
|
||||
// If it's an end event, then create a measurement from the most recent begin
|
||||
// event with the same name.
|
||||
if (isEndEvent) {
|
||||
final beginEventName = _createEventName(
|
||||
taskId: taskId, name: name, isBeginEvent: true, isEndEvent: false);
|
||||
JS(
|
||||
'',
|
||||
'performance.measure(#, #, #)',
|
||||
name,
|
||||
_postfixWithCount(beginEventName),
|
||||
currentEventName,
|
||||
);
|
||||
_decrementEventCount(beginEventName);
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
|
|
|
@ -17,7 +17,9 @@ typedef TimelineSyncFunction<T> = T Function();
|
|||
typedef Future TimelineAsyncFunction();
|
||||
|
||||
// These values must be kept in sync with the enum "EventType" in
|
||||
// runtime/vm/timeline.h.
|
||||
// runtime/vm/timeline.h, along with the JS-specific implementations in:
|
||||
// - _internal/js_runtime/lib/developer_patch.dart
|
||||
// - _internal/js_dev_runtime/patch/developer_patch.dart
|
||||
const int _begin = 1;
|
||||
const int _end = 2;
|
||||
const int _instant = 4;
|
||||
|
|
|
@ -26,6 +26,7 @@ developer/timeline_test: Skip # Not supported
|
|||
isolate/issue_24243_parent_isolate_test: Skip # Requires checked mode
|
||||
|
||||
[ $runtime == d8 ]
|
||||
developer/timeline_recorders_test: SkipByDesign # https://bugs.chromium.org/p/v8/issues/detail?id=14129
|
||||
js/export/static_interop_mock/proto_test: SkipByDesign # Uses dart:html.
|
||||
js/js_util/javascriptobject_extensions_test: SkipByDesign # Uses dart:html.
|
||||
js/static_interop_test/constants_test: SkipByDesign # Uses dart:html.
|
||||
|
|
Loading…
Reference in a new issue