Diff
Modified: trunk/LayoutTests/ChangeLog (201182 => 201183)
--- trunk/LayoutTests/ChangeLog 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/LayoutTests/ChangeLog 2016-05-19 21:30:55 UTC (rev 201183)
@@ -1,3 +1,14 @@
+2016-05-19 Joseph Pecoraro <[email protected]>
+
+ Web Inspector: HeapSnapshot Instances view should remove dead objects
+ https://bugs.webkit.org/show_bug.cgi?id=157920
+ <rdar://problem/26375866>
+
+ Reviewed by Timothy Hatcher.
+
+ * inspector/unit-tests/heap-snapshot-collection-event-expected.txt: Added.
+ * inspector/unit-tests/heap-snapshot-collection-event.html: Added.
+
2016-05-18 Filip Pizlo <[email protected]>
DFG::LICMPhase shouldn't hoist type checks unless it knows that the check will succeed at the loop pre-header
Added: trunk/LayoutTests/inspector/unit-tests/heap-snapshot-collection-event-expected.txt (0 => 201183)
--- trunk/LayoutTests/inspector/unit-tests/heap-snapshot-collection-event-expected.txt (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/heap-snapshot-collection-event-expected.txt 2016-05-19 21:30:55 UTC (rev 201183)
@@ -0,0 +1,20 @@
+Testing HeapSnapshot CollectionEvent between snapshots.
+
+
+== Running test suite: HeapSnapshot.CollectionEvent
+-- Running test case: HeapSnapshot
+PASS: Should not have an error creating a snapshot.
+PASS: Should create HeapSnapshotProxy snapshot.
+
+-- Running test case: HeapSnapshotCollectionEvent
+PASS: Should not have an error creating a snapshot.
+PASS: Received HeapSnapshot.CollectionEvent.
+PASS: Collection should include at least 200 nodes (100 objects and 100 strings).
+PASS: Collection should affect the first snapshot.
+
+-- Running test case: HeapSnapshot.prototype.updateCategories
+PASS: 0 Objects were dead before.
+PASS: At least 100 Objects are dead after.
+PASS: 0 strings were dead before.
+PASS: At least 100 strings are dead after.
+
Added: trunk/LayoutTests/inspector/unit-tests/heap-snapshot-collection-event.html (0 => 201183)
--- trunk/LayoutTests/inspector/unit-tests/heap-snapshot-collection-event.html (rev 0)
+++ trunk/LayoutTests/inspector/unit-tests/heap-snapshot-collection-event.html 2016-05-19 21:30:55 UTC (rev 201183)
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+<script>
+function triggerCreateObjects() {
+ window.list = [];
+ for (let i = 0; i < 100; ++i)
+ window.list.push({key: "value" + i});
+}
+
+function triggerRemoveObjects() {
+ window.list = null;
+}
+
+function test()
+{
+ let suite = InspectorTest.createAsyncSuite("HeapSnapshot.CollectionEvent");
+
+ let snapshot = null;
+
+ suite.addTestCase({
+ name: "HeapSnapshot",
+ test: (resolve, reject) => {
+ InspectorTest.evaluateInPage("triggerCreateObjects()");
+ HeapAgent.snapshot((error, timestamp, snapshotStringData) => {
+ InspectorTest.expectThat(!error, "Should not have an error creating a snapshot.");
+ let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+ workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => {
+ snapshot = WebInspector.HeapSnapshotProxy.deserialize(objectId, serializedSnapshot);
+ InspectorTest.expectThat(snapshot, "Should create HeapSnapshotProxy snapshot.");
+ resolve();
+ });
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "HeapSnapshotCollectionEvent",
+ test: (resolve, reject) => {
+ InspectorTest.evaluateInPage("triggerRemoveObjects()");
+
+ let workerProxy = WebInspector.HeapSnapshotWorkerProxy.singleton();
+ HeapAgent.snapshot((error, timestamp, snapshotStringData) => {
+ InspectorTest.expectThat(!error, "Should not have an error creating a snapshot.");
+ workerProxy.createSnapshot(snapshotStringData, ({objectId, snapshot: serializedSnapshot}) => {
+ // Ignore result. This should trigger the collection event.
+ });
+ });
+
+ workerProxy.addEventListener("HeapSnapshot.CollectionEvent", (event) => {
+ InspectorTest.pass("Received HeapSnapshot.CollectionEvent.");
+ InspectorTest.expectThat(Object.keys(event.data.collectedNodes).length >= 200, "Collection should include at least 200 nodes (100 objects and 100 strings).");
+ InspectorTest.expectThat(event.data.affectedSnapshots.includes(snapshot.identifier), "Collection should affect the first snapshot.");
+ resolve();
+ });
+ }
+ });
+
+ suite.addTestCase({
+ name: "HeapSnapshot.prototype.updateCategories",
+ test: (resolve, reject) => {
+ let categoriesBefore = snapshot.categories;
+ snapshot.updateCategories(() => {
+ let categoriesAfter = snapshot.categories;
+ InspectorTest.expectThat(categoriesBefore.get("Object").deadCount === 0, "0 Objects were dead before.");
+ InspectorTest.expectThat(categoriesAfter.get("Object").deadCount >= 100, "At least 100 Objects are dead after.");
+ InspectorTest.expectThat(categoriesBefore.get("string").deadCount === 0, "0 strings were dead before.");
+ InspectorTest.expectThat(categoriesAfter.get("string").deadCount >= 100, "At least 100 strings are dead after.");
+ resolve();
+ });
+ }
+ });
+
+ suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body _onload_="runTest()">
+ <p>Testing HeapSnapshot CollectionEvent between snapshots.</p>
+</body>
+</html>
Modified: trunk/Source/WebInspectorUI/ChangeLog (201182 => 201183)
--- trunk/Source/WebInspectorUI/ChangeLog 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/ChangeLog 2016-05-19 21:30:55 UTC (rev 201183)
@@ -1,3 +1,91 @@
+2016-05-19 Joseph Pecoraro <[email protected]>
+
+ Web Inspector: HeapSnapshot Instances view should remove dead objects
+ https://bugs.webkit.org/show_bug.cgi?id=157920
+ <rdar://problem/26375866>
+
+ Reviewed by Timothy Hatcher.
+
+ * UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js:
+ (HeapSnapshotWorker):
+ (HeapSnapshotWorker.prototype.clearSnapshots):
+ (HeapSnapshotWorker.prototype.createSnapshot):
+ Preserve a list of snapshots for this page's session. When
+ new snapshots come in we can determine which nodes have died
+ and update previous snapshots. Emit a CollectionEvent
+ containing the nodes that were deleted and affected snapshots.
+
+ * UserInterface/Workers/HeapSnapshot/HeapSnapshot.js:
+ (HeapSnapshot):
+ Stash the largest node identifier.
+ Create a byte-per-node list to mark it as dead or alive.
+ All snapshots start with all live nodes.
+
+ (HeapSnapshot.prototype.updateDeadNodesAndGatherCollectionData):
+ (HeapSnapshot.prototype._markDeadNodes):
+ When we get a new snapshot we can mark dead nodes in previous
+ snapshots. Determine the nodeIdentifiers of dead nodes, and
+ mark them as dead if they existed in previous snapshots.
+
+ (HeapSnapshot.buildCategories):
+ (HeapSnapshot.prototype.updateCategories):
+ (HeapSnapshotDiff.prototype.updateCategories):
+ Include a "deadCount" in category data. And provide a method
+ to return an updated category list.
+
+ (HeapSnapshot.prototype.serializeNode):
+ Include a "dead" property on nodes when they are first fetched.
+
+ * UserInterface/Proxies/HeapSnapshotDiffProxy.js:
+ (WebInspector.HeapSnapshotDiffProxy.prototype.updateForCollectionEvent):
+ (WebInspector.HeapSnapshotDiffProxy.prototype.updateCategories):
+ * UserInterface/Proxies/HeapSnapshotNodeProxy.js:
+ (WebInspector.HeapSnapshotNodeProxy):
+ (WebInspector.HeapSnapshotNodeProxy.deserialize):
+ * UserInterface/Proxies/HeapSnapshotProxy.js:
+ (WebInspector.HeapSnapshotProxy.prototype.updateForCollectionEvent):
+ (WebInspector.HeapSnapshotProxy.prototype.updateCategories):
+ Update snapshot proxies from a collection event by updating properties
+ and dispatching an event from the model object.
+
+ * UserInterface/Proxies/HeapSnapshotWorkerProxy.js:
+ (WebInspector.HeapSnapshotWorkerProxy):
+ (WebInspector.HeapSnapshotWorkerProxy.prototype.clearSnapshots):
+ (WebInspector.HeapSnapshotWorkerProxy.prototype._mainResourceDidChange):
+ Clear the session snapshot list when the main resource changes.
+ However we can't yet clear the HeapSnapshot objects on the worker
+ because we may still have UI that interact with them.
+
+ * UserInterface/Views/HeapAllocationsTimelineView.js:
+ (WebInspector.HeapAllocationsTimelineView):
+ (WebInspector.HeapAllocationsTimelineView.prototype.closed):
+ Register and unregister for HeapSnapshot collection events.
+
+ (WebInspector.HeapAllocationsTimelineView.prototype._heapSnapshotCollectionEvent.updateHeapSnapshotForEvent):
+ (WebInspector.HeapAllocationsTimelineView.prototype._heapSnapshotCollectionEvent):
+ Update all the snapshots we know about when a collection event happens.
+
+ * UserInterface/Views/HeapSnapshotClassDataGridNode.js:
+ (WebInspector.HeapSnapshotClassDataGridNode.prototype.removeCollectedNodes):
+ (WebInspector.HeapSnapshotClassDataGridNode.prototype.updateCount):
+ (WebInspector.HeapSnapshotClassDataGridNode.prototype._populate):
+ (WebInspector.HeapSnapshotClassDataGridNode.prototype._fetchBatch):
+ * UserInterface/Views/HeapSnapshotContentView.js:
+ (WebInspector.HeapSnapshotContentView.prototype._heapSnapshotDataGridTreeDidPopulate):
+ (WebInspector.HeapSnapshotContentView):
+ * UserInterface/Views/HeapSnapshotDataGridTree.js:
+ (WebInspector.HeapSnapshotDataGridTree):
+ (WebInspector.HeapSnapshotDataGridTree.prototype.removeChild):
+ (WebInspector.HeapSnapshotDataGridTree.prototype.removeCollectedNodes):
+ (WebInspector.HeapSnapshotDataGridTree.prototype._heapSnapshotCollectedNodes):
+ (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.populateTopLevel):
+ (WebInspector.HeapSnapshotInstancesDataGridTree.prototype.removeCollectedNodes):
+ (WebInspector.HeapSnapshotInstancesDataGridTree):
+ Update the Instances DataGridTree UI when a collection event happens.
+ Remove any top level InstanceDataGridNodes that may be showing for collected nodes.
+ Update the category counts on the ClassDataGridNodes.
+ Update FetchMoreDataGridNodes to have updated counts (and replace removed nodes).
+
2016-05-19 Timothy Hatcher <[email protected]>
Web Inspector: REGRESSION: Search magnifying glass shifted in Search tab input field
Modified: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotDiffProxy.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotDiffProxy.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotDiffProxy.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -62,6 +62,16 @@
get totalObjectCount() { return this._totalObjectCount; }
get categories() { return this._categories; }
+ updateForCollectionEvent(event)
+ {
+ if (!event.data.affectedSnapshots.includes(this._snapshot2._identifier))
+ return;
+
+ this.updateCategories(() => {
+ this.dispatchEventToListeners(WebInspector.HeapSnapshotProxy.Event.CollectedNodes, event.data);
+ });
+ }
+
allocationBucketCounts(bucketSizes, callback)
{
WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, "allocationBucketCounts", bucketSizes, callback);
@@ -74,6 +84,14 @@
});
}
+ updateCategories(callback)
+ {
+ WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, "updateCategories", (categories) => {
+ this._categories = Map.fromObject(categories);
+ callback();
+ });
+ }
+
nodeWithIdentifier(nodeIdentifier, callback)
{
WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, "nodeWithIdentifier", nodeIdentifier, (serializedNode) => {
Modified: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotNodeProxy.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -25,7 +25,7 @@
WebInspector.HeapSnapshotNodeProxy = class HeapSnapshotNodeProxy
{
- constructor(snapshotObjectId, identifier, className, size, retainedSize, internal, gcRoot, dominatorNodeIdentifier, hasChildren)
+ constructor(snapshotObjectId, identifier, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren)
{
this._proxyObjectId = snapshotObjectId;
@@ -35,6 +35,7 @@
this.retainedSize = retainedSize;
this.internal = internal;
this.gcRoot = gcRoot;
+ this.dead = dead;
this.dominatorNodeIdentifier = dominatorNodeIdentifier;
this.hasChildren = hasChildren;
}
@@ -43,8 +44,8 @@
static deserialize(objectId, serializedNode)
{
- let {id, className, size, retainedSize, internal, gcRoot, dominatorNodeIdentifier, hasChildren} = serializedNode;
- return new WebInspector.HeapSnapshotNodeProxy(objectId, id, className, size, retainedSize, internal, gcRoot, dominatorNodeIdentifier, hasChildren);
+ let {id, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren} = serializedNode;
+ return new WebInspector.HeapSnapshotNodeProxy(objectId, id, className, size, retainedSize, internal, gcRoot, dead, dominatorNodeIdentifier, hasChildren);
}
// Proxied
Modified: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotProxy.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -55,6 +55,16 @@
get totalObjectCount() { return this._totalObjectCount; }
get categories() { return this._categories; }
+ updateForCollectionEvent(event)
+ {
+ if (!event.data.affectedSnapshots.includes(this._identifier))
+ return;
+
+ this.updateCategories(() => {
+ this.dispatchEventToListeners(WebInspector.HeapSnapshotProxy.Event.CollectedNodes, event.data);
+ });
+ }
+
allocationBucketCounts(bucketSizes, callback)
{
WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, "allocationBucketCounts", bucketSizes, callback);
@@ -67,6 +77,14 @@
});
}
+ updateCategories(callback)
+ {
+ WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, "updateCategories", (categories) => {
+ this._categories = Map.fromObject(categories);
+ callback();
+ });
+ }
+
nodeWithIdentifier(nodeIdentifier, callback)
{
WebInspector.HeapSnapshotWorkerProxy.singleton().callMethod(this._proxyObjectId, "nodeWithIdentifier", nodeIdentifier, (serializedNode) => {
@@ -74,3 +92,7 @@
});
}
};
+
+WebInspector.HeapSnapshotProxy.Event = {
+ CollectedNodes: "heap-snapshot-proxy-did-collect-nodes"
+};
Modified: trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Proxies/HeapSnapshotWorkerProxy.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -34,6 +34,8 @@
this._nextCallId = 1;
this._callbacks = new Map;
+
+ WebInspector.Frame.addEventListener(WebInspector.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this);
}
// Static
@@ -47,6 +49,11 @@
// Actions
+ clearSnapshots(callback)
+ {
+ this.performAction("clearSnapshots", callback);
+ }
+
createSnapshot(snapshotStringData, callback)
{
this.performAction("createSnapshot", ...arguments);
@@ -88,6 +95,14 @@
// Private
+ _mainResourceDidChange(event)
+ {
+ if (!event.target.isMainFrame())
+ return;
+
+ this.clearSnapshots(function(){});
+ }
+
_postMessage()
{
this._heapSnapshotWorker.postMessage(...arguments);
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapAllocationsTimelineView.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -89,6 +89,8 @@
this._pendingRecords = [];
timeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._heapAllocationsTimelineRecordAdded, this);
+
+ WebInspector.HeapSnapshotWorkerProxy.singleton().addEventListener("HeapSnapshot.CollectionEvent", this._heapSnapshotCollectionEvent, this);
}
// Public
@@ -227,6 +229,8 @@
this._dataGrid.closed();
this._contentViewContainer.closeAllContentViews();
+
+ WebInspector.HeapSnapshotWorkerProxy.singleton().removeEventListener("HeapSnapshot.CollectionEvent", this._heapSnapshotCollectionEvent, this);
}
layout()
@@ -269,6 +273,22 @@
this.needsLayout();
}
+ _heapSnapshotCollectionEvent(event)
+ {
+ function updateHeapSnapshotForEvent(heapSnapshot) {
+ heapSnapshot.updateForCollectionEvent(event);
+ }
+
+ for (let node of this._dataGrid.children)
+ updateHeapSnapshotForEvent(node.record.heapSnapshot);
+ for (let record of this._pendingRecords)
+ updateHeapSnapshotForEvent(record.heapSnapshot);
+ if (this._heapSnapshotDiff)
+ updateHeapSnapshotForEvent(this._heapSnapshotDiff);
+
+ // FIXME: <https://webkit.org/b/157904> Web Inspector: Snapshot List should show the total size and the total live size
+ }
+
_snapshotListPathComponentClicked(event)
{
this.showHeapSnapshotList();
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotClassDataGridNode.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -92,6 +92,43 @@
}
}
+ removeCollectedNodes(collectedNodes)
+ {
+ let nodesToRemove = [];
+
+ this.forEachImmediateChild((dataGridNode) => {
+ if (dataGridNode instanceof WebInspector.HeapSnapshotInstanceDataGridNode) {
+ let heapSnapshotNode = dataGridNode.node;
+ if (heapSnapshotNode.id in collectedNodes)
+ nodesToRemove.push(dataGridNode);
+ }
+ });
+
+ if (nodesToRemove.length) {
+ for (let dataGridNode of nodesToRemove)
+ this.removeChild(dataGridNode);
+ }
+
+ if (this._instances) {
+ this._instances = this._instances.filter((instance) => !(instance.id in collectedNodes));
+ this._fetchBatch(nodesToRemove.length);
+ }
+ }
+
+ updateCount(count)
+ {
+ if (count === this._data.count)
+ return;
+
+ if (!count) {
+ this._tree.removeChild(this);
+ return;
+ }
+
+ this._data.count = count;
+ this.needsRefresh();
+ }
+
// Private
_populate()
@@ -99,7 +136,8 @@
this.removeEventListener("populate", this._populate, this);
this._tree.heapSnapshot.instancesWithClassName(this._data.className, (instances) => {
- this._instances = instances;
+ // FIXME: <https://webkit.org/b/157905> Web Inspector: Provide a way to toggle between showing only live objects and live+dead objects
+ this._instances = instances.filter((instance) => !instance.dead);
this._sortInstances();
// Batch.
@@ -128,9 +166,11 @@
}
let count = newCount - oldCount;
- for (let i = 0; i <= count; ++i) {
- let instance = this._instances[oldCount + i];
- this.appendChild(new WebInspector.HeapSnapshotInstanceDataGridNode(instance, this._tree));
+ if (count) {
+ for (let i = 0; i <= count; ++i) {
+ let instance = this._instances[oldCount + i];
+ this.appendChild(new WebInspector.HeapSnapshotInstanceDataGridNode(instance, this._tree));
+ }
}
if (this._batched)
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotContentView.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -86,6 +86,7 @@
_heapSnapshotDataGridTreeDidPopulate()
{
+ this._dataGrid.removeChildren();
for (let child of this._heapSnapshotDataGridTree.children)
this._dataGrid.appendChild(child);
}
Modified: trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotDataGridTree.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotDataGridTree.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/HeapSnapshotDataGridTree.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -32,6 +32,7 @@
console.assert(heapSnapshot instanceof WebInspector.HeapSnapshotProxy || heapSnapshot instanceof WebInspector.HeapSnapshotDiffProxy);
this._heapSnapshot = heapSnapshot;
+ this._heapSnapshot.addEventListener(WebInspector.HeapSnapshotProxy.Event.CollectedNodes, this._heapSnapshotCollectedNodes, this);
this._children = [];
this._sortComparator = sortComparator;
@@ -106,6 +107,11 @@
this._children.splice(index, 0, node);
}
+ removeChild(node)
+ {
+ this._children.remove(node, true);
+ }
+
removeChildren()
{
this._children = [];
@@ -160,12 +166,24 @@
// Implemented by subclasses.
}
+ removeCollectedNodes(collectedNodes)
+ {
+ // Implemented by subclasses.
+ }
+
didPopulate()
{
this.sort();
this.dispatchEventToListeners(WebInspector.HeapSnapshotDataGridTree.Event.DidPopulate);
}
+
+ // Private
+
+ _heapSnapshotCollectedNodes(event)
+ {
+ this.removeCollectedNodes(event.data.collectedNodes);
+ }
};
WebInspector.HeapSnapshotDataGridTree.Event = {
@@ -182,14 +200,33 @@
populateTopLevel()
{
// Populate the first level with the different non-internal classes.
- for (let [className, {size, retainedSize, count, internalCount}] of this.heapSnapshot.categories) {
+ for (let [className, {size, retainedSize, count, internalCount, deadCount}] of this.heapSnapshot.categories) {
if (count === internalCount)
continue;
- this.appendChild(new WebInspector.HeapSnapshotClassDataGridNode({className, size, retainedSize, count}, this));
+
+ // FIXME: <https://webkit.org/b/157905> Web Inspector: Provide a way to toggle between showing only live objects and live+dead objects
+ let liveCount = count - deadCount;
+ if (!liveCount)
+ continue;
+
+ this.appendChild(new WebInspector.HeapSnapshotClassDataGridNode({className, size, retainedSize, count: liveCount}, this));
}
this.didPopulate()
}
+
+ removeCollectedNodes(collectedNodes)
+ {
+ for (let classDataGridNode of this.children) {
+ let {count, deadCount} = this.heapSnapshot.categories.get(classDataGridNode.data.className);
+ let liveCount = count - deadCount;
+ classDataGridNode.updateCount(liveCount);
+ if (liveCount)
+ classDataGridNode.removeCollectedNodes(collectedNodes);
+ }
+
+ this.didPopulate();
+ }
};
WebInspector.HeapSnapshotObjectGraphDataGridTree = class HeapSnapshotInstancesDataGridTree extends WebInspector.HeapSnapshotDataGridTree
Modified: trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshot.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -52,7 +52,7 @@
// Terminology:
// - `nodeIndex` is an index into the `nodes` list.
-// - `nodeOrdinal` is the order of the node in the `nodes` list. (nodeIndex / nodeFieldCount)
+// - `nodeOrdinal` is the order of the node in the `nodes` list. (nodeIndex / nodeFieldCount).
// - `nodeIdentifier` is the node's id value. (nodes[nodeIndex + nodeIdOffset]).
// - `edgeIndex` is an index into the `edges` list.
//
@@ -61,8 +61,9 @@
// Iterate edges by walking `edges` (edgeFieldCount) and checking if fromIdentifier is current.
// - _nodeOrdinalToFirstIncomingEdge - `nodeOrdinal` to `incomingEdgeIndex` in `incomingEdges`.
// Iterate edges by walking `incomingEdges` until `nodeOrdinal+1`'s first incoming edge index.
-// - _nodeOrdinalToDominatorNodeOrdinal - `nodeOrdinal` to `nodeOrdinal` of dominator
-// - _nodeOrdinalToRetainedSizes - `nodeOrdinal` to retain size value
+// - _nodeOrdinalToDominatorNodeOrdinal - `nodeOrdinal` to `nodeOrdinal` of dominator.
+// - _nodeOrdinalToRetainedSizes - `nodeOrdinal` to retain size value.
+// - _nodeOrdinalIsDead - `nodeOrdinal` is dead or alive.
//
// Temporary Lists:
// - nodeOrdinalToPostOrderIndex - `nodeOrdinal` to a `postOrderIndex`.
@@ -96,10 +97,14 @@
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;
- this._nodeIdentifierToOrdinal.set(nodes[nodeIndex + nodeIdOffset], nodeOrdinal);
+ let nodeIdentifier = nodes[nodeIndex + nodeIdOffset];
+ this._nodeIdentifierToOrdinal.set(nodeIdentifier, nodeOrdinal);
this._totalSize += nodes[nodeIndex + nodeSizeOffset];
+ if (nodeIdentifier > this._lastNodeIdentifier)
+ this._lastNodeIdentifier = nodeIdentifier;
}
// FIXME: Replace toIdentifier and fromIdentifier in edges with nodeIndex to reduce hash lookups?
@@ -125,6 +130,8 @@
postOrderIndexToNodeOrdinal = null;
+ this._nodeOrdinalIsDead = new Uint8Array(this._nodeCount);
+
this._categories = HeapSnapshot.buildCategories(this);
}
@@ -137,6 +144,7 @@
let nodes = snapshot._nodes;
let nodeClassNamesTable = snapshot._nodeClassNamesTable;
let nodeOrdinalToRetainedSizes = snapshot._nodeOrdinalToRetainedSizes;
+ let nodeOrdinalIsDead = snapshot._nodeOrdinalIsDead;
// Skip the <root> node.
let firstNodeIndex = nodeFieldCount;
@@ -150,16 +158,19 @@
let size = nodes[nodeIndex + nodeSizeOffset];
let retainedSize = nodeOrdinalToRetainedSizes[nodeOrdinal];
let internal = nodes[nodeIndex + nodeInternalOffset] ? true : false;
+ let dead = nodeOrdinalIsDead[nodeOrdinal] ? true : false;
let category = categories[className];
if (!category)
- category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0};
+ category = categories[className] = {className, size: 0, retainedSize: 0, count: 0, internalCount: 0, deadCount: 0};
category.size += size;
category.retainedSize += retainedSize;
category.count += 1;
if (internal)
category.internalCount += 1;
+ if (dead)
+ category.deadCount += 1;
}
return categories;
@@ -228,6 +239,11 @@
return HeapSnapshot.instancesWithClassName(this, className);
}
+ updateCategories()
+ {
+ return HeapSnapshot.buildCategories(this);
+ }
+
nodeWithIdentifier(nodeIdentifier)
{
let nodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier);
@@ -323,10 +339,60 @@
};
}
+ updateDeadNodesAndGatherCollectionData(snapshots)
+ {
+ let previousSnapshotIndex = snapshots.indexOf(this) - 1;
+ let previousSnapshot = snapshots[previousSnapshotIndex];
+ console.assert(previousSnapshot instanceof HeapSnapshot);
+ if (!previousSnapshot)
+ return;
+
+ let lastNodeIdentifier = previousSnapshot._lastNodeIdentifier;
+
+ // 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) {
+ let nodeIdentifier = this._nodes[nodeIndex + nodeIdOffset];
+ if (nodeIdentifier > lastNodeIdentifier)
+ continue;
+ known.set(nodeIdentifier, nodeIndex);
+ }
+
+ // Determine which node identifiers have since been deleted.
+ let collectedNodesList = [];
+ for (let nodeIndex = 0; nodeIndex < previousSnapshot._nodes.length; nodeIndex += nodeFieldCount) {
+ let nodeIdentifier = previousSnapshot._nodes[nodeIndex + nodeIdOffset];
+ let wasDeleted = !known.has(nodeIdentifier);
+ if (wasDeleted)
+ collectedNodesList.push(nodeIdentifier);
+ }
+
+ // Update dead nodes in previous snapshots.
+ let affectedSnapshots = [];
+ for (let snapshot of snapshots) {
+ if (snapshot === this)
+ break;
+ if (snapshot._markDeadNodes(collectedNodesList))
+ affectedSnapshots.push(snapshot._identifier);
+ }
+
+ // Convert list to a map.
+ let collectedNodes = {};
+ for (let i = 0; i < collectedNodesList.length; ++i)
+ collectedNodes[collectedNodesList[i]] = true;
+
+ return {
+ collectedNodes,
+ affectedSnapshots,
+ };
+ }
+
// Public
serialize()
{
+ // FIXME: <https://webkit.org/b/157904> Web Inspector: Snapshot List should show the total size and the total live size
+
return {
identifier: this._identifier,
title: this._title,
@@ -356,6 +422,7 @@
retainedSize: this._nodeOrdinalToRetainedSizes[nodeOrdinal],
internal: this._nodes[nodeIndex + nodeInternalOffset] ? true : false,
gcRoot: this._nodeOrdinalIsGCRoot[nodeOrdinal] ? true : false,
+ dead: this._nodeOrdinalIsDead[nodeOrdinal] ? true : false,
dominatorNodeIdentifier,
hasChildren,
};
@@ -619,6 +686,22 @@
}
}
+ _markDeadNodes(collectedNodesList)
+ {
+ let affected = false;
+
+ for (let i = 0; i < collectedNodesList.length; ++i) {
+ let nodeIdentifier = collectedNodesList[i];
+ if (nodeIdentifier > this._lastNodeIdentifier)
+ continue;
+ let nodeOrdinal = this._nodeIdentifierToOrdinal.get(nodeIdentifier);
+ this._nodeOrdinalIsDead[nodeOrdinal] = 1;
+ affected = true;
+ }
+
+ return affected;
+ }
+
_isNodeGlobalObject(nodeIndex)
{
let className = this._nodeClassNamesTable[this._nodes[nodeIndex + nodeClassNameOffset]];
@@ -725,6 +808,11 @@
return HeapSnapshot.instancesWithClassName(this._snapshot2, className, (nodeIdentifier) => this._addedNodeIdentifiers.has(nodeIdentifier));
}
+ updateCategories()
+ {
+ return HeapSnapshot.buildCategories(this._snapshot2, (nodeIdentifier) => this._addedNodeIdentifiers.has(nodeIdentifier));
+ }
+
nodeWithIdentifier(nodeIdentifier) { return this._snapshot2.nodeWithIdentifier(nodeIdentifier); }
shortestGCRootPath(nodeIdentifier) { return this._snapshot2.shortestGCRootPath(nodeIdentifier); }
dominatedNodes(nodeIdentifier) { return this._snapshot2.dominatedNodes(nodeIdentifier); }
Modified: trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js (201182 => 201183)
--- trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js 2016-05-19 21:25:29 UTC (rev 201182)
+++ trunk/Source/WebInspectorUI/UserInterface/Workers/HeapSnapshot/HeapSnapshotWorker.js 2016-05-19 21:30:55 UTC (rev 201183)
@@ -33,17 +33,37 @@
{
this._nextObjectId = 1;
this._objects = new Map;
+ this._snapshots = [];
self.addEventListener("message", this._handleMessage.bind(this));
}
// Actions
+ clearSnapshots()
+ {
+ // FIXME: <https://webkit.org/b/157907> Web Inspector: Snapshots should be cleared at some point
+ // this._objects.clear();
+
+ this._snapshots = [];
+ }
+
createSnapshot(snapshotString, title)
{
let objectId = this._nextObjectId++;
let snapshot = new HeapSnapshot(objectId, snapshotString, title);
+ this._snapshots.push(snapshot);
this._objects.set(objectId, snapshot);
+
+ if (this._snapshots.length > 1) {
+ setTimeout(() => {
+ let collectionData = snapshot.updateDeadNodesAndGatherCollectionData(this._snapshots);
+ if (!collectionData.affectedSnapshots.length)
+ return;
+ this.sendEvent("HeapSnapshot.CollectionEvent", collectionData);
+ }, 0);
+ }
+
return {objectId, snapshot: snapshot.serialize()};
}