mirror of
https://github.com/dart-lang/sdk
synced 2024-11-05 18:22:09 +00:00
Timeline service protocol support with Observatory UI
Service Protocol: - _clearVMTimeline (clear timeline) - _getVMTimeline (fetch timeline) - _setVMTimelineFlag (control recording) - _getVMTimelineFlag (...) - _getVMTimeline service unit test Observatory: - timeline page with record on/off, clear timeline, and refresh buttons. - trace-viewer based timeline view that runs in an iframe driven by a small javascript program Misc: - Fix default enable logic bugs - Add 'Debugger Pause' timeline event - Threads can have a name and that name will be displayed in Observatory - Tighten up locking when printing JSON R=rmacnak@google.com Review URL: https://codereview.chromium.org/1406413006 .
This commit is contained in:
parent
686faebf70
commit
43b0ae00e7
29 changed files with 7593 additions and 226 deletions
|
@ -894,20 +894,23 @@ DART_EXPORT int64_t Dart_TimelineGetMicros();
|
|||
#define DART_TIMELINE_STREAM_COMPILER (1 << 1)
|
||||
/** Timeline stream for Dart provided events */
|
||||
#define DART_TIMELINE_STREAM_DART (1 << 2)
|
||||
/** Timeline stream for debugger provided events */
|
||||
#define DART_TIMELINE_STREAM_DEBUGGER (1 << 3)
|
||||
/** Timeline stream for embedder provided events */
|
||||
#define DART_TIMELINE_STREAM_EMBEDDER (1 << 3)
|
||||
#define DART_TIMELINE_STREAM_EMBEDDER (1 << 4)
|
||||
/** Timeline stream for GC events */
|
||||
#define DART_TIMELINE_STREAM_GC (1 << 4)
|
||||
#define DART_TIMELINE_STREAM_GC (1 << 5)
|
||||
/** Timeline stream for isolate events */
|
||||
#define DART_TIMELINE_STREAM_ISOLATE (1 << 5)
|
||||
#define DART_TIMELINE_STREAM_ISOLATE (1 << 6)
|
||||
|
||||
/** Timeline stream for VM events */
|
||||
#define DART_TIMELINE_STREAM_VM (1 << 6)
|
||||
#define DART_TIMELINE_STREAM_VM (1 << 7)
|
||||
|
||||
/** Enable all timeline stream recording for an isolate */
|
||||
#define DART_TIMELINE_STREAM_ALL (DART_TIMELINE_STREAM_API | \
|
||||
DART_TIMELINE_STREAM_COMPILER | \
|
||||
DART_TIMELINE_STREAM_DART | \
|
||||
DART_TIMELINE_STREAM_DEBUGGER | \
|
||||
DART_TIMELINE_STREAM_EMBEDDER | \
|
||||
DART_TIMELINE_STREAM_GC | \
|
||||
DART_TIMELINE_STREAM_ISOLATE)
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
Running Observatory against content_shell:
|
||||
|
||||
0) Open your mobile web application in Dart Editor and launch it on mobile.
|
||||
|
||||
1) Forward localhost:9222 to the content_shell's remote debugging protocol:
|
||||
|
||||
$ adb forward tcp:9222 localabstract:content_shell_devtools_remote
|
||||
|
||||
2) Start the Observatory servers:
|
||||
|
||||
$ ./run.sh
|
||||
|
||||
By default Observatory will be available on localhost:9090
|
||||
|
||||
3) Release the content_shell's remote debugging protocol by clicking the 'Stop'
|
||||
button in the Dart Editor's debugger. By releasing the debugging protocol,
|
||||
Observatory can communicate with the content_shell.
|
||||
|
||||
4) On Observatory's connect to VM page you should see the name of your app
|
||||
on the right hand side of the page.
|
|
@ -1,159 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
library observatory_server;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final Logger logger = new Logger('ObsServe');
|
||||
|
||||
class ObservatoryServer {
|
||||
static const _CHROME_PREFIX = '/crdptargets/';
|
||||
|
||||
// Is logging enabled?
|
||||
bool log;
|
||||
|
||||
// Host to listen on.
|
||||
String host;
|
||||
// Port to listen on.
|
||||
int port;
|
||||
|
||||
// Host that pub is listening on.
|
||||
String pubHost;
|
||||
// Port that pub is listening on.
|
||||
int pubPort;
|
||||
|
||||
HttpServer _server;
|
||||
final HttpClient _client = new HttpClient();
|
||||
|
||||
ObservatoryServer(List<String> args) {
|
||||
var parser = new ArgParser();
|
||||
parser.addFlag('log', help: 'Log activity.', defaultsTo: true);
|
||||
parser.addOption('port', help: 'Specify port listen on',
|
||||
defaultsTo: '9090');
|
||||
parser.addOption('host',
|
||||
help: 'Specify host to listen on',
|
||||
defaultsTo: '127.0.0.1');
|
||||
parser.addOption('pub-port', help: 'Specify port that pub is listening on',
|
||||
defaultsTo: '9191');
|
||||
parser.addOption('pub-host', help: 'Specify host that pub is listening on',
|
||||
defaultsTo: '127.0.0.1');
|
||||
var results = parser.parse(args);
|
||||
host = results['host'];
|
||||
port = int.parse(results['port']);
|
||||
log = results['log'];
|
||||
pubHost = results['pub-host'];
|
||||
pubPort = int.parse(results['pub-port']);
|
||||
}
|
||||
|
||||
List<Map> _makeTargetList(List<Map> tabs) {
|
||||
var r = <Map>[];
|
||||
tabs.forEach((tab) {
|
||||
var uri = Uri.parse(tab['url']);
|
||||
if (uri.host == 'devtools') {
|
||||
// Ignore.
|
||||
return;
|
||||
}
|
||||
var target = {
|
||||
'lastConnectionTime': 0,
|
||||
'chrome': true,
|
||||
'name': tab['title'],
|
||||
'networkAddress': tab['webSocketDebuggerUrl'],
|
||||
};
|
||||
r.add(target);
|
||||
});
|
||||
return r;
|
||||
}
|
||||
|
||||
void _getChromeTabs(HttpRequest request) {
|
||||
var path = request.uri.path;
|
||||
var method = request.method;
|
||||
if (method != 'GET') {
|
||||
return;
|
||||
}
|
||||
assert(path.startsWith(_CHROME_PREFIX));
|
||||
var networkAddress = path.substring(_CHROME_PREFIX.length);
|
||||
if ((networkAddress == '') || (networkAddress == null)) {
|
||||
request.response.write('[]');
|
||||
request.response.close();
|
||||
return;
|
||||
}
|
||||
networkAddress = Uri.decodeComponent(networkAddress);
|
||||
var chunks = networkAddress.split(':');
|
||||
var chromeAddress = chunks[0];
|
||||
var chromePort =
|
||||
(chunks[1] == null) || (chunks[1] == '') ? 9222 : int.parse(chunks[1]);
|
||||
logger.info('tabs from $chromeAddress:$chromePort');
|
||||
_client.open(method, chromeAddress, chromePort, 'json')
|
||||
.then((HttpClientRequest pubRequest) {
|
||||
// Calling .close() on an HttpClientRequest sends the request to the
|
||||
// server. The future completes to an HttpClientResponse when the
|
||||
// server has responded.
|
||||
return pubRequest.close();
|
||||
}).then((HttpClientResponse response) {
|
||||
var respond = (contents) {
|
||||
var tabs = JSON.decode(contents);
|
||||
var targets = _makeTargetList(tabs);
|
||||
request.response.write(JSON.encode(targets));
|
||||
request.response.close().catchError((e) {
|
||||
logger.severe('tabs from $chromeAddress:$chromePort failed');
|
||||
logger.severe(e.toString());
|
||||
});
|
||||
};
|
||||
response.transform(UTF8.decoder).listen(respond);
|
||||
}).catchError((e) {
|
||||
logger.severe('tabs from $chromeAddress:$chromePort failed');
|
||||
logger.severe(e.toString());
|
||||
});
|
||||
}
|
||||
|
||||
/// Forward [request] to pub.
|
||||
void _forwardToPub(HttpRequest request) {
|
||||
var path = request.uri.path;
|
||||
var method = request.method;
|
||||
logger.info('pub $method $path');
|
||||
_client.open(method, pubHost, pubPort, path)
|
||||
.then((HttpClientRequest pubRequest) {
|
||||
return pubRequest.close();
|
||||
}).then((HttpClientResponse response) {
|
||||
return request.response.addStream(response);
|
||||
}).then((_) => request.response.close())
|
||||
.catchError((e) {
|
||||
logger.severe('pub $method $path failed.');
|
||||
logger.severe(e.toString());
|
||||
});
|
||||
}
|
||||
|
||||
void _onHttpRequest(HttpRequest request) {
|
||||
// Allow cross origin requests.
|
||||
request.response.headers.add('Access-Control-Allow-Origin', '*');
|
||||
if (request.uri.path.startsWith(_CHROME_PREFIX)) {
|
||||
_getChromeTabs(request);
|
||||
} else {
|
||||
_forwardToPub(request);
|
||||
}
|
||||
}
|
||||
|
||||
/// Future completes to [this] on successful startup.
|
||||
Future start() {
|
||||
return HttpServer.bind(host, port).then((s) {
|
||||
_server = s;
|
||||
_server.listen(_onHttpRequest);
|
||||
print('ObsServe is running on ${_server.address}:${_server.port}');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
main(List<String> args) {
|
||||
hierarchicalLoggingEnabled = true;
|
||||
logger.level = Level.ALL;
|
||||
logger.onRecord.listen(print);
|
||||
new ObservatoryServer(args)..start();
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ export 'package:observatory/src/elements/script_view.dart';
|
|||
export 'package:observatory/src/elements/service_ref.dart';
|
||||
export 'package:observatory/src/elements/service_view.dart';
|
||||
export 'package:observatory/src/elements/sliding_checkbox.dart';
|
||||
export 'package:observatory/src/elements/timeline_page.dart';
|
||||
export 'package:observatory/src/elements/view_footer.dart';
|
||||
export 'package:observatory/src/elements/vm_connect.dart';
|
||||
export 'package:observatory/src/elements/vm_ref.dart';
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
<link rel="import" href="src/elements/script_view.html">
|
||||
<link rel="import" href="src/elements/service_ref.html">
|
||||
<link rel="import" href="src/elements/sliding_checkbox.html">
|
||||
<link rel="import" href="src/elements/timeline_page.html">
|
||||
<link rel="import" href="src/elements/view_footer.html">
|
||||
<link rel="import" href="src/elements/vm_connect.html">
|
||||
<link rel="import" href="src/elements/vm_ref.html">
|
||||
|
|
|
@ -141,6 +141,7 @@ class ObservatoryApplication extends Observable {
|
|||
_pageRegistry.add(new MetricsPage(this));
|
||||
_pageRegistry.add(new PortsPage(this));
|
||||
_pageRegistry.add(new LoggingPage(this));
|
||||
_pageRegistry.add(new TimelinePage(this));
|
||||
// Note that ErrorPage must be the last entry in the list as it is
|
||||
// the catch all.
|
||||
_pageRegistry.add(new ErrorPage(this));
|
||||
|
|
|
@ -425,3 +425,21 @@ class MetricsPage extends Page {
|
|||
|
||||
bool canVisit(Uri uri) => uri.path == 'metrics';
|
||||
}
|
||||
|
||||
class TimelinePage extends Page {
|
||||
TimelinePage(app) : super(app);
|
||||
|
||||
void onInstall() {
|
||||
if (element == null) {
|
||||
element = new Element.tag('timeline-page');
|
||||
}
|
||||
assert(element != null);
|
||||
}
|
||||
|
||||
void _visit(Uri uri) {
|
||||
assert(element != null);
|
||||
assert(canVisit(uri));
|
||||
}
|
||||
|
||||
bool canVisit(Uri uri) => uri.path == 'timeline';
|
||||
}
|
||||
|
|
78
runtime/observatory/lib/src/elements/timeline_page.dart
Normal file
78
runtime/observatory/lib/src/elements/timeline_page.dart
Normal file
|
@ -0,0 +1,78 @@
|
|||
// 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.
|
||||
|
||||
library timeline_page_element;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:html';
|
||||
import 'observatory_element.dart';
|
||||
import 'package:observatory/app.dart';
|
||||
import 'package:observatory/service_html.dart';
|
||||
import 'package:polymer/polymer.dart';
|
||||
|
||||
|
||||
@CustomTag('timeline-page')
|
||||
class TimelinePageElement extends ObservatoryElement {
|
||||
TimelinePageElement.created() : super.created();
|
||||
|
||||
attached() {
|
||||
super.attached();
|
||||
_resizeSubscription = window.onResize.listen((_) => _updateSize());
|
||||
_updateSize();
|
||||
}
|
||||
|
||||
detached() {
|
||||
super.detached();
|
||||
if (_resizeSubscription != null) {
|
||||
_resizeSubscription.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Future postMessage(String method) {
|
||||
IFrameElement e = $['root'];
|
||||
var message = {
|
||||
'method': method,
|
||||
'params': {
|
||||
'vmAddress': (app.vm as WebSocketVM).target.networkAddress
|
||||
}
|
||||
};
|
||||
e.contentWindow.postMessage(JSON.encode(message), window.location.href);
|
||||
return null;
|
||||
}
|
||||
|
||||
Future refresh() async {
|
||||
return postMessage('refresh');
|
||||
}
|
||||
|
||||
Future clear() async {
|
||||
await app.vm.invokeRpc('_clearVMTimeline', {});
|
||||
return postMessage('clear');
|
||||
}
|
||||
|
||||
Future recordOn() async {
|
||||
return app.vm.invokeRpc('_setVMTimelineFlag', {
|
||||
'_record': 'all',
|
||||
});
|
||||
}
|
||||
|
||||
Future recordOff() async {
|
||||
return app.vm.invokeRpc('_setVMTimelineFlag', {
|
||||
'_record': 'none',
|
||||
});
|
||||
}
|
||||
|
||||
_updateSize() {
|
||||
IFrameElement e = $['root'];
|
||||
final totalHeight = window.innerHeight;
|
||||
final top = e.offset.top;
|
||||
final bottomMargin = 32;
|
||||
final mainHeight = totalHeight - top - bottomMargin;
|
||||
e.style.setProperty('height', '${mainHeight}px');
|
||||
e.style.setProperty('width', '100%');
|
||||
}
|
||||
|
||||
|
||||
StreamSubscription _resizeSubscription;
|
||||
}
|
21
runtime/observatory/lib/src/elements/timeline_page.html
Normal file
21
runtime/observatory/lib/src/elements/timeline_page.html
Normal file
|
@ -0,0 +1,21 @@
|
|||
<link rel="import" href="../../../../packages/polymer/polymer.html">
|
||||
<link rel="import" href="nav_bar.html">
|
||||
<link rel="import" href="observatory_element.html">
|
||||
|
||||
<polymer-element name="timeline-page" extends="observatory-element">
|
||||
<template>
|
||||
<link rel="stylesheet" href="css/shared.css">
|
||||
<style>
|
||||
</style>
|
||||
<nav-bar>
|
||||
<top-nav-menu></top-nav-menu>
|
||||
<nav-menu link="{{ makeLink('/timeline') }}" anchor="timeline" last="{{ true }}"></nav-menu>
|
||||
<nav-refresh callback="{{ refresh }}"></nav-refresh>
|
||||
<nav-refresh callback="{{ clear }}" label="Clear"></nav-refresh>
|
||||
<nav-refresh callback="{{ recordOn }}" label="Start recording"></nav-refresh>
|
||||
<nav-refresh callback="{{ recordOff }}" label="Stop recording"></nav-refresh>
|
||||
</nav-bar>
|
||||
<iframe id="root" frameborder="0" src="../../../../timeline.html"></iframe>
|
||||
</template>
|
||||
</polymer-element>
|
||||
<script type="application/dart" src="timeline_page.dart"></script>
|
|
@ -56,6 +56,9 @@
|
|||
<div class="memberValue">
|
||||
See <a on-click="{{ goto }}" _href="{{ gotoLink('/flags') }}">flags</a>
|
||||
</div>
|
||||
<div class="memberValue">
|
||||
View <a on-click="{{ goto }}" _href="{{ gotoLink('/timeline') }}">timeline</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
|
|
|
@ -834,8 +834,8 @@ abstract class VM extends ServiceObjectOwner {
|
|||
await listenEventStream(kIsolateStream, _dispatchEventToIsolate);
|
||||
await listenEventStream(kDebugStream, _dispatchEventToIsolate);
|
||||
await listenEventStream(_kGraphStream, _dispatchEventToIsolate);
|
||||
} on FakeVMRpcException catch (e) {
|
||||
// ignore FakeVMRpcExceptions here.
|
||||
} on FakeVMRpcException catch (_) {
|
||||
// ignore FakeVMRpcExceptions here.
|
||||
}
|
||||
}
|
||||
return await invokeRpcNoUpgrade('getVM', {});
|
||||
|
|
|
@ -134,6 +134,8 @@
|
|||
'lib/src/elements/service_view.html',
|
||||
'lib/src/elements/sliding_checkbox.dart',
|
||||
'lib/src/elements/sliding_checkbox.html',
|
||||
'lib/src/elements/timeline_page.dart',
|
||||
'lib/src/elements/timeline_page.html',
|
||||
'lib/src/elements/view_footer.dart',
|
||||
'lib/src/elements/view_footer.html',
|
||||
'lib/src/elements/vm_connect.dart',
|
||||
|
@ -152,5 +154,8 @@
|
|||
'web/index.html',
|
||||
'web/main.dart',
|
||||
'web/favicon.ico',
|
||||
'web/third_party/trace_viewer_full.html',
|
||||
'web/timeline.js',
|
||||
'web/timeline.html',
|
||||
],
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ transformers:
|
|||
inline_stylesheets:
|
||||
lib/src/elements/css/shared.css: false
|
||||
packages/charted/charts/themes/quantum_theme.css: false
|
||||
$exclude: [web/third_party/*.html, web/timeline.html]
|
||||
- $dart2js:
|
||||
$include: "**/*.polymer.bootstrap.dart"
|
||||
suppressWarnings: false
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
if [ ! -d "web" ]; then
|
||||
echo "Error: you must run this script from the client directory."
|
||||
exit
|
||||
fi
|
||||
|
||||
PUB_PATH=pub
|
||||
PUB_ARGS="serve --hostname 127.0.0.1 --port 9191"
|
||||
DART_PATH=dart
|
||||
DART_ARGS="bin/server.dart --host 127.0.0.1 --port 9090"
|
||||
DART_ARGS="$DART_ARGS --pub-host 127.0.0.1 --pub-port 9191"
|
||||
|
||||
# Kill any child processes on exit.
|
||||
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT
|
||||
|
||||
echo "This script assumes that both *pub* and *dart* are in your PATH."
|
||||
echo "Launching Observatory server."
|
||||
echo "Launching pub."
|
||||
echo ""
|
||||
echo ""
|
||||
echo ""
|
||||
$DART_PATH $DART_ARGS &
|
||||
$PUB_PATH $PUB_ARGS
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// 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.
|
||||
// VMOptions=--error_on_bad_type --error_on_bad_override --complete_timeline
|
||||
|
||||
import 'dart:developer';
|
||||
import 'package:observatory/service_io.dart';
|
||||
import 'package:unittest/unittest.dart';
|
||||
|
||||
import 'test_helper.dart';
|
||||
|
||||
primeTimeline() {
|
||||
Timeline.startSync('apple');
|
||||
Timeline.finishSync();
|
||||
}
|
||||
|
||||
List<Map> filterForDartEvents(List<Map> events) {
|
||||
return events.where((event) => event['cat'] == 'Dart').toList();
|
||||
}
|
||||
|
||||
var tests = [
|
||||
(VM vm) async {
|
||||
Map result = await vm.invokeRpcNoUpgrade('_getVMTimeline', {});
|
||||
expect(result['type'], equals('_Timeline'));
|
||||
expect(result['traceEvents'], new isInstanceOf<List>());
|
||||
List<Map> dartEvents = filterForDartEvents(result['traceEvents']);
|
||||
expect(dartEvents.length, equals(1));
|
||||
Map dartEvent = dartEvents[0];
|
||||
expect(dartEvent['name'], equals('apple'));
|
||||
},
|
||||
];
|
||||
|
||||
main(args) async => runVMTests(args, tests, testeeBefore: primeTimeline);
|
4
runtime/observatory/web/third_party/README.md
vendored
Normal file
4
runtime/observatory/web/third_party/README.md
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
This directory contains a vulcanized version of trace-viewer:
|
||||
|
||||
https://github.com/catapult-project/catapult/wiki/Embedding-Trace-Viewer
|
||||
|
7113
runtime/observatory/web/third_party/trace_viewer_full.html
vendored
Normal file
7113
runtime/observatory/web/third_party/trace_viewer_full.html
vendored
Normal file
File diff suppressed because one or more lines are too long
28
runtime/observatory/web/timeline.html
Normal file
28
runtime/observatory/web/timeline.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html>
|
||||
<html style="height: 100%; width: 100%">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Dart VM Observatory Timeline</title>
|
||||
<script src="timeline.js"></script>
|
||||
<link rel="import" href="third_party/trace_viewer_full.html">
|
||||
<style>
|
||||
html, body {
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
margin: 0px;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#trace-viewer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#trace-viewer:focus {
|
||||
outline: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
93
runtime/observatory/web/timeline.js
Normal file
93
runtime/observatory/web/timeline.js
Normal file
|
@ -0,0 +1,93 @@
|
|||
// 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.
|
||||
|
||||
function onModelLoaded() {
|
||||
viewer.globalMode = true;
|
||||
viewer.model = model;
|
||||
}
|
||||
|
||||
function clearTimeline() {
|
||||
viewer.model = undefined;
|
||||
}
|
||||
|
||||
function onImportFail() {
|
||||
var overlay = new tr.ui.b.Overlay();
|
||||
overlay.textContent = tr.b.normalizeException(err).message;
|
||||
overlay.title = 'Import error';
|
||||
overlay.visible = true;
|
||||
console.log('import failed');
|
||||
}
|
||||
|
||||
function updateTimeline(events) {
|
||||
model = new tr.Model();
|
||||
var importer = new tr.importer.Import(model);
|
||||
var p = importer.importTracesWithProgressDialog([events]);
|
||||
p.then(onModelLoaded, onImportFail);
|
||||
}
|
||||
|
||||
function registerForMessages() {
|
||||
window.addEventListener("message", onMessage, false);
|
||||
}
|
||||
|
||||
function fetchUri(uri, onLoad, onError) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', uri, true);
|
||||
xhr.responseType = 'text';
|
||||
xhr.addEventListener("load", onLoad);
|
||||
xhr.addEventListener("error", onError);
|
||||
xhr.send();
|
||||
console.log('GET ' + uri);
|
||||
}
|
||||
|
||||
function fetchTimelineOnLoad(event) {
|
||||
var xhr = event.target;
|
||||
var response = JSON.parse(xhr.responseText);
|
||||
var result = response['result'];
|
||||
var traceEvents = result['traceEvents'];
|
||||
updateTimeline(traceEvents);
|
||||
}
|
||||
|
||||
function fetchTimelineOnError(event) {
|
||||
}
|
||||
|
||||
function fetchTimeline(vmAddress) {
|
||||
var parser = document.createElement('a');
|
||||
parser.href = vmAddress;
|
||||
var requestUri = 'http://' +
|
||||
parser.hostname +
|
||||
':' +
|
||||
parser.port +
|
||||
'/_getVMTimeline';
|
||||
fetchUri(requestUri, fetchTimelineOnLoad, fetchTimelineOnError);
|
||||
}
|
||||
|
||||
function onMessage(event) {
|
||||
var request = JSON.parse(event.data);
|
||||
var method = request['method'];
|
||||
var params = request['params'];
|
||||
switch (method) {
|
||||
case 'refresh':
|
||||
fetchTimeline(params['vmAddress']);
|
||||
break;
|
||||
case 'clear':
|
||||
clearTimeline();
|
||||
break;
|
||||
default:
|
||||
console.log('Unknown method:' + method + '.');
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var container = document.createElement('track-view-container');
|
||||
container.id = 'track_view_container';
|
||||
viewer = document.createElement('tr-ui-timeline-view');
|
||||
viewer.track_view_container = container;
|
||||
viewer.appendChild(container);
|
||||
viewer.id = 'trace-viewer';
|
||||
viewer.globalMode = true;
|
||||
document.body.appendChild(viewer);
|
||||
registerForMessages();
|
||||
});
|
||||
|
||||
console.log('loaded');
|
|
@ -93,6 +93,8 @@ const char* Dart::InitOnce(const uint8_t* vm_isolate_snapshot,
|
|||
Thread::InitOnceBeforeIsolate();
|
||||
Thread::EnsureInit();
|
||||
Timeline::InitOnce();
|
||||
Thread* thread = Thread::Current();
|
||||
thread->set_name("Dart_Initialize");
|
||||
TimelineDurationScope tds(Timeline::GetVMStream(),
|
||||
"Dart::InitOnce");
|
||||
Isolate::InitOnce();
|
||||
|
|
|
@ -5737,6 +5737,8 @@ DART_EXPORT void Dart_TimelineSetRecordedStreams(int64_t stream_mask) {
|
|||
(stream_mask & DART_TIMELINE_STREAM_COMPILER) != 0);
|
||||
isolate->GetDartStream()->set_enabled(
|
||||
(stream_mask & DART_TIMELINE_STREAM_DART) != 0);
|
||||
isolate->GetDebuggerStream()->set_enabled(
|
||||
(stream_mask & DART_TIMELINE_STREAM_DEBUGGER) != 0);
|
||||
isolate->GetEmbedderStream()->set_enabled(
|
||||
(stream_mask & DART_TIMELINE_STREAM_EMBEDDER) != 0);
|
||||
isolate->GetGCStream()->set_enabled(
|
||||
|
@ -5753,6 +5755,8 @@ DART_EXPORT void Dart_GlobalTimelineSetRecordedStreams(int64_t stream_mask) {
|
|||
(stream_mask & DART_TIMELINE_STREAM_COMPILER) != 0;
|
||||
const bool dart_enabled =
|
||||
(stream_mask & DART_TIMELINE_STREAM_DART) != 0;
|
||||
const bool debugger_enabled =
|
||||
(stream_mask & DART_TIMELINE_STREAM_DEBUGGER) != 0;
|
||||
const bool embedder_enabled =
|
||||
(stream_mask & DART_TIMELINE_STREAM_EMBEDDER) != 0;
|
||||
const bool gc_enabled = (stream_mask & DART_TIMELINE_STREAM_GC) != 0;
|
||||
|
@ -5761,6 +5765,7 @@ DART_EXPORT void Dart_GlobalTimelineSetRecordedStreams(int64_t stream_mask) {
|
|||
Timeline::SetStreamAPIEnabled(api_enabled);
|
||||
Timeline::SetStreamCompilerEnabled(compiler_enabled);
|
||||
Timeline::SetStreamDartEnabled(dart_enabled);
|
||||
Timeline::SetStreamDebuggerEnabled(debugger_enabled);
|
||||
Timeline::SetStreamEmbedderEnabled(embedder_enabled);
|
||||
Timeline::SetStreamGCEnabled(gc_enabled);
|
||||
Timeline::SetStreamIsolateEnabled(isolate_enabled);
|
||||
|
|
|
@ -2522,7 +2522,11 @@ void Debugger::Pause(DebuggerEvent* event) {
|
|||
// We are about to invoke the debuggers event handler. Disable interrupts
|
||||
// for this thread while waiting for debug commands over the service protocol.
|
||||
{
|
||||
DisableThreadInterruptsScope dtis(Thread::Current());
|
||||
Thread* thread = Thread::Current();
|
||||
DisableThreadInterruptsScope dtis(thread);
|
||||
TimelineDurationScope tds(thread,
|
||||
isolate_->GetDebuggerStream(),
|
||||
"Debugger Pause");
|
||||
InvokeEventHandler(event);
|
||||
}
|
||||
|
||||
|
|
|
@ -864,7 +864,6 @@ Isolate* Isolate::Init(const char* name_prefix,
|
|||
// Initialize Timeline streams.
|
||||
#define ISOLATE_TIMELINE_STREAM_INIT(name, enabled_by_default) \
|
||||
result->stream_##name##_.Init(#name, \
|
||||
Timeline::EnableStreamByDefault(#name) || \
|
||||
enabled_by_default, \
|
||||
Timeline::Stream##name##EnabledFlag());
|
||||
ISOLATE_TIMELINE_STREAM_LIST(ISOLATE_TIMELINE_STREAM_INIT);
|
||||
|
|
|
@ -2492,6 +2492,90 @@ static bool GetVMMetric(Thread* thread, JSONStream* js) {
|
|||
}
|
||||
|
||||
|
||||
static const MethodParameter* set_vm_timeline_flag_params[] = {
|
||||
NO_ISOLATE_PARAMETER,
|
||||
new MethodParameter("_record", true),
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
static bool SetVMTimelineFlag(Thread* thread, JSONStream* js) {
|
||||
Isolate* isolate = thread->isolate();
|
||||
ASSERT(isolate != NULL);
|
||||
StackZone zone(thread);
|
||||
|
||||
bool recording = strcmp(js->LookupParam("_record"), "all") == 0;
|
||||
Timeline::SetStreamAPIEnabled(recording);
|
||||
Timeline::SetStreamCompilerEnabled(recording);
|
||||
Timeline::SetStreamDartEnabled(recording);
|
||||
Timeline::SetStreamDebuggerEnabled(recording);
|
||||
Timeline::SetStreamEmbedderEnabled(recording);
|
||||
Timeline::SetStreamGCEnabled(recording);
|
||||
Timeline::SetStreamIsolateEnabled(recording);
|
||||
|
||||
PrintSuccess(js);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const MethodParameter* get_vm_timeline_flag_params[] = {
|
||||
NO_ISOLATE_PARAMETER,
|
||||
new MethodParameter("_record", false),
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
static bool GetVMTimelineFlag(Thread* thread, JSONStream* js) {
|
||||
Isolate* isolate = thread->isolate();
|
||||
ASSERT(isolate != NULL);
|
||||
StackZone zone(thread);
|
||||
|
||||
js->PrintError(kFeatureDisabled, "TODO(johnmccutchan)");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const MethodParameter* clear_vm_timeline_params[] = {
|
||||
NO_ISOLATE_PARAMETER,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
static bool ClearVMTimeline(Thread* thread, JSONStream* js) {
|
||||
Isolate* isolate = thread->isolate();
|
||||
ASSERT(isolate != NULL);
|
||||
StackZone zone(thread);
|
||||
|
||||
Timeline::Clear();
|
||||
|
||||
PrintSuccess(js);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const MethodParameter* get_vm_timeline_params[] = {
|
||||
NO_ISOLATE_PARAMETER,
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
static bool GetVMTimeline(Thread* thread, JSONStream* js) {
|
||||
Isolate* isolate = thread->isolate();
|
||||
ASSERT(isolate != NULL);
|
||||
StackZone zone(thread);
|
||||
Timeline::ReclaimCachedBlocksFromThreads();
|
||||
TimelineEventRecorder* timeline_recorder = Timeline::recorder();
|
||||
// TODO(johnmccutchan): Return an error.
|
||||
ASSERT(timeline_recorder != NULL);
|
||||
TimelineEventFilter filter;
|
||||
timeline_recorder->PrintJSON(js, &filter);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static const MethodParameter* resume_params[] = {
|
||||
ISOLATE_PARAMETER,
|
||||
NULL,
|
||||
|
@ -3333,6 +3417,8 @@ static ServiceMethodDescriptor service_methods_[] = {
|
|||
add_breakpoint_at_activation_params },
|
||||
{ "_clearCpuProfile", ClearCpuProfile,
|
||||
clear_cpu_profile_params },
|
||||
{ "_clearVMTimeline", ClearVMTimeline,
|
||||
clear_vm_timeline_params, },
|
||||
{ "evaluate", Evaluate,
|
||||
evaluate_params },
|
||||
{ "evaluateInFrame", EvaluateInFrame,
|
||||
|
@ -3387,6 +3473,10 @@ static ServiceMethodDescriptor service_methods_[] = {
|
|||
get_vm_metric_params },
|
||||
{ "_getVMMetricList", GetVMMetricList,
|
||||
get_vm_metric_list_params },
|
||||
{ "_getVMTimeline", GetVMTimeline,
|
||||
get_vm_timeline_params },
|
||||
{ "_getVMTimelineFlag", GetVMTimelineFlag,
|
||||
get_vm_timeline_flag_params },
|
||||
{ "pause", Pause,
|
||||
pause_params },
|
||||
{ "removeBreakpoint", RemoveBreakpoint,
|
||||
|
@ -3409,6 +3499,8 @@ static ServiceMethodDescriptor service_methods_[] = {
|
|||
set_trace_class_allocation_params },
|
||||
{ "setVMName", SetVMName,
|
||||
set_vm_name_params },
|
||||
{ "_setVMTimelineFlag", SetVMTimelineFlag,
|
||||
set_vm_timeline_flag_params },
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -214,6 +214,7 @@ void Thread::EnsureInit() {
|
|||
Thread::Thread(bool init_vm_constants)
|
||||
: id_(OSThread::GetCurrentThreadId()),
|
||||
join_id_(OSThread::GetCurrentThreadJoinId()),
|
||||
trace_id_(OSThread::GetCurrentThreadTraceId()),
|
||||
thread_interrupt_disabled_(1), // Thread interrupts disabled by default.
|
||||
isolate_(NULL),
|
||||
heap_(NULL),
|
||||
|
@ -228,7 +229,8 @@ Thread::Thread(bool init_vm_constants)
|
|||
vm_tag_(0),
|
||||
pending_functions_(GrowableObjectArray::null()),
|
||||
no_callback_scope_depth_(0),
|
||||
thread_list_next_(NULL) {
|
||||
thread_list_next_(NULL),
|
||||
name_(NULL) {
|
||||
ClearState();
|
||||
|
||||
#define DEFAULT_INIT(type_name, member_name, init_expr, default_init_value) \
|
||||
|
|
|
@ -363,6 +363,21 @@ LEAF_RUNTIME_ENTRY_LIST(DEFINE_OFFSET_METHOD)
|
|||
return join_id_;
|
||||
}
|
||||
|
||||
ThreadId trace_id() const {
|
||||
ASSERT(trace_id_ != OSThread::kInvalidThreadJoinId);
|
||||
return trace_id_;
|
||||
}
|
||||
|
||||
const char* name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
void set_name(const char* name) {
|
||||
ASSERT(Thread::Current() == this);
|
||||
ASSERT(name_ == NULL);
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
// Used to temporarily disable or enable thread interrupts.
|
||||
void DisableThreadInterrupts();
|
||||
void EnableThreadInterrupts();
|
||||
|
@ -410,6 +425,7 @@ LEAF_RUNTIME_ENTRY_LIST(DEFINE_OFFSET_METHOD)
|
|||
|
||||
const ThreadId id_;
|
||||
const ThreadId join_id_;
|
||||
const ThreadId trace_id_;
|
||||
uintptr_t thread_interrupt_disabled_;
|
||||
Isolate* isolate_;
|
||||
Heap* heap_;
|
||||
|
@ -459,6 +475,9 @@ LEAF_RUNTIME_ENTRY_LIST(DECLARE_MEMBERS)
|
|||
// All |Thread|s are registered in the thread list.
|
||||
Thread* thread_list_next_;
|
||||
|
||||
// A name for this thread.
|
||||
const char* name_;
|
||||
|
||||
static Thread* thread_list_head_;
|
||||
static Mutex* thread_list_lock_;
|
||||
|
||||
|
|
|
@ -418,6 +418,8 @@ void ThreadPool::Worker::Shutdown() {
|
|||
// static
|
||||
void ThreadPool::Worker::Main(uword args) {
|
||||
Thread::EnsureInit();
|
||||
Thread* thread = Thread::Current();
|
||||
thread->set_name("Dart ThreadPool Worker");
|
||||
Worker* worker = reinterpret_cast<Worker*>(args);
|
||||
ThreadId id = OSThread::GetCurrentThreadId();
|
||||
ThreadJoinId join_id = OSThread::GetCurrentThreadJoinId();
|
||||
|
|
|
@ -84,7 +84,7 @@ void Timeline::InitOnce() {
|
|||
vm_stream_->Init("VM", EnableStreamByDefault("VM"), NULL);
|
||||
// Global overrides.
|
||||
#define ISOLATE_TIMELINE_STREAM_FLAG_DEFAULT(name, not_used) \
|
||||
stream_##name##_enabled_ = false;
|
||||
stream_##name##_enabled_ = EnableStreamByDefault(#name);
|
||||
ISOLATE_TIMELINE_STREAM_LIST(ISOLATE_TIMELINE_STREAM_FLAG_DEFAULT)
|
||||
#undef ISOLATE_TIMELINE_STREAM_FLAG_DEFAULT
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ TimelineEventRecorder* Timeline::recorder() {
|
|||
|
||||
bool Timeline::EnableStreamByDefault(const char* stream_name) {
|
||||
// TODO(johnmccutchan): Allow for command line control over streams.
|
||||
return (FLAG_timeline_dir != NULL) || FLAG_timing;
|
||||
return (FLAG_timeline_dir != NULL) || FLAG_timing || FLAG_complete_timeline;
|
||||
}
|
||||
|
||||
|
||||
|
@ -141,11 +141,21 @@ void Timeline::ReclaimCachedBlocksFromThreads() {
|
|||
}
|
||||
|
||||
|
||||
void Timeline::Clear() {
|
||||
TimelineEventRecorder* recorder = Timeline::recorder();
|
||||
if (recorder == NULL) {
|
||||
return;
|
||||
}
|
||||
ReclaimCachedBlocksFromThreads();
|
||||
recorder->Clear();
|
||||
}
|
||||
|
||||
|
||||
TimelineEventRecorder* Timeline::recorder_ = NULL;
|
||||
TimelineStream* Timeline::vm_stream_ = NULL;
|
||||
|
||||
#define ISOLATE_TIMELINE_STREAM_DEFINE_FLAG(name, enabled_by_default) \
|
||||
bool Timeline::stream_##name##_enabled_ = false;
|
||||
bool Timeline::stream_##name##_enabled_ = enabled_by_default;
|
||||
ISOLATE_TIMELINE_STREAM_LIST(ISOLATE_TIMELINE_STREAM_DEFINE_FLAG)
|
||||
#undef ISOLATE_TIMELINE_STREAM_DEFINE_FLAG
|
||||
|
||||
|
@ -631,6 +641,26 @@ TimelineEventRecorder::TimelineEventRecorder()
|
|||
|
||||
|
||||
void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const {
|
||||
ThreadIterator it;
|
||||
while (it.HasNext()) {
|
||||
Thread* thread = it.Next();
|
||||
const char* thread_name = thread->name();
|
||||
if (thread_name == NULL) {
|
||||
// Only emit a thread name if one was set.
|
||||
continue;
|
||||
}
|
||||
JSONObject obj(events);
|
||||
int64_t pid = OS::ProcessId();
|
||||
int64_t tid = OSThread::ThreadIdToIntPtr(thread->trace_id());
|
||||
obj.AddProperty("name", "thread_name");
|
||||
obj.AddProperty("ph", "M");
|
||||
obj.AddProperty64("pid", pid);
|
||||
obj.AddProperty64("tid", tid);
|
||||
{
|
||||
JSONObject args(&obj, "args");
|
||||
args.AddPropertyF("name", "%s (%" Pd64 ")", thread_name, tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -783,7 +813,8 @@ TimelineEventRingRecorder::~TimelineEventRingRecorder() {
|
|||
|
||||
void TimelineEventRingRecorder::PrintJSONEvents(
|
||||
JSONArray* events,
|
||||
TimelineEventFilter* filter) const {
|
||||
TimelineEventFilter* filter) {
|
||||
MutexLocker ml(&lock_);
|
||||
intptr_t block_offset = FindOldestBlockIndex();
|
||||
if (block_offset == -1) {
|
||||
// All blocks are empty.
|
||||
|
@ -807,7 +838,6 @@ void TimelineEventRingRecorder::PrintJSONEvents(
|
|||
|
||||
void TimelineEventRingRecorder::PrintJSON(JSONStream* js,
|
||||
TimelineEventFilter* filter) {
|
||||
MutexLocker ml(&lock_);
|
||||
JSONObject topLevel(js);
|
||||
topLevel.AddProperty("type", "_Timeline");
|
||||
{
|
||||
|
@ -843,6 +873,15 @@ TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() {
|
|||
}
|
||||
|
||||
|
||||
void TimelineEventRingRecorder::Clear() {
|
||||
MutexLocker ml(&lock_);
|
||||
for (intptr_t i = 0; i < num_blocks_; i++) {
|
||||
TimelineEventBlock* block = blocks_[i];
|
||||
block->Reset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
intptr_t TimelineEventRingRecorder::FindOldestBlockIndex() const {
|
||||
int64_t earliest_time = kMaxInt64;
|
||||
intptr_t earliest_index = -1;
|
||||
|
@ -923,7 +962,6 @@ TimelineEventEndlessRecorder::TimelineEventEndlessRecorder()
|
|||
|
||||
void TimelineEventEndlessRecorder::PrintJSON(JSONStream* js,
|
||||
TimelineEventFilter* filter) {
|
||||
MutexLocker ml(&lock_);
|
||||
JSONObject topLevel(js);
|
||||
topLevel.AddProperty("type", "_Timeline");
|
||||
{
|
||||
|
@ -977,9 +1015,9 @@ TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked() {
|
|||
|
||||
void TimelineEventEndlessRecorder::PrintJSONEvents(
|
||||
JSONArray* events,
|
||||
TimelineEventFilter* filter) const {
|
||||
TimelineEventFilter* filter) {
|
||||
MutexLocker ml(&lock_);
|
||||
TimelineEventBlock* current = head_;
|
||||
|
||||
while (current != NULL) {
|
||||
if (!filter->IncludeBlock(current)) {
|
||||
current = current->next();
|
||||
|
|
|
@ -30,6 +30,7 @@ class Zone;
|
|||
V(API, false) \
|
||||
V(Compiler, false) \
|
||||
V(Dart, false) \
|
||||
V(Debugger, false) \
|
||||
V(Embedder, false) \
|
||||
V(GC, false) \
|
||||
V(Isolate, false) \
|
||||
|
@ -52,6 +53,8 @@ class Timeline : public AllStatic {
|
|||
// Reclaim all |TimelineEventBlocks|s that are cached by threads.
|
||||
static void ReclaimCachedBlocksFromThreads();
|
||||
|
||||
static void Clear();
|
||||
|
||||
#define ISOLATE_TIMELINE_STREAM_FLAGS(name, not_used) \
|
||||
static const bool* Stream##name##EnabledFlag() { \
|
||||
return &stream_##name##_enabled_; \
|
||||
|
@ -523,6 +526,7 @@ class TimelineEventRecorder {
|
|||
virtual void CompleteEvent(TimelineEvent* event) = 0;
|
||||
virtual TimelineEventBlock* GetHeadBlockLocked() = 0;
|
||||
virtual TimelineEventBlock* GetNewBlockLocked() = 0;
|
||||
virtual void Clear() = 0;
|
||||
|
||||
// Utility method(s).
|
||||
void PrintJSONMeta(JSONArray* array) const;
|
||||
|
@ -560,8 +564,9 @@ class TimelineEventRingRecorder : public TimelineEventRecorder {
|
|||
TimelineEventBlock* GetHeadBlockLocked();
|
||||
intptr_t FindOldestBlockIndex() const;
|
||||
TimelineEventBlock* GetNewBlockLocked();
|
||||
void Clear();
|
||||
|
||||
void PrintJSONEvents(JSONArray* array, TimelineEventFilter* filter) const;
|
||||
void PrintJSONEvents(JSONArray* array, TimelineEventFilter* filter);
|
||||
|
||||
TimelineEventBlock** blocks_;
|
||||
intptr_t capacity_;
|
||||
|
@ -590,6 +595,8 @@ class TimelineEventStreamingRecorder : public TimelineEventRecorder {
|
|||
TimelineEventBlock* GetHeadBlockLocked() {
|
||||
return NULL;
|
||||
}
|
||||
void Clear() {
|
||||
}
|
||||
TimelineEvent* StartEvent();
|
||||
void CompleteEvent(TimelineEvent* event);
|
||||
};
|
||||
|
@ -610,12 +617,10 @@ class TimelineEventEndlessRecorder : public TimelineEventRecorder {
|
|||
void CompleteEvent(TimelineEvent* event);
|
||||
TimelineEventBlock* GetNewBlockLocked();
|
||||
TimelineEventBlock* GetHeadBlockLocked();
|
||||
|
||||
void PrintJSONEvents(JSONArray* array, TimelineEventFilter* filter) const;
|
||||
|
||||
// Useful only for testing. Only works for one thread.
|
||||
void Clear();
|
||||
|
||||
void PrintJSONEvents(JSONArray* array, TimelineEventFilter* filter);
|
||||
|
||||
TimelineEventBlock* head_;
|
||||
intptr_t block_index_;
|
||||
|
||||
|
|
Loading…
Reference in a new issue