Title: [201183] trunk
Revision
201183
Author
[email protected]
Date
2016-05-19 14:30:55 -0700 (Thu, 19 May 2016)

Log Message

Web Inspector: HeapSnapshot Instances view should remove dead objects
https://bugs.webkit.org/show_bug.cgi?id=157920
<rdar://problem/26375866>

Patch by Joseph Pecoraro <[email protected]> on 2016-05-19
Reviewed by Timothy Hatcher.

Source/WebInspectorUI:

* 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).

LayoutTests:

* inspector/unit-tests/heap-snapshot-collection-event-expected.txt: Added.
* inspector/unit-tests/heap-snapshot-collection-event.html: Added.

Modified Paths

Added Paths

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()};
     }
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to