db07faaa8c
- [x] Remove Stop / Start buttons and replace with fine grained category control. - [x] Restrict the cpu profile to the time span of the timeline. - [x] Display recorder name. - [x] Show a popup when fetching the timeline from the VM. - [x] Show a popup when fetching the cpu profile(s) from the VM. Fixes #26401 Fixes #26394 BUG= R=rmacnak@google.com Review URL: https://codereview.chromium.org/1972523003 .
256 lines
7.5 KiB
JavaScript
256 lines
7.5 KiB
JavaScript
// 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.
|
|
|
|
// See also timeline_message_handler.js.
|
|
|
|
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 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);
|
|
}
|
|
|
|
var traceObject;
|
|
var pendingRequests;
|
|
var loadingOverlay;
|
|
|
|
function showLoadingOverlay(msg) {
|
|
if (!loadingOverlay) {
|
|
loadingOverlay = new tr.ui.b.Overlay();
|
|
}
|
|
loadingOverlay.textContent = msg;
|
|
loadingOverlay.title = 'Loading...';
|
|
loadingOverlay.visible = true;
|
|
}
|
|
|
|
function hideLoadingOverlay() {
|
|
if (!loadingOverlay) {
|
|
return;
|
|
}
|
|
loadingOverlay.visible = false;
|
|
loadingOverlay = undefined;
|
|
}
|
|
|
|
function gotReponse() {
|
|
pendingRequests--;
|
|
if (pendingRequests == 0) {
|
|
console.log("Got all timeline parts");
|
|
updateTimeline(traceObject);
|
|
hideLoadingOverlay();
|
|
}
|
|
}
|
|
|
|
function processTimelineResponse(response) {
|
|
if (response.error) {
|
|
// Maybe profiling is disabled.
|
|
console.log("ERROR " + response.error.message);
|
|
} else {
|
|
var result = response['result'];
|
|
var newStackFrames = result['stackFrames']; // Map.
|
|
var newTraceEvents = result['traceEvents']; // List.
|
|
|
|
// Merge in timeline events.
|
|
traceObject.traceEvents = traceObject.traceEvents.concat(newTraceEvents);
|
|
for (var key in newStackFrames) {
|
|
if (newStackFrames.hasOwnProperty(key)) {
|
|
traceObject.stackFrames[key] = newStackFrames[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
gotReponse();
|
|
}
|
|
|
|
function fetchTimelineOnLoad(event) {
|
|
var xhr = event.target;
|
|
var response = JSON.parse(xhr.responseText);
|
|
processTimelineResponse(response);
|
|
}
|
|
|
|
function fetchTimelineOnError(event) {
|
|
var xhr = event.target;
|
|
console.log(xhr.statusText);
|
|
gotReponse();
|
|
}
|
|
|
|
function fetchCPUProfile(vmAddress, isolateIds, timeOrigin, timeExtent) {
|
|
showLoadingOverlay('Fetching CPU profile(s) from Dart VM');
|
|
var parser = document.createElement('a');
|
|
parser.href = vmAddress;
|
|
pendingRequests += isolateIds.length;
|
|
for (var i = 0; i < isolateIds.length; i++) {
|
|
var isolateId = isolateIds[i];
|
|
var requestUri = 'http://' +
|
|
parser.hostname +
|
|
':' +
|
|
parser.port +
|
|
'/_getCpuProfileTimeline?tags=VMUser&isolateId=' +
|
|
isolateId +
|
|
'&timeOriginMicros=' + timeOrigin +
|
|
'&timeExtentMicros=' + timeExtent;
|
|
fetchUri(requestUri, fetchTimelineOnLoad, fetchTimelineOnError);
|
|
}
|
|
}
|
|
|
|
function fetchTimeline(vmAddress, isolateIds) {
|
|
// Reset combined timeline.
|
|
traceObject = {
|
|
'stackFrames': {},
|
|
'traceEvents': []
|
|
};
|
|
pendingRequests = 1;
|
|
|
|
showLoadingOverlay('Fetching timeline from Dart VM');
|
|
var parser = document.createElement('a');
|
|
parser.href = vmAddress;
|
|
var requestUri = 'http://' +
|
|
parser.hostname +
|
|
':' +
|
|
parser.port +
|
|
'/_getVMTimeline';
|
|
fetchUri(requestUri, function(event) {
|
|
// Grab the response.
|
|
var xhr = event.target;
|
|
var response = JSON.parse(xhr.responseText);
|
|
// Extract the time origin and extent.
|
|
var timeOrigin = response['result']['timeOriginMicros'];
|
|
var timeExtent = response['result']['timeExtentMicros'];
|
|
console.assert(Number.isInteger(timeOrigin), timeOrigin);
|
|
console.assert(Number.isInteger(timeExtent), timeExtent);
|
|
console.log(timeOrigin);
|
|
console.log(timeExtent);
|
|
// fetchCPUProfile.
|
|
fetchCPUProfile(vmAddress, isolateIds, timeOrigin, timeExtent);
|
|
// This must happen after 'fetchCPUProfile';
|
|
processTimelineResponse(response);
|
|
}, fetchTimelineOnError);
|
|
}
|
|
|
|
function saveTimeline() {
|
|
if (pendingRequests > 0) {
|
|
var overlay = new tr.ui.b.Overlay();
|
|
overlay.textContent = 'Cannot save timeline while fetching one.';
|
|
overlay.title = 'Save error';
|
|
overlay.visible = true;
|
|
console.log('cannot save timeline while fetching one.');
|
|
return;
|
|
}
|
|
if (!traceObject ||
|
|
!traceObject.traceEvents ||
|
|
(traceObject.traceEvents.length == 0)) {
|
|
var overlay = new tr.ui.b.Overlay();
|
|
overlay.textContent = 'Cannot save an empty timeline.';
|
|
overlay.title = 'Save error';
|
|
overlay.visible = true;
|
|
console.log('Cannot save an empty timeline.');
|
|
return;
|
|
}
|
|
var blob = new Blob([JSON.stringify(traceObject)],
|
|
{type: 'application/json'});
|
|
var blobUrl = URL.createObjectURL(blob);
|
|
var link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');
|
|
link.href = blobUrl;
|
|
var now = new Date();
|
|
var defaultFilename = "dart-timeline-" +
|
|
now.getFullYear() +
|
|
"-" +
|
|
now.getMonth() +
|
|
"-" +
|
|
now.getDate() +
|
|
".json";
|
|
var filename = window.prompt('Save as', defaultFilename);
|
|
if (filename) {
|
|
link.download = filename;
|
|
link.click();
|
|
}
|
|
}
|
|
|
|
function loadTimeline() {
|
|
if (pendingRequests > 0) {
|
|
var overlay = new tr.ui.b.Overlay();
|
|
overlay.textContent = 'Cannot load timeline while fetching one.';
|
|
overlay.title = 'Save error';
|
|
overlay.visible = true;
|
|
console.log('Cannot load timeline while fetching one.');
|
|
return;
|
|
}
|
|
var inputElement = document.createElement('input');
|
|
inputElement.type = 'file';
|
|
inputElement.multiple = false;
|
|
|
|
var changeFired = false;
|
|
inputElement.addEventListener('change', function(e) {
|
|
if (changeFired)
|
|
return;
|
|
changeFired = true;
|
|
|
|
var file = inputElement.files[0];
|
|
var reader = new FileReader();
|
|
reader.onload = function(event) {
|
|
try {
|
|
traceObject = JSON.parse(event.target.result);
|
|
updateTimeline(traceObject);
|
|
} catch (error) {
|
|
tr.ui.b.Overlay.showError('Error while loading file: ' + error);
|
|
}
|
|
};
|
|
reader.onerror = function(event) {
|
|
tr.ui.b.Overlay.showError('Error while loading file: ' + event);
|
|
};
|
|
reader.onabort = function(event) {
|
|
tr.ui.b.Overlay.showError('Error while loading file: ' + event);
|
|
}
|
|
reader.readAsText(file);
|
|
});
|
|
inputElement.click();
|
|
}
|
|
|
|
window.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);
|
|
timeline_loaded = true;
|
|
console.log('DOMContentLoaded');
|
|
if (timeline_vm_address != undefined) {
|
|
console.log('Triggering delayed timeline refresh.');
|
|
fetchTimeline(timeline_vm_address, timeline_isolates);
|
|
timeline_vm_address = undefined;
|
|
timeline_isolates = undefined;
|
|
}
|
|
});
|
|
|
|
console.log('timeline.js loaded');
|