WebKit Bugzilla
Attachment 361540 Details for
Bug 194448
: Web Inspector: Import / Export Heap Snapshots
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
[PATCH] For Landing
for-landing-0.patch (text/plain), 39.78 KB, created by
Joseph Pecoraro
on 2019-02-08 15:22:06 PST
(
hide
)
Description:
[PATCH] For Landing
Filename:
MIME Type:
Creator:
Joseph Pecoraro
Created:
2019-02-08 15:22:06 PST
Size:
39.78 KB
patch
obsolete
>diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 3eade05ab1c..a4ea879246c 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,15 @@ >+2019-02-08 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Import / Export Heap Snapshots >+ https://bugs.webkit.org/show_bug.cgi?id=194448 >+ <rdar://problem/47928093> >+ >+ Reviewed by Devin Rousso. >+ >+ * inspector/heap/imported-snapshot-expected.txt: Added. >+ * inspector/heap/imported-snapshot.html: Added. >+ * platform/mac/TestExpectations: >+ > 2019-02-06 Devin Rousso <drousso@apple.com> > > Web Inspector: DOM: don't send the entire function string with each event listener >diff --git a/LayoutTests/inspector/heap/imported-snapshot-expected.txt b/LayoutTests/inspector/heap/imported-snapshot-expected.txt >new file mode 100644 >index 00000000000..a797d7e40b3 >--- /dev/null >+++ b/LayoutTests/inspector/heap/imported-snapshot-expected.txt >@@ -0,0 +1,11 @@ >+Test for an imported HeapSnapshot. >+ >+ >+== Running test suite: HeapSnapshot.imported >+-- Running test case: HeapSnapshot.imported >+PASS: Should not have an error creating a snapshot. >+PASS: Normal snapshot is not imported. >+PASS: Normal snapshot title should not be set. >+PASS: Imported snapshot is imported. >+PASS: Imported snapshot title should be set. >+ >diff --git a/LayoutTests/inspector/heap/imported-snapshot.html b/LayoutTests/inspector/heap/imported-snapshot.html >new file mode 100644 >index 00000000000..9e50cb2a72b >--- /dev/null >+++ b/LayoutTests/inspector/heap/imported-snapshot.html >@@ -0,0 +1,44 @@ >+<!DOCTYPE html> >+<html> >+<head> >+<script src="../../http/tests/inspector/resources/inspector-test.js"></script> >+<script> >+function test() >+{ >+ let suite = InspectorTest.createAsyncSuite("HeapSnapshot.imported"); >+ >+ suite.addTestCase({ >+ name: "HeapSnapshot.imported", >+ description: "createSnapshot() and createImportedSnapshot() differences.", >+ test(resolve, reject) { >+ HeapAgent.snapshot((error, timestamp, snapshotStringData) => { >+ InspectorTest.expectThat(!error, "Should not have an error creating a snapshot."); >+ >+ const importedTitle = "Imported Snapshot"; >+ let workerProxy = WI.HeapSnapshotWorkerProxy.singleton(); >+ workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => { >+ let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot); >+ snapshot.snapshotStringData = snapshotStringData; >+ workerProxy.createImportedSnapshot(snapshotStringData, importedTitle, ({objectId, snapshot: serializedSnapshot}) => { >+ let importedSnapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot); >+ importedSnapshot.snapshotStringData = snapshotStringData; >+ >+ InspectorTest.expectFalse(snapshot.imported, "Normal snapshot is not imported."); >+ InspectorTest.expectNull(snapshot.title, "Normal snapshot title should not be set."); >+ InspectorTest.expectTrue(importedSnapshot.imported, "Imported snapshot is imported."); >+ InspectorTest.expectEqual(importedSnapshot.title, importedTitle, "Imported snapshot title should be set."); >+ resolve(); >+ }); >+ }); >+ }); >+ } >+ }); >+ >+ suite.runTestCasesAndFinish(); >+} >+</script> >+</head> >+<body onload="runTest()"> >+<p>Test for an imported HeapSnapshot.</p> >+</body> >+</html> >diff --git a/LayoutTests/platform/mac/TestExpectations b/LayoutTests/platform/mac/TestExpectations >index cad5d121b07..c3a9484f49f 100644 >--- a/LayoutTests/platform/mac/TestExpectations >+++ b/LayoutTests/platform/mac/TestExpectations >@@ -1086,6 +1086,7 @@ webkit.org/b/169228 inspector/worker/console-basic.html [ Pass Timeout ] > webkit.org/b/164872 inspector/worker/debugger-multiple-targets-pause.html [ Pass Failure Timeout ] > webkit.org/b/165582 inspector/worker/debugger-scripts.html [ Pass Failure ] > webkit.org/b/167203 inspector/worker/debugger-shared-breakpoint.html [ Pass Failure Timeout ] >+webkit.org/b/155607 inspector/heap/imported-snapshot.html [ Pass Timeout ] > webkit.org/b/155607 inspector/heap/snapshot.html [ Pass Timeout ] > webkit.org/b/143719 inspector/console/console-api.html [ Pass Timeout ] > webkit.org/b/156078 inspector/console/heapSnapshot.html [ Pass Timeout ] >diff --git a/Source/WebInspectorUI/ChangeLog b/Source/WebInspectorUI/ChangeLog >index 09d1db43699..339393a0a86 100644 >--- a/Source/WebInspectorUI/ChangeLog >+++ b/Source/WebInspectorUI/ChangeLog >@@ -1,3 +1,88 @@ >+2019-02-08 Joseph Pecoraro <pecoraro@apple.com> >+ >+ Web Inspector: Import / Export Heap Snapshots >+ https://bugs.webkit.org/show_bug.cgi?id=194448 >+ <rdar://problem/47928093> >+ >+ Reviewed by Devin Rousso. >+ >+ * Localizations/en.lproj/localizedStrings.js: >+ New strings. >+ >+ * UserInterface/Proxies/HeapSnapshotProxy.js: >+ (WI.HeapSnapshotProxy): >+ (WI.HeapSnapshotProxy.deserialize): >+ (WI.HeapSnapshotProxy.prototype.get imported): >+ (WI.HeapSnapshotProxy.prototype.get snapshotStringData): >+ (WI.HeapSnapshotProxy.prototype.set snapshotStringData): >+ Include an "imported" state on the HeapSnapshot and allow for >+ stashing the snapshotStringData on the main thread side. >+ >+ * UserInterface/Proxies/HeapSnapshotWorkerProxy.js: >+ (WI.HeapSnapshotWorkerProxy.prototype.createImportedSnapshot): >+ * UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js: >+ (HeapSnapshotWorker.prototype.clearSnapshots): >+ (HeapSnapshotWorker.prototype.createSnapshot): >+ Provide a specialized way to create an imported HeapSnapshot. >+ Track imported snapshots separately since they won't want to >+ be searched for live/dead objects due to active recording GCs. >+ >+ * UserInterface/Workers/HeapSnapshot/HeapSnapshot.js: >+ (HeapSnapshot): >+ (HeapSnapshot.updateCategoriesAndMetadata): >+ (HeapSnapshot.allocationBucketCounts): >+ (HeapSnapshot.instancesWithClassName): >+ (HeapSnapshot.prototype.nodeWithIdentifier): >+ (HeapSnapshot.prototype.dominatedNodes): >+ (HeapSnapshot.prototype.retainedNodes): >+ (HeapSnapshot.prototype.retainers): >+ (HeapSnapshot.prototype.updateDeadNodesAndGatherCollectionData): >+ (HeapSnapshot.prototype.serialize): >+ (HeapSnapshot.prototype.serializeNode): >+ (HeapSnapshot.prototype._buildPostOrderIndexes): >+ (HeapSnapshot.prototype._buildDominatorIndexes): >+ (HeapSnapshot.prototype._buildRetainedSizes): >+ (HeapSnapshot.prototype._gcRootPathes.visitNode): >+ (HeapSnapshot.prototype._gcRootPathes): >+ Construct a HeapSnapshot knowinng whether or not it is imported. >+ Imported snapshots may be the "GCDebugging" snapshot type which >+ differs from "Inspector" by the number of node fields. So keep >+ the node field count a member instead of a global constant >+ in order to work with both snapshot types. >+ >+ * UserInterface/Models/HeapAllocationsInstrument.js: >+ (WI.HeapAllocationsInstrument.prototype._takeHeapSnapshot): >+ * UserInterface/Protocol/ConsoleObserver.js: >+ (WI.ConsoleObserver.prototype.heapSnapshot): >+ * UserInterface/Protocol/HeapObserver.js: >+ (WI.HeapObserver.prototype.trackingStart): >+ (WI.HeapObserver.prototype.trackingComplete): >+ Stash the original string JSON data on the main thread side >+ where we already have the data. >+ >+ * UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js: >+ (WI.HeapAllocationsTimelineOverviewGraph.prototype.layout): >+ Don't show [S] icons for imported snapshots with no timestamp. >+ >+ * UserInterface/Views/HeapAllocationsTimelineView.js: >+ (WI.HeapAllocationsTimelineView): >+ (WI.HeapAllocationsTimelineView.prototype.get navigationItems): >+ (WI.HeapAllocationsTimelineView.prototype._importButtonNavigationItemClicked): >+ (WI.HeapAllocationsTimelineView.prototype._takeHeapSnapshotClicked): >+ Import button that just creates a new snapshot. >+ >+ * UserInterface/Views/HeapSnapshotContentView.js: >+ (WI.HeapSnapshotContentView): >+ (WI.HeapSnapshotContentView.prototype.get navigationItems): >+ (WI.HeapSnapshotContentView.prototype.get supportsSave): >+ (WI.HeapSnapshotContentView.prototype.get saveData): >+ (WI.HeapSnapshotContentView.prototype._exportSnapshot): >+ Export button that saves the original data. >+ >+ * UserInterface/Views/TimelineTabContentView.js: >+ (WI.TimelineTabContentView.displayNameForRecord): >+ Specialized display string for imported snapshots. >+ > 2019-02-08 Joseph Pecoraro <pecoraro@apple.com> > > Web Inspector: Add Debug setting to show Internal Object Classes in Heap Snapshot >diff --git a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >index 68c2f0b7207..96ad46e9a70 100644 >--- a/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >+++ b/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js >@@ -418,6 +418,7 @@ localizedStrings["Expand columns"] = "Expand columns"; > localizedStrings["Expanded"] = "Expanded"; > localizedStrings["Experimental"] = "Experimental"; > localizedStrings["Export"] = "Export"; >+localizedStrings["Export (%s)"] = "Export (%s)"; > localizedStrings["Export HAR"] = "Export HAR"; > localizedStrings["Export Result"] = "Export Result"; > localizedStrings["Export Test"] = "Export Test"; >@@ -488,6 +489,7 @@ localizedStrings["HTML Attributes"] = "HTML Attributes"; > localizedStrings["Headers"] = "Headers"; > localizedStrings["Headers:"] = "Headers:"; > localizedStrings["Heading Level"] = "Heading Level"; >+localizedStrings["Heap Snapshot %s-%s-%s at %s.%s.%s"] = "Heap Snapshot %s-%s-%s at %s.%s.%s"; > localizedStrings["Heap Snapshot Object (%s)"] = "Heap Snapshot Object (%s)"; > localizedStrings["Height"] = "Height"; > localizedStrings["Hide Console"] = "Hide Console"; >@@ -519,6 +521,7 @@ localizedStrings["Immediate Pause Requested"] = "Immediate Pause Requested"; > localizedStrings["Import"] = "Import"; > localizedStrings["Imported"] = "Imported"; > localizedStrings["Imported Recordings"] = "Imported Recordings"; >+localizedStrings["Imported \u2014 %s"] = "Imported \u2014 %s"; > localizedStrings["Incomplete"] = "Incomplete"; > localizedStrings["Indent width:"] = "Indent width:"; > localizedStrings["Index"] = "Index"; >diff --git a/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js b/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js >index 03c33b9b2e0..090906578bd 100644 >--- a/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js >+++ b/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js >@@ -79,6 +79,7 @@ WI.HeapAllocationsInstrument = class HeapAllocationsInstrument extends WI.Instru > let workerProxy = WI.HeapSnapshotWorkerProxy.singleton(); > workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => { > let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot); >+ snapshot.snapshotStringData = snapshotStringData; > WI.timelineManager.heapSnapshotAdded(timestamp, snapshot); > }); > }); >diff --git a/Source/WebInspectorUI/UserInterface/Protocol/ConsoleObserver.js b/Source/WebInspectorUI/UserInterface/Protocol/ConsoleObserver.js >index 047ee50acb0..136c2147314 100644 >--- a/Source/WebInspectorUI/UserInterface/Protocol/ConsoleObserver.js >+++ b/Source/WebInspectorUI/UserInterface/Protocol/ConsoleObserver.js >@@ -53,6 +53,7 @@ WI.ConsoleObserver = class ConsoleObserver > let workerProxy = WI.HeapSnapshotWorkerProxy.singleton(); > workerProxy.createSnapshot(snapshotStringData, title || null, ({objectId, snapshot: serializedSnapshot}) => { > let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot); >+ snapshot.snapshotStringData = snapshotStringData; > WI.timelineManager.heapSnapshotAdded(timestamp, snapshot); > }); > } >diff --git a/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js b/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js >index 828bf53d2f8..1b9d4291d0a 100644 >--- a/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js >+++ b/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js >@@ -37,6 +37,7 @@ WI.HeapObserver = class HeapObserver > let workerProxy = WI.HeapSnapshotWorkerProxy.singleton(); > workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => { > let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot); >+ snapshot.snapshotStringData = snapshotStringData; > WI.timelineManager.heapTrackingStarted(timestamp, snapshot); > }); > } >@@ -46,6 +47,7 @@ WI.HeapObserver = class HeapObserver > let workerProxy = WI.HeapSnapshotWorkerProxy.singleton(); > workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => { > let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot); >+ snapshot.snapshotStringData = snapshotStringData; > WI.timelineManager.heapTrackingCompleted(timestamp, snapshot); > }); > } >diff --git a/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js b/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js >index dce225e6c52..60f45c93480 100644 >--- a/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js >+++ b/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js >@@ -25,7 +25,7 @@ > > WI.HeapSnapshotProxy = class HeapSnapshotProxy extends WI.Object > { >- constructor(snapshotObjectId, identifier, title, totalSize, totalObjectCount, liveSize, categories) >+ constructor(snapshotObjectId, identifier, title, totalSize, totalObjectCount, liveSize, categories, imported) > { > super(); > >@@ -37,6 +37,8 @@ WI.HeapSnapshotProxy = class HeapSnapshotProxy extends WI.Object > this._totalObjectCount = totalObjectCount; > this._liveSize = liveSize; > this._categories = Map.fromObject(categories); >+ this._imported = imported; >+ this._snapshotStringData = null; > > console.assert(!this.invalid); > >@@ -49,8 +51,8 @@ WI.HeapSnapshotProxy = class HeapSnapshotProxy extends WI.Object > > static deserialize(objectId, serializedSnapshot) > { >- let {identifier, title, totalSize, totalObjectCount, liveSize, categories} = serializedSnapshot; >- return new WI.HeapSnapshotProxy(objectId, identifier, title, totalSize, totalObjectCount, liveSize, categories); >+ let {identifier, title, totalSize, totalObjectCount, liveSize, categories, imported} = serializedSnapshot; >+ return new WI.HeapSnapshotProxy(objectId, identifier, title, totalSize, totalObjectCount, liveSize, categories, imported); > } > > static invalidateSnapshotProxies() >@@ -73,8 +75,19 @@ WI.HeapSnapshotProxy = class HeapSnapshotProxy extends WI.Object > get totalObjectCount() { return this._totalObjectCount; } > get liveSize() { return this._liveSize; } > get categories() { return this._categories; } >+ get imported() { return this._imported; } > get invalid() { return this._proxyObjectId === 0; } > >+ get snapshotStringData() >+ { >+ return this._snapshotStringData; >+ } >+ >+ set snapshotStringData(data) >+ { >+ this._snapshotStringData = data; >+ } >+ > updateForCollectionEvent(event) > { > console.assert(!this.invalid); >diff --git a/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js b/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js >index 4c51b4a374a..d4c0a882d56 100644 >--- a/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js >+++ b/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js >@@ -66,6 +66,12 @@ WI.HeapSnapshotWorkerProxy = class HeapSnapshotWorkerProxy extends WI.Object > this.performAction("createSnapshotDiff", ...arguments); > } > >+ createImportedSnapshot(snapshotStringData, title, callback) >+ { >+ const imported = true; >+ this.performAction("createSnapshot", snapshotStringData, title, imported, callback); >+ } >+ > // Public > > performAction(actionName) >diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js >index eea29c8337f..6d3e2fc6535 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js >+++ b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js >@@ -73,6 +73,9 @@ WI.HeapAllocationsTimelineOverviewGraph = class HeapAllocationsTimelineOverviewG > } > > for (let record of visibleRecords) { >+ if (isNaN(record.timestamp)) >+ continue; >+ > const halfImageWidth = 8; > let x = xScale(record.timestamp) - halfImageWidth; > if (x <= 1) >diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js >index 73a604c7fbb..cde7b4efec6 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js >@@ -59,6 +59,11 @@ WI.HeapAllocationsTimelineView = class HeapAllocationsTimelineView extends WI.Ti > }, > }; > >+ this._importButtonNavigationItem = new WI.ButtonNavigationItem("import", WI.UIString("Import"), "Images/Import.svg", 15, 15); >+ this._importButtonNavigationItem.toolTip = WI.UIString("Import"); >+ this._importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; >+ this._importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._importButtonNavigationItemClicked, this); >+ > let snapshotTooltip = WI.UIString("Take snapshot"); > this._takeHeapSnapshotButtonItem = new WI.ButtonNavigationItem("take-snapshot", snapshotTooltip, "Images/Camera.svg", 16, 16); > this._takeHeapSnapshotButtonItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._takeHeapSnapshotClicked, this); >@@ -184,7 +189,7 @@ WI.HeapAllocationsTimelineView = class HeapAllocationsTimelineView extends WI.Ti > get navigationItems() > { > if (this._showingSnapshotList) { >- let items = [this._takeHeapSnapshotButtonItem, this._compareHeapSnapshotsButtonItem]; >+ let items = [this._importButtonNavigationItem, this._takeHeapSnapshotButtonItem, this._compareHeapSnapshotsButtonItem]; > if (this._selectingComparisonHeapSnapshots) > items.push(this._compareHeapSnapshotHelpTextItem); > return items; >@@ -214,6 +219,22 @@ WI.HeapAllocationsTimelineView = class HeapAllocationsTimelineView extends WI.Ti > return components.concat(this._contentViewContainer.currentContentView.selectionPathComponents); > } > >+ get supportsSave() >+ { >+ if (this._showingSnapshotList) >+ return false; >+ >+ if (!this._contentViewContainer.currentContentView) >+ return false; >+ >+ return this._contentViewContainer.currentContentView.supportsSave; >+ } >+ >+ get saveData() >+ { >+ return this._contentViewContainer.currentContentView.saveData; >+ } >+ > selectRecord(record) > { > if (record) >@@ -382,12 +403,27 @@ WI.HeapAllocationsTimelineView = class HeapAllocationsTimelineView extends WI.Ti > this._compareHeapSnapshotsButtonItem.enabled = hasAtLeastTwoValidSnapshots; > } > >+ _importButtonNavigationItemClicked() >+ { >+ WI.FileUtilities.importText(function(result) { >+ let snapshotStringData = result.text; >+ let workerProxy = WI.HeapSnapshotWorkerProxy.singleton(); >+ workerProxy.createImportedSnapshot(snapshotStringData, result.filename, ({objectId, snapshot: serializedSnapshot}) => { >+ let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot); >+ snapshot.snapshotStringData = snapshotStringData; >+ const timestamp = NaN; >+ WI.timelineManager.heapSnapshotAdded(timestamp, snapshot); >+ }); >+ }); >+ } >+ > _takeHeapSnapshotClicked() > { > HeapAgent.snapshot(function(error, timestamp, snapshotStringData) { > let workerProxy = WI.HeapSnapshotWorkerProxy.singleton(); > workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => { > let snapshot = WI.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot); >+ snapshot.snapshotStringData = snapshotStringData; > WI.timelineManager.heapSnapshotAdded(timestamp, snapshot); > }); > }); >diff --git a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js >index 62cddbf11c7..e5b5dc59a54 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js >@@ -33,6 +33,12 @@ WI.HeapSnapshotContentView = class HeapSnapshotContentView extends WI.ContentVie > > this.element.classList.add("heap-snapshot"); > >+ this._exportButtonNavigationItem = new WI.ButtonNavigationItem("export", WI.UIString("Export"), "Images/Export.svg", 15, 15); >+ this._exportButtonNavigationItem.toolTip = WI.UIString("Export (%s)").format(WI.saveKeyboardShortcut.displayName); >+ this._exportButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; >+ this._exportButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.High; >+ this._exportButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, () => { this._exportSnapshot(); }); >+ > this._dataGrid = new WI.DataGrid(columns); > this._dataGrid.sortColumnIdentifier = "retainedSize"; > this._dataGrid.sortOrder = WI.DataGrid.SortOrder.Descending; >@@ -52,6 +58,23 @@ WI.HeapSnapshotContentView = class HeapSnapshotContentView extends WI.ContentVie > > // Protected > >+ get navigationItems() >+ { >+ if (this.representedObject instanceof WI.HeapSnapshotProxy) >+ return [this._exportButtonNavigationItem]; >+ return []; >+ } >+ >+ get supportsSave() >+ { >+ return this.representedObject instanceof WI.HeapSnapshotProxy; >+ } >+ >+ get saveData() >+ { >+ return {customSaveHandler: () => { this._exportSnapshot(); }}; >+ } >+ > shown() > { > super.shown(); >@@ -73,6 +96,31 @@ WI.HeapSnapshotContentView = class HeapSnapshotContentView extends WI.ContentVie > > // Private > >+ _exportSnapshot() >+ { >+ if (!this.representedObject.snapshotStringData) { >+ InspectorFrontendHost.beep(); >+ return; >+ } >+ >+ let date = new Date; >+ let values = [ >+ date.getFullYear(), >+ Number.zeroPad(date.getMonth() + 1, 2), >+ Number.zeroPad(date.getDate(), 2), >+ Number.zeroPad(date.getHours(), 2), >+ Number.zeroPad(date.getMinutes(), 2), >+ Number.zeroPad(date.getSeconds(), 2), >+ ]; >+ let filename = WI.UIString("Heap Snapshot %s-%s-%s at %s.%s.%s").format(...values); >+ let url = "web-inspector:///" + encodeURI(filename) + ".json"; >+ WI.FileUtilities.save({ >+ url, >+ content: this.representedObject.snapshotStringData, >+ forceSaveAs: true, >+ }); >+ } >+ > _sortDataGrid() > { > if (!this._heapSnapshotDataGridTree) >diff --git a/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js b/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js >index 596242d4766..e8f7db1397e 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js >@@ -147,8 +147,6 @@ WI.RecordingContentView = class RecordingContentView extends WI.ContentView > return {customSaveHandler: () => { this._exportRecording(); }}; > } > >- // Protected >- > initialLayout() > { > let previewHeader = this.element.appendChild(document.createElement("header")); >diff --git a/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js b/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js >index 1e91a77ffaa..e9d70cecdce 100644 >--- a/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js >+++ b/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js >@@ -277,6 +277,8 @@ WI.TimelineTabContentView = class TimelineTabContentView extends WI.ContentBrows > case WI.TimelineRecord.Type.RenderingFrame: > return WI.UIString("Frame %d").format(timelineRecord.frameNumber); > case WI.TimelineRecord.Type.HeapAllocations: >+ if (timelineRecord.heapSnapshot.imported) >+ return WI.UIString("Imported \u2014 %s").format(timelineRecord.heapSnapshot.title); > if (timelineRecord.heapSnapshot.title) > return WI.UIString("Snapshot %d \u2014 %s").format(timelineRecord.heapSnapshot.identifier, timelineRecord.heapSnapshot.title); > return WI.UIString("Snapshot %d").format(timelineRecord.heapSnapshot.identifier); >diff --git a/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js b/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js >index df454156994..af6fdff9a6e 100644 >--- a/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js >+++ b/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js >@@ -36,6 +36,7 @@ const nodeIdOffset = 0; > const nodeSizeOffset = 1; > const nodeClassNameOffset = 2; > const nodeInternalOffset = 3; >+const gcDebuggingNodeFieldCount = 7; > > // edges > // [<0:fromId>, <1:toId>, <2:typeTableIndex>, <3:edgeDataIndexOrEdgeNameIndex>] >@@ -73,21 +74,24 @@ let nextSnapshotIdentifier = 1; > > HeapSnapshot = class HeapSnapshot > { >- constructor(objectId, snapshotDataString, title = null) >+ constructor(objectId, snapshotDataString, title = null, imported = false) > { > this._identifier = nextSnapshotIdentifier++; > this._objectId = objectId; > this._title = title; >+ this._imported = imported; > > let json = JSON.parse(snapshotDataString); > snapshotDataString = null; > > let {version, type, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = json; > console.assert(version === 1, "Expect JavaScriptCore Heap Snapshot version 1"); >- console.assert(!type || type === "Inspector", "Expect an Inspector Heap Snapshot"); >+ console.assert(!type || (type === "Inspector" || type === "GCDebugging"), "Expect an Inspector / GCDebugging Heap Snapshot"); >+ >+ this._nodeFieldCount = type === "GCDebugging" ? gcDebuggingNodeFieldCount : nodeFieldCount; > > this._nodes = nodes; >- this._nodeCount = nodes.length / nodeFieldCount; >+ this._nodeCount = nodes.length / this._nodeFieldCount; > > this._edges = edges; > this._edgeCount = edges.length / edgeFieldCount; >@@ -99,8 +103,8 @@ HeapSnapshot = class HeapSnapshot > this._totalSize = 0; > this._nodeIdentifierToOrdinal = new Map; // <node identifier> => nodeOrdinal > this._lastNodeIdentifier = 0; >- for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex += nodeFieldCount) { >- let nodeOrdinal = nodeIndex / nodeFieldCount; >+ for (let nodeIndex = 0; nodeIndex < nodes.length; nodeIndex += this._nodeFieldCount) { >+ let nodeOrdinal = nodeIndex / this._nodeFieldCount; > let nodeIdentifier = nodes[nodeIndex + nodeIdOffset]; > this._nodeIdentifierToOrdinal.set(nodeIdentifier, nodeOrdinal); > this._totalSize += nodes[nodeIndex + nodeSizeOffset]; >@@ -151,9 +155,9 @@ HeapSnapshot = class HeapSnapshot > let nodeOrdinalIsDead = snapshot._nodeOrdinalIsDead; > > // Skip the <root> node. >- let firstNodeIndex = nodeFieldCount; >+ let firstNodeIndex = snapshot._nodeFieldCount; > let firstNodeOrdinal = 1; >- for (let nodeIndex = firstNodeIndex, nodeOrdinal = firstNodeOrdinal; nodeIndex < nodes.length; nodeIndex += nodeFieldCount, nodeOrdinal++) { >+ for (let nodeIndex = firstNodeIndex, nodeOrdinal = firstNodeOrdinal; nodeIndex < nodes.length; nodeIndex += snapshot._nodeFieldCount, nodeOrdinal++) { > if (allowNodeIdentifierCallback && !allowNodeIdentifierCallback(nodes[nodeIndex + nodeIdOffset])) > continue; > >@@ -191,10 +195,10 @@ HeapSnapshot = class HeapSnapshot > let nodes = snapshot._nodes; > > // Skip the <root> node. >- let firstNodeIndex = nodeFieldCount; >+ let firstNodeIndex = snapshot._nodeFieldCount; > > outer: >- for (let nodeIndex = firstNodeIndex; nodeIndex < nodes.length; nodeIndex += nodeFieldCount) { >+ for (let nodeIndex = firstNodeIndex; nodeIndex < nodes.length; nodeIndex += snapshot._nodeFieldCount) { > if (allowNodeIdentifierCallback && !allowNodeIdentifierCallback(nodes[nodeIndex + nodeIdOffset])) > continue; > >@@ -219,9 +223,9 @@ HeapSnapshot = class HeapSnapshot > let nodeClassNamesTable = snapshot._nodeClassNamesTable; > > // Skip the <root> node. >- let firstNodeIndex = nodeFieldCount; >+ let firstNodeIndex = snapshot._nodeFieldCount; > let firstNodeOrdinal = 1; >- for (let nodeIndex = firstNodeIndex, nodeOrdinal = firstNodeOrdinal; nodeIndex < nodes.length; nodeIndex += nodeFieldCount, nodeOrdinal++) { >+ for (let nodeIndex = firstNodeIndex, nodeOrdinal = firstNodeOrdinal; nodeIndex < nodes.length; nodeIndex += snapshot._nodeFieldCount, nodeOrdinal++) { > if (allowNodeIdentifierCallback && !allowNodeIdentifierCallback(nodes[nodeIndex + nodeIdOffset])) > continue; > >@@ -253,7 +257,7 @@ HeapSnapshot = class HeapSnapshot > nodeWithIdentifier(nodeIdentifier) > { > let nodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier); >- let nodeIndex = nodeOrdinal * nodeFieldCount; >+ let nodeIndex = nodeOrdinal * this._nodeFieldCount; > return this.serializeNode(nodeIndex); > } > >@@ -297,7 +301,7 @@ HeapSnapshot = class HeapSnapshot > let targetNodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier); > for (let nodeOrdinal = 0; nodeOrdinal < this._nodeCount; ++nodeOrdinal) { > if (this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal] === targetNodeOrdinal) >- dominatedNodes.push(nodeOrdinal * nodeFieldCount); >+ dominatedNodes.push(nodeOrdinal * this._nodeFieldCount); > } > > return dominatedNodes.map(this.serializeNode, this); >@@ -313,7 +317,7 @@ HeapSnapshot = class HeapSnapshot > for (; this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier; edgeIndex += edgeFieldCount) { > let toNodeIdentifier = this._edges[edgeIndex + edgeToIdOffset]; > let toNodeOrdinal = this._nodeIdentifierToOrdinal.get(toNodeIdentifier); >- let toNodeIndex = toNodeOrdinal * nodeFieldCount; >+ let toNodeIndex = toNodeOrdinal * this._nodeFieldCount; > retainedNodes.push(toNodeIndex); > edges.push(edgeIndex); > } >@@ -334,7 +338,7 @@ HeapSnapshot = class HeapSnapshot > let incomingEdgeIndexEnd = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal + 1]; > for (let edgeIndex = incomingEdgeIndex; edgeIndex < incomingEdgeIndexEnd; ++edgeIndex) { > let fromNodeOrdinal = this._incomingNodes[edgeIndex]; >- let fromNodeIndex = fromNodeOrdinal * nodeFieldCount; >+ let fromNodeIndex = fromNodeOrdinal * this._nodeFieldCount; > retainers.push(fromNodeIndex); > edges.push(this._incomingEdges[edgeIndex]); > } >@@ -347,6 +351,9 @@ HeapSnapshot = class HeapSnapshot > > updateDeadNodesAndGatherCollectionData(snapshots) > { >+ console.assert(!this._imported, "Should never use an imported snapshot to modify snapshots"); >+ console.assert(snapshots.every((x) => !x._imported), "Should never modify nodes of imported snapshots"); >+ > let previousSnapshotIndex = snapshots.indexOf(this) - 1; > let previousSnapshot = snapshots[previousSnapshotIndex]; > if (!previousSnapshot) >@@ -356,7 +363,7 @@ HeapSnapshot = class HeapSnapshot > > // All of the node identifiers that could have existed prior to this snapshot. > let known = new Map; >- for (let nodeIndex = 0; nodeIndex < this._nodes.length; nodeIndex += nodeFieldCount) { >+ for (let nodeIndex = 0; nodeIndex < this._nodes.length; nodeIndex += this._nodeFieldCount) { > let nodeIdentifier = this._nodes[nodeIndex + nodeIdOffset]; > if (nodeIdentifier > lastNodeIdentifier) > continue; >@@ -365,7 +372,7 @@ HeapSnapshot = class HeapSnapshot > > // Determine which node identifiers have since been deleted. > let collectedNodesList = []; >- for (let nodeIndex = 0; nodeIndex < previousSnapshot._nodes.length; nodeIndex += nodeFieldCount) { >+ for (let nodeIndex = 0; nodeIndex < previousSnapshot._nodes.length; nodeIndex += this._nodeFieldCount) { > let nodeIdentifier = previousSnapshot._nodes[nodeIndex + nodeIdOffset]; > let wasDeleted = !known.has(nodeIdentifier); > if (wasDeleted) >@@ -403,20 +410,21 @@ HeapSnapshot = class HeapSnapshot > totalObjectCount: this._nodeCount - 1, // <root>. > liveSize: this._liveSize, > categories: this._categories, >+ imported: this._imported, > }; > } > > serializeNode(nodeIndex) > { >- console.assert((nodeIndex % nodeFieldCount) === 0, "Invalid nodeIndex to serialize"); >+ console.assert((nodeIndex % this._nodeFieldCount) === 0, "Invalid nodeIndex to serialize"); > > let nodeIdentifier = this._nodes[nodeIndex + nodeIdOffset]; >- let nodeOrdinal = nodeIndex / nodeFieldCount; >+ let nodeOrdinal = nodeIndex / this._nodeFieldCount; > let edgeIndex = this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal]; > let hasChildren = this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier; > > let dominatorNodeOrdinal = this._nodeOrdinalToDominatorNodeOrdinal[nodeOrdinal]; >- let dominatorNodeIndex = dominatorNodeOrdinal * nodeFieldCount; >+ let dominatorNodeIndex = dominatorNodeOrdinal * this._nodeFieldCount; > let dominatorNodeIdentifier = this._nodes[dominatorNodeIndex + nodeIdOffset]; > > return { >@@ -533,7 +541,7 @@ HeapSnapshot = class HeapSnapshot > > while (stackTop >= 0) { > let nodeOrdinal = stackNodes[stackTop]; >- let nodeIdentifier = this._nodes[(nodeOrdinal * nodeFieldCount) + nodeIdOffset]; >+ let nodeIdentifier = this._nodes[(nodeOrdinal * this._nodeFieldCount) + nodeIdOffset]; > let edgeIndex = stackEdges[stackTop]; > > if (this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier) { >@@ -657,7 +665,7 @@ HeapSnapshot = class HeapSnapshot > changed = true; > > let outgoingEdgeIndex = this._nodeOrdinalToFirstOutgoingEdge[nodeOrdinal]; >- let nodeIdentifier = this._nodes[(nodeOrdinal * nodeFieldCount) + nodeIdOffset]; >+ let nodeIdentifier = this._nodes[(nodeOrdinal * this._nodeFieldCount) + nodeIdOffset]; > for (let edgeIndex = outgoingEdgeIndex; this._edges[edgeIndex + edgeFromIdOffset] === nodeIdentifier; edgeIndex += edgeFieldCount) { > let toNodeIdentifier = this._edges[edgeIndex + edgeToIdOffset]; > let toNodeOrdinal = this._nodeIdentifierToOrdinal.get(toNodeIdentifier); >@@ -678,7 +686,7 @@ HeapSnapshot = class HeapSnapshot > _buildRetainedSizes(postOrderIndexToNodeOrdinal) > { > // Self size. >- for (let nodeIndex = 0, nodeOrdinal = 0; nodeOrdinal < this._nodeCount; nodeIndex += nodeFieldCount, nodeOrdinal++) >+ for (let nodeIndex = 0, nodeOrdinal = 0; nodeOrdinal < this._nodeCount; nodeIndex += this._nodeFieldCount, nodeOrdinal++) > this._nodeOrdinalToRetainedSizes[nodeOrdinal] = this._nodes[nodeIndex + nodeSizeOffset]; > > // Attribute size to dominator. >@@ -731,7 +739,7 @@ HeapSnapshot = class HeapSnapshot > { > if (this._nodeOrdinalIsGCRoot[nodeOrdinal]) { > let fullPath = currentPath.slice(); >- let nodeIndex = nodeOrdinal * nodeFieldCount; >+ let nodeIndex = nodeOrdinal * this._nodeFieldCount; > fullPath.push({node: nodeIndex}); > paths.push(fullPath); > return; >@@ -741,7 +749,7 @@ HeapSnapshot = class HeapSnapshot > return; > visited[nodeOrdinal] = 1; > >- let nodeIndex = nodeOrdinal * nodeFieldCount; >+ let nodeIndex = nodeOrdinal * this._nodeFieldCount; > currentPath.push({node: nodeIndex}); > > // Loop in reverse order because edges were added in reverse order. >@@ -750,7 +758,7 @@ HeapSnapshot = class HeapSnapshot > let incomingEdgeIndexEnd = this._nodeOrdinalToFirstIncomingEdge[nodeOrdinal + 1]; > for (let incomingEdgeIndex = incomingEdgeIndexEnd - 1; incomingEdgeIndex >= incomingEdgeIndexStart; --incomingEdgeIndex) { > let fromNodeOrdinal = this._incomingNodes[incomingEdgeIndex]; >- let fromNodeIndex = fromNodeOrdinal * nodeFieldCount; >+ let fromNodeIndex = fromNodeOrdinal * this._nodeFieldCount; > let fromNodeIsInternal = this._nodes[fromNodeIndex + nodeInternalOffset]; > if (fromNodeIsInternal) > continue; >diff --git a/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js b/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js >index 13f68498312..f268aafdb5c 100644 >--- a/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js >+++ b/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js >@@ -47,20 +47,23 @@ HeapSnapshotWorker = class HeapSnapshotWorker > this._snapshots = []; > } > >- createSnapshot(snapshotString, title) >+ createSnapshot(snapshotString, title, imported) > { > let objectId = this._nextObjectId++; >- let snapshot = new HeapSnapshot(objectId, snapshotString, title); >- this._snapshots.push(snapshot); >+ let snapshot = new HeapSnapshot(objectId, snapshotString, title, imported); > this._objects.set(objectId, snapshot); > >- if (this._snapshots.length > 1) { >- setTimeout(() => { >- let collectionData = snapshot.updateDeadNodesAndGatherCollectionData(this._snapshots); >- if (!collectionData || !collectionData.affectedSnapshots.length) >- return; >- this.sendEvent("HeapSnapshot.CollectionEvent", collectionData); >- }, 0); >+ if (!imported) { >+ this._snapshots.push(snapshot); >+ >+ if (this._snapshots.length > 1) { >+ setTimeout(() => { >+ let collectionData = snapshot.updateDeadNodesAndGatherCollectionData(this._snapshots); >+ if (!collectionData || !collectionData.affectedSnapshots.length) >+ return; >+ this.sendEvent("HeapSnapshot.CollectionEvent", collectionData); >+ }, 0); >+ } > } > > return {objectId, snapshot: snapshot.serialize()};
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 194448
:
361530
|
361531
|
361533
| 361540