Diff
Modified: trunk/LayoutTests/ChangeLog (241218 => 241219)
--- trunk/LayoutTests/ChangeLog 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/LayoutTests/ChangeLog 2019-02-09 00:25:46 UTC (rev 241219)
@@ -1,3 +1,15 @@
+2019-02-08 Joseph Pecoraro <[email protected]>
+
+ 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-08 Nikita Vasilyev <[email protected]>
Web Inspector: Styles: close unbalanced quotes and parenthesis when editing values
Added: trunk/LayoutTests/inspector/heap/imported-snapshot-expected.txt (0 => 241219)
--- trunk/LayoutTests/inspector/heap/imported-snapshot-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/heap/imported-snapshot-expected.txt 2019-02-09 00:25:46 UTC (rev 241219)
@@ -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.
+
Added: trunk/LayoutTests/inspector/heap/imported-snapshot.html (0 => 241219)
--- trunk/LayoutTests/inspector/heap/imported-snapshot.html (rev 0)
+++ trunk/LayoutTests/inspector/heap/imported-snapshot.html 2019-02-09 00:25:46 UTC (rev 241219)
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<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>
Modified: trunk/LayoutTests/platform/mac/TestExpectations (241218 => 241219)
--- trunk/LayoutTests/platform/mac/TestExpectations 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/LayoutTests/platform/mac/TestExpectations 2019-02-09 00:25:46 UTC (rev 241219)
@@ -1086,6 +1086,7 @@
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 ]
Modified: trunk/Source/WebInspectorUI/ChangeLog (241218 => 241219)
--- trunk/Source/WebInspectorUI/ChangeLog 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/ChangeLog 2019-02-09 00:25:46 UTC (rev 241219)
@@ -1,5 +1,90 @@
2019-02-08 Joseph Pecoraro <[email protected]>
+ 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 <[email protected]>
+
Web Inspector: Add Debug setting to show Internal Object Classes in Heap Snapshot
https://bugs.webkit.org/show_bug.cgi?id=194445
Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -418,6 +418,7 @@
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["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["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";
Modified: trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/HeapAllocationsInstrument.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -79,6 +79,7 @@
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);
});
});
Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/ConsoleObserver.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Protocol/ConsoleObserver.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/ConsoleObserver.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -53,6 +53,7 @@
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);
});
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Protocol/HeapObserver.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -37,6 +37,7 @@
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 @@
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);
});
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -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 @@
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 @@
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 @@
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);
Modified: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -66,6 +66,12 @@
this.performAction("createSnapshotDiff", ...arguments);
}
+ createImportedSnapshot(snapshotStringData, title, callback)
+ {
+ const imported = true;
+ this.performAction("createSnapshot", snapshotStringData, title, imported, callback);
+ }
+
// Public
performAction(actionName)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineOverviewGraph.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -73,6 +73,9 @@
}
for (let record of visibleRecords) {
+ if (isNaN(record.timestamp))
+ continue;
+
const halfImageWidth = 8;
let x = xScale(record.timestamp) - halfImageWidth;
if (x <= 1)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -59,6 +59,11 @@
},
};
+ 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 @@
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 @@
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,6 +403,20 @@
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) {
@@ -388,6 +423,7 @@
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);
});
});
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -33,6 +33,12 @@
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 @@
// 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 @@
// 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 = "" + encodeURI(filename) + ".json";
+ WI.FileUtilities.save({
+ url,
+ content: this.representedObject.snapshotStringData,
+ forceSaveAs: true,
+ });
+ }
+
_sortDataGrid()
{
if (!this._heapSnapshotDataGridTree)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RecordingContentView.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -147,8 +147,6 @@
return {customSaveHandler: () => { this._exportRecording(); }};
}
- // Protected
-
initialLayout()
{
let previewHeader = this.element.appendChild(document.createElement("header"));
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineTabContentView.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -277,6 +277,8 @@
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);
Modified: trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -36,6 +36,7 @@
const nodeSizeOffset = 1;
const nodeClassNameOffset = 2;
const nodeInternalOffset = 3;
+const gcDebuggingNodeFieldCount = 7;
// edges
// [<0:fromId>, <1:toId>, <2:typeTableIndex>, <3:edgeDataIndexOrEdgeNameIndex>]
@@ -73,11 +74,12 @@
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;
@@ -84,10 +86,12 @@
let {version, type, nodes, nodeClassNames, edges, edgeTypes, edgeNames} = json;
console.assert(version === 1, "Expect _javascript_Core 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
_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 @@
{
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 @@
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 @@
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;
Modified: trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js (241218 => 241219)
--- trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js 2019-02-09 00:24:24 UTC (rev 241218)
+++ trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js 2019-02-09 00:25:46 UTC (rev 241219)
@@ -47,20 +47,23 @@
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()};