Index: chrome/browser/resources/profiler/profiler.js |
diff --git a/chrome/browser/resources/profiler/profiler.js b/chrome/browser/resources/profiler/profiler.js |
index f7caaa24236ff25ecbef0526112efc2542d05e0d..064b5c9846e55113e63cda0aef34c7b29c358b0e 100644 |
--- a/chrome/browser/resources/profiler/profiler.js |
+++ b/chrome/browser/resources/profiler/profiler.js |
@@ -104,7 +104,13 @@ var MainView = (function() { |
var TOGGLE_SNAPSHOTS_LINK_ID = 'snapshots-link'; |
var SNAPSHOTS_ROW = 'snapshots-row'; |
var SNAPSHOT_SELECTION_SUMMARY_ID = 'snapshot-selection-summary'; |
- var TAKE_SNAPSHOT_BUTTON_ID = 'snapshot-button'; |
+ var TAKE_SNAPSHOT_BUTTON_ID = 'take-snapshot-button'; |
+ |
+ var SAVE_SNAPSHOTS_BUTTON_ID = 'save-snapshots-button'; |
+ var SNAPSHOT_FILE_LOADER_ID = 'snapshot-file-loader'; |
+ var LOAD_ERROR_ID = 'file-load-error'; |
+ |
+ var DOWNLOAD_IFRAME_ID = 'download-iframe'; |
// -------------------------------------------------------------------------- |
// Row keys |
@@ -799,6 +805,17 @@ var MainView = (function() { |
} |
} |
+ /** |
+ * Set the visibility state of a node. |
+ */ |
+ function setNodeDisplay(n, visible) { |
+ if (visible) { |
+ n.style.display = ''; |
+ } else { |
+ n.style.display = 'none'; |
+ } |
+ } |
+ |
// -------------------------------------------------------------------------- |
// Functions that augment, bucket, and compute aggregates for the input data. |
// -------------------------------------------------------------------------- |
@@ -1203,6 +1220,9 @@ var MainView = (function() { |
var pid = data.process_id; |
var ptype = data.process_type; |
+ // Save the browser's representation of the data |
+ snapshot.origData.push(data); |
+ |
// Augment each data row with the process information. |
var rows = data.list; |
for (var i = 0; i < rows.length; ++i) { |
@@ -1312,7 +1332,14 @@ var MainView = (function() { |
var summaryDiv = $(SNAPSHOT_SELECTION_SUMMARY_ID); |
var selectedSnapshots = this.getSelectedSnapshotIndexes_(); |
- if (selectedSnapshots.length == 1) { |
+ if (selectedSnapshots.length == 0) { |
+ // This can occur during an attempt to load a file or following file |
+ // load failure. We just ignore it and move on. |
+ } else if (selectedSnapshots.length == 1) { |
+ // If only one snapshot is chosen then we will display that snapshot's |
+ // data in its entirety. |
+ this.flatData_ = this.snapshots_[selectedSnapshots[0]].flatData; |
+ |
// Don't bother displaying any text when just 1 snapshot is selected, |
// since it is obvious what this should do. |
summaryDiv.innerText = ''; |
@@ -1739,12 +1766,15 @@ var MainView = (function() { |
g_browserBridge.sendResetData.bind(g_browserBridge); |
$(TAKE_SNAPSHOT_BUTTON_ID).onclick = this.takeSnapshot_.bind(this); |
+ |
+ $(SAVE_SNAPSHOTS_BUTTON_ID).onclick = this.saveSnapshots_.bind(this); |
+ $(SNAPSHOT_FILE_LOADER_ID).onchange = this.loadFileChanged_.bind(this); |
}, |
takeSnapshot_: function() { |
// Start a new empty snapshot. Make note of the current time, so we know |
// when the snaphot was taken. |
- this.snapshots_.push({flatData: [], time: getTimeMillis()}); |
+ this.snapshots_.push({flatData: [], origData: [], time: getTimeMillis()}); |
// Update the UI to reflect the new snapshot. |
this.addSnapshotToList_(this.snapshots_.length - 1); |
@@ -1754,6 +1784,106 @@ var MainView = (function() { |
g_browserBridge.sendGetData(); |
}, |
+ saveSnapshots_: function() { |
+ var snapshots = []; |
+ for (var i = 0; i < this.snapshots_.length; ++i) { |
+ snapshots.push({ data: this.snapshots_[i].origData, |
+ timestamp: Math.floor( |
+ this.snapshots_[i].time / 1000) }); |
+ } |
+ |
+ var dump = { |
+ 'userAgent': navigator.userAgent, |
+ 'version': 1, |
+ 'snapshots': snapshots |
+ } |
+ |
+ var dumpText = JSON.stringify(dump, null, ' '); |
+ var blobBuilder = new WebKitBlobBuilder(); |
+ blobBuilder.append(dumpText, 'native'); |
+ var textBlob = blobBuilder.getBlob('octet/stream'); |
+ var blobUrl = window.webkitURL.createObjectURL(textBlob); |
+ $(DOWNLOAD_IFRAME_ID).src = blobUrl; |
+ }, |
+ |
+ loadFileChanged_: function() { |
+ this.loadSnapshots_($(SNAPSHOT_FILE_LOADER_ID).files[0]) |
+ }, |
+ |
+ loadSnapshots_: function(file) { |
+ if (file) { |
+ var fileReader = new FileReader(); |
+ |
+ fileReader.onload = this.onLoadSnapshotsFile_.bind(this, file); |
+ fileReader.onerror = this.onLoadSnapshotsFileError_.bind(this, file); |
+ |
+ fileReader.readAsText(file); |
+ } |
+ }, |
+ |
+ onLoadSnapshotsFile_: function(file, event) { |
+ try { |
+ var parsed = null; |
+ parsed = JSON.parse(event.target.result) |
+ |
+ if (parsed.version != 1) { |
+ throw new Error('Unrecognized version: ' + parsed.version); |
+ } |
+ |
+ if (parsed.snapshots.length < 1) { |
+ throw new Error('File contains no data'); |
+ } |
+ |
+ this.displayLoadedFile_(file, parsed); |
+ this.hideFileLoadError_(); |
+ } catch (error) { |
+ this.displayFileLoadError_('File load failure: ' + error.message); |
+ } |
+ }, |
+ |
+ clearExistingSnapshots_: function() { |
+ var tbody = $('snapshots-tbody'); |
+ this.snapshots_ = []; |
+ tbody.innerHTML = ''; |
+ this.updateMergedDataSoon_(); |
+ }, |
+ |
+ displayLoadedFile_: function (file, content) { |
+ this.clearExistingSnapshots_(); |
+ $(TAKE_SNAPSHOT_BUTTON_ID).disabled = true; |
+ $(SAVE_SNAPSHOTS_BUTTON_ID).disabled = true; |
+ |
+ if (content.snapshots.length > 1) { |
+ setNodeDisplay($(SNAPSHOTS_ROW), true); |
+ } |
+ |
+ for (var i = 0; i < content.snapshots.length; ++i) { |
+ var snapshot = content.snapshots[i]; |
+ this.snapshots_.push({flatData: [], origData: [], |
+ time: snapshot.timestamp * 1000}); |
+ this.addSnapshotToList_(this.snapshots_.length - 1); |
+ var snapshotData = snapshot.data; |
+ for (var j = 0; j < snapshotData.length; ++j){ |
+ this.addDataToSnapshot(snapshotData[j]); |
+ } |
+ } |
+ this.redrawData_(); |
+ }, |
+ |
+ onLoadSnapshotsFileError_: function(file, filedata) { |
+ this.displayFileLoadError_('Error loading ' + file.name); |
+ }, |
+ |
+ displayFileLoadError_: function(message) { |
+ $(LOAD_ERROR_ID).textContent = message; |
+ $(LOAD_ERROR_ID).hidden = false; |
+ }, |
+ |
+ hideFileLoadError_: function() { |
+ $(LOAD_ERROR_ID).textContent = ''; |
+ $(LOAD_ERROR_ID).hidden = true; |
+ }, |
+ |
getSnapshotCheckbox_: function(i) { |
return $(this.getSnapshotCheckboxId_(i)); |
}, |