Diff
Modified: trunk/Source/WebCore/ChangeLog (97854 => 97855)
--- trunk/Source/WebCore/ChangeLog 2011-10-19 13:31:03 UTC (rev 97854)
+++ trunk/Source/WebCore/ChangeLog 2011-10-19 13:36:47 UTC (rev 97855)
@@ -1,3 +1,54 @@
+2011-10-19 Ilya Tikhonovsky <[email protected]>
+
+ Web Inspector: [Chromium] Add an ability to show the objects that were allocated between snapshot N-2 and snapshot N-1 and still alive in snapshot N.
+ https://bugs.webkit.org/show_bug.cgi?id=61178
+
+ This ability will help us to see only the leaked objects.
+ Scenario:
+ 1) do an action on inspected page which leaks _javascript_ data;
+ 2) make snapshot;
+ 3) repeat first two steps three or more times;
+ 4) select the last snapshot and select the filter 'Objects allocated between Snapshot 1 and Snapshot2' instead of 'All objects'.
+ The view shows the object that were allocated between snapshot N-2 and snapshot N-1 and still alive in snapshot N
+
+ Reviewed by Pavel Feldman.
+
+ * English.lproj/localizedStrings.js:
+ * inspector/front-end/DetailedHeapshotGridNodes.js:
+ (WebInspector.HeapSnapshotConstructorNode):
+ (WebInspector.HeapSnapshotConstructorNode.prototype._createNodesProvider):
+ * inspector/front-end/DetailedHeapshotView.js:
+ (WebInspector.HeapSnapshotConstructorsDataGrid):
+ (WebInspector.HeapSnapshotConstructorsDataGrid.prototype.setDataSource):
+ (WebInspector.HeapSnapshotConstructorsDataGrid.prototype.populateChildren):
+ (WebInspector.HeapSnapshotConstructorsDataGrid.prototype._filterSelectIndexChanged.firstSnapshotLoaded):
+ (WebInspector.HeapSnapshotConstructorsDataGrid.prototype._filterSelectIndexChanged.secondSnapshotLoaded):
+ (WebInspector.HeapSnapshotConstructorsDataGrid.prototype._filterSelectIndexChanged):
+ (WebInspector.HeapSnapshotDiffDataGrid.prototype._baseProfileIndexChanged):
+ (WebInspector.HeapSnapshotDiffDataGrid.prototype.populateChildren):
+ (WebInspector.DetailedHeapshotView.profileCallback):
+ (WebInspector.DetailedHeapshotView):
+ (WebInspector.DetailedHeapshotView.prototype.get statusBarItems):
+ (WebInspector.DetailedHeapshotView.prototype._changeBase):
+ (WebInspector.DetailedHeapshotView.prototype._changeFilter):
+ (WebInspector.DetailedHeapshotView.prototype._loadProfileByIndex):
+ (WebInspector.DetailedHeapshotView.prototype._changeView):
+ (WebInspector.DetailedHeapshotView.prototype._updateFilterOptions):
+ * inspector/front-end/HeapSnapshot.js:
+ (WebInspector.HeapSnapshot.prototype.dispose):
+ (WebInspector.HeapSnapshot.prototype.get maxNodeId):
+ (WebInspector.HeapSnapshot.prototype.aggregates):
+ (WebInspector.HeapSnapshot.prototype._buildAggregates):
+ (WebInspector.HeapSnapshot.prototype._sortAggregateIndexes):
+ (WebInspector.HeapSnapshot.prototype.createNodesProviderForClass):
+ (WebInspector.HeapSnapshot.prototype.updateStaticData):
+ * inspector/front-end/HeapSnapshotProxy.js:
+ (WebInspector.HeapSnapshotWorker):
+ (WebInspector.HeapSnapshotProxy.prototype.aggregates):
+ (WebInspector.HeapSnapshotProxy.prototype.createNodesProviderForClass):
+ (WebInspector.HeapSnapshotProxy.prototype.get maxNodeId):
+ (WebInspector.HeapSnapshotProxy.prototype.startLoading):
+
2011-10-19 Alexander Pavlov <[email protected]>
Strip trailing whitespace in the WebCore/css C++ code.
Modified: trunk/Source/WebCore/English.lproj/localizedStrings.js
(Binary files differ)
Modified: trunk/Source/WebCore/inspector/front-end/DetailedHeapshotGridNodes.js (97854 => 97855)
--- trunk/Source/WebCore/inspector/front-end/DetailedHeapshotGridNodes.js 2011-10-19 13:31:03 UTC (rev 97854)
+++ trunk/Source/WebCore/inspector/front-end/DetailedHeapshotGridNodes.js 2011-10-19 13:36:47 UTC (rev 97855)
@@ -493,14 +493,14 @@
WebInspector.HeapSnapshotInstanceNode.prototype.__proto__ = WebInspector.HeapSnapshotGenericObjectNode.prototype;
-WebInspector.HeapSnapshotConstructorNode = function(tree, className, aggregate)
+WebInspector.HeapSnapshotConstructorNode = function(tree, className, aggregate, aggregatesKey)
{
WebInspector.HeapSnapshotGridNode.call(this, tree, aggregate.count > 0);
this._name = className;
this._count = aggregate.count;
this._shallowSize = aggregate.self;
this._retainedSize = aggregate.maxRet;
- this._provider = this._createNodesProvider(tree.snapshot, className);
+ this._provider = this._createNodesProvider(tree.snapshot, className, aggregatesKey);
}
WebInspector.HeapSnapshotConstructorNode.prototype = {
@@ -509,9 +509,9 @@
return new WebInspector.HeapSnapshotInstanceNode(this.dataGrid, null, this.dataGrid.snapshot, item);
},
- _createNodesProvider: function(snapshot, className)
+ _createNodesProvider: function(snapshot, className, aggregatesKey)
{
- return snapshot.createNodesProviderForClass(className);
+ return snapshot.createNodesProviderForClass(className, aggregatesKey);
},
comparator: function()
Modified: trunk/Source/WebCore/inspector/front-end/DetailedHeapshotView.js (97854 => 97855)
--- trunk/Source/WebCore/inspector/front-end/DetailedHeapshotView.js 2011-10-19 13:31:03 UTC (rev 97854)
+++ trunk/Source/WebCore/inspector/front-end/DetailedHeapshotView.js 2011-10-19 13:36:47 UTC (rev 97855)
@@ -189,6 +189,7 @@
retainedSize: { title: WebInspector.UIString("Retained Size"), width: "90px", sort: "descending", sortable: true }
};
WebInspector.HeapSnapshotSortableDataGrid.call(this, columns);
+ this._filterProfileIndex = -1;
}
WebInspector.HeapSnapshotConstructorsDataGrid.prototype = {
@@ -208,19 +209,64 @@
{
this.snapshotView = snapshotView;
this.snapshot = snapshot;
- this.populateChildren();
+ if (this._filterProfileIndex === -1)
+ this.populateChildren();
},
populateChildren: function()
{
- function aggregatesReceived(aggregates)
+ function aggregatesReceived(key, aggregates)
{
for (var constructor in aggregates)
- this.appendChild(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor]));
+ this.appendChild(new WebInspector.HeapSnapshotConstructorNode(this, constructor, aggregates[constructor], key));
this.sortingChanged();
}
- this.snapshot.aggregates(false, aggregatesReceived.bind(this));
- }
+
+ if (this._filterProfileIndex === -1) {
+ this.snapshot.aggregates(false, "allObjects", null, aggregatesReceived.bind(this, "allObjects"));
+ return;
+ }
+
+ this.dispose();
+ this.removeChildren();
+ this.resetSortingCache();
+
+ var key = this._minNodeId + ".." + this._maxNodeId;
+ var filter = "function(node) { var id = node.id; return id > " + this._minNodeId + " && id <= " + this._maxNodeId + "; }";
+ this.snapshot.aggregates(false, key, filter, aggregatesReceived.bind(this, key));
+ },
+
+ _filterSelectIndexChanged: function(loader, profileIndex)
+ {
+ this._filterProfileIndex = profileIndex;
+
+ delete this._maxNodeId;
+ delete this._minNodeId;
+
+ if (this._filterProfileIndex === -1) {
+ this.populateChildren();
+ return;
+ }
+
+ function firstSnapshotLoaded(snapshot)
+ {
+ this._maxNodeId = snapshot.maxNodeId;
+ if (profileIndex > 0)
+ loader(profileIndex - 1, secondSnapshotLoaded.bind(this));
+ else {
+ this._minNodeId = 0;
+ this.populateChildren();
+ }
+ }
+
+ function secondSnapshotLoaded(snapshot)
+ {
+ this._minNodeId = snapshot.maxNodeId;
+ this.populateChildren();
+ }
+
+ loader(profileIndex, firstSnapshotLoaded.bind(this));
+ },
};
WebInspector.HeapSnapshotConstructorsDataGrid.prototype.__proto__ = WebInspector.HeapSnapshotSortableDataGrid.prototype;
@@ -262,6 +308,11 @@
this.snapshot = snapshot;
},
+ _baseProfileIndexChanged: function(loader, profileIndex)
+ {
+ loader(profileIndex, this.setBaseDataSource.bind(this));
+ },
+
setBaseDataSource: function(baseSnapshot)
{
this.baseSnapshot = baseSnapshot;
@@ -302,9 +353,9 @@
node.calculateDiff(this, addNodeIfNonZeroDiff.bind(this, node));
}
}
- this.snapshot.aggregates(true, aggregatesReceived.bind(this));
+ this.snapshot.aggregates(true, "allObjects", null, aggregatesReceived.bind(this));
}
- this.baseSnapshot.aggregates(true, baseAggregatesReceived.bind(this));
+ this.baseSnapshot.aggregates(true, "allObjects", null, baseAggregatesReceived.bind(this));
}
};
@@ -519,6 +570,7 @@
this.parent = parent;
this.parent.addEventListener("profile added", this._updateBaseOptions, this);
+ this.parent.addEventListener("profile added", this._updateFilterOptions, this);
this.showCountAsPercent = false;
this.showShallowSizeAsPercent = false;
@@ -616,6 +668,11 @@
this.baseSelectElement.addEventListener("change", this._changeBase.bind(this), false);
this._updateBaseOptions();
+ this.filterSelectElement = document.createElement("select");
+ this.filterSelectElement.className = "status-bar-item";
+ this.filterSelectElement.addEventListener("change", this._changeFilter.bind(this), false);
+ this._updateFilterOptions();
+
this.percentButton = new WebInspector.StatusBarButton("", "percent-time-status-bar-item status-bar-item");
this.percentButton.addEventListener("click", this._percentClicked.bind(this), false);
this.helpButton = new WebInspector.StatusBarButton("", "heapshot-help-status-bar-item status-bar-item");
@@ -629,11 +686,13 @@
{
var list = this._profiles();
var profileIndex;
- for (var i = 0; i < list.length; ++i)
+ for (var i = 0; i < list.length; ++i) {
if (list[i].uid === this._profileUid) {
profileIndex = i;
break;
}
+ }
+
if (profileIndex > 0)
this.baseSelectElement.selectedIndex = profileIndex - 1;
else
@@ -658,7 +717,7 @@
get statusBarItems()
{
- return [this.viewSelectElement, this.baseSelectElement, this.percentButton.element, this.helpButton.element];
+ return [this.viewSelectElement, this.baseSelectElement, this.filterSelectElement, this.percentButton.element, this.helpButton.element];
},
get profile()
@@ -865,15 +924,23 @@
return;
this._baseProfileUid = this._profiles()[this.baseSelectElement.selectedIndex].uid;
- this._loadProfile(this._baseProfileUid, baseProfileLoaded.bind(this));
+ this.dataGrid._baseProfileIndexChanged(this._loadProfileByIndex.bind(this), this.baseSelectElement.selectedIndex);
- function baseProfileLoaded()
- {
- delete this._baseProfileWrapper;
- this.baseProfile._lastShown = Date.now();
- this.diffDataGrid.setBaseDataSource(this.baseProfileWrapper);
- }
+ if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
+ return;
+ // The current search needs to be performed again. First negate out previous match
+ // count by calling the search finished callback with a negative number of matches.
+ // Then perform the search again with the same query and callback.
+ this._searchFinishedCallback(this, -this._searchResults.length);
+ this.performSearch(this.currentQuery, this._searchFinishedCallback);
+ },
+
+ _changeFilter: function()
+ {
+ var profileIndex = this.filterSelectElement.selectedIndex - 1;
+ this.dataGrid._filterSelectIndexChanged(this._loadProfileByIndex.bind(this), profileIndex);
+
if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
return;
@@ -894,6 +961,12 @@
WebInspector.panels.profiles.loadHeapSnapshot(profileUid, callback);
},
+ _loadProfileByIndex: function(profileIndex, callback)
+ {
+ var profileUid = this._profiles()[profileIndex].uid;
+ WebInspector.panels.profiles.loadHeapSnapshot(profileUid, callback);
+ },
+
isDetailedSnapshot: function(snapshot)
{
var s = new WebInspector.HeapSnapshot(snapshot);
@@ -1003,11 +1076,12 @@
this.currentView.show();
this.refreshVisibleData();
this.dataGrid.updateWidths();
+
if (this.currentView === this.diffView) {
this.baseSelectElement.removeStyleClass("hidden");
if (!this.dataGrid.snapshotView) {
+ this._changeBase();
this.dataGrid.setDataSource(this, this.profileWrapper);
- this._changeBase();
}
} else {
this.baseSelectElement.addStyleClass("hidden");
@@ -1015,6 +1089,11 @@
this.dataGrid.setDataSource(this, this.profileWrapper);
}
+ if (this.currentView === this.constructorsView)
+ this.filterSelectElement.removeStyleClass("hidden");
+ else
+ this.filterSelectElement.addStyleClass("hidden");
+
if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
return;
@@ -1184,6 +1263,33 @@
}
},
+ _updateFilterOptions: function()
+ {
+ var list = this._profiles();
+ // We're assuming that snapshots can only be added.
+ if (this.filterSelectElement.length - 1 === list.length)
+ return;
+
+ if (!this.filterSelectElement.length) {
+ var filterOption = document.createElement("option");
+ filterOption.label = WebInspector.UIString("All objects");
+ this.filterSelectElement.appendChild(filterOption);
+ }
+
+ for (var i = this.filterSelectElement.length - 1, n = list.length; i < n; ++i) {
+ var filterOption = document.createElement("option");
+ var title = list[i].title;
+ if (!title.indexOf(UserInitiatedProfileName)) {
+ if (!i)
+ title = WebInspector.UIString("Objects allocated before Snapshot %d", title.substring(UserInitiatedProfileName.length + 1));
+ else
+ title = WebInspector.UIString("Objects allocated between Snapshots %d and %d", title.substring(UserInitiatedProfileName.length + 1) - 1, title.substring(UserInitiatedProfileName.length + 1));
+ }
+ filterOption.label = title;
+ this.filterSelectElement.appendChild(filterOption);
+ }
+ },
+
_updatePercentButton: function()
{
if (this._isShowingAsPercent) {
Modified: trunk/Source/WebCore/inspector/front-end/HeapSnapshot.js (97854 => 97855)
--- trunk/Source/WebCore/inspector/front-end/HeapSnapshot.js 2011-10-19 13:31:03 UTC (rev 97854)
+++ trunk/Source/WebCore/inspector/front-end/HeapSnapshot.js 2011-10-19 13:36:47 UTC (rev 97855)
@@ -712,7 +712,7 @@
delete this._nodeIndex;
if (this._aggregates) {
delete this._aggregates;
- this._aggregatesIndexesSorted = false;
+ delete this._aggregatesSortedFlags;
}
delete this._baseNodeIds;
delete this._dominatedNodes;
@@ -752,6 +752,19 @@
return new WebInspector.HeapSnapshotNode(this, this._rootNodeIndex);
},
+ get maxNodeId()
+ {
+ if (typeof this._maxNodeId === "number")
+ return this._maxNodeId;
+ this._maxNodeId = 0;
+ for (var iter = this._allNodes; iter.hasNext(); iter.next()) {
+ var id = iter.node.id;
+ if ((id % 2) && id > this._maxNodeId)
+ this._maxNodeId = id;
+ }
+ return this._maxNodeId;
+ },
+
get rootNodeIndex()
{
return this._rootNodeIndex;
@@ -789,13 +802,35 @@
return this._flags[node.nodeIndex];
},
- aggregates: function(sortedIndexes)
+ aggregates: function(sortedIndexes, key, filterString)
{
- if (!this._aggregates)
- this._buildAggregates();
- if (sortedIndexes && !this._aggregatesIndexesSorted)
- this._sortAggregateIndexes();
- return this._aggregates;
+ if (!this._aggregates) {
+ this._aggregates = {};
+ this._aggregatesSortedFlags = {};
+ }
+
+ var aggregates = this._aggregates[key];
+ if (aggregates) {
+ if (sortedIndexes && !this._aggregatesSortedFlags[key]) {
+ this._sortAggregateIndexes(aggregates);
+ this._aggregatesSortedFlags[key] = sortedIndexes;
+ }
+ return aggregates;
+ }
+
+ var filter;
+ if (filterString)
+ filter = this._parseFilter(filterString);
+
+ aggregates = this._buildAggregates(filter);
+
+ if (sortedIndexes)
+ this._sortAggregateIndexes(aggregates);
+
+ this._aggregatesSortedFlags[key] = sortedIndexes;
+ this._aggregates[key] = aggregates;
+
+ return aggregates;
},
_buildReverseIndex: function(indexArrayName, backRefsArrayName, indexCallback, dataCallback)
@@ -853,18 +888,20 @@
}).bind(this));
},
- _buildAggregates: function()
+ _buildAggregates: function(filter)
{
- this._aggregates = {};
+ var aggregates = {};
for (var iter = this._allNodes; iter.hasNext(); iter.next()) {
var node = iter.node;
+ if (filter && !filter(node))
+ continue;
if (node.type !== "native" && node.selfSize === 0)
continue;
var nameMatters = node.type === "object" || node.type === "native";
var className = node.className;
- if (!this._aggregates.hasOwnProperty(className))
- this._aggregates[className] = { count: 0, self: 0, maxRet: 0, type: node.type, name: nameMatters ? node.name : null, idxs: [] };
- var clss = this._aggregates[className];
+ if (!aggregates.hasOwnProperty(className))
+ aggregates[className] = { count: 0, self: 0, maxRet: 0, type: node.type, name: nameMatters ? node.name : null, idxs: [] };
+ var clss = aggregates[className];
++clss.count;
clss.self += node.selfSize;
if (node.retainedSize > clss.maxRet)
@@ -872,23 +909,22 @@
clss.idxs.push(node.nodeIndex);
}
// Shave off provisionally allocated space.
- for (var className in this._aggregates)
- this._aggregates[className].idxs = this._aggregates[className].idxs.slice(0);
+ for (var className in aggregates)
+ aggregates[className].idxs = aggregates[className].idxs.slice(0);
+ return aggregates;
},
- _sortAggregateIndexes: function()
+ _sortAggregateIndexes: function(aggregates)
{
var nodeA = new WebInspector.HeapSnapshotNode(this);
var nodeB = new WebInspector.HeapSnapshotNode(this);
- for (var clss in this._aggregates)
- this._aggregates[clss].idxs.sort(
+ for (var clss in aggregates)
+ aggregates[clss].idxs.sort(
function(idxA, idxB) {
nodeA.nodeIndex = idxA;
nodeB.nodeIndex = idxB;
return nodeA.id < nodeB.id ? -1 : 1;
});
-
- this._aggregatesIndexesSorted = true;
},
_buildNodeIndex: function()
@@ -1048,9 +1084,9 @@
return new WebInspector.HeapSnapshotNodesProvider(this, this._parseFilter(filter));
},
- createNodesProviderForClass: function(className)
+ createNodesProviderForClass: function(className, aggregatesKey)
{
- return new WebInspector.HeapSnapshotNodesProvider(this, null, this.aggregates(false)[className].idxs);
+ return new WebInspector.HeapSnapshotNodesProvider(this, null, this.aggregates(aggregatesKey)[className].idxs);
},
createNodesProviderForDominator: function(nodeIndex, filter)
@@ -1066,7 +1102,7 @@
updateStaticData: function()
{
- return {nodeCount: this.nodeCount, rootNodeIndex: this._rootNodeIndex, totalSize: this.totalSize, uid: this.uid, nodeFlags: this._nodeFlags};
+ return {nodeCount: this.nodeCount, rootNodeIndex: this._rootNodeIndex, totalSize: this.totalSize, uid: this.uid, nodeFlags: this._nodeFlags, maxNodeId: this.maxNodeId};
}
};
Modified: trunk/Source/WebCore/inspector/front-end/HeapSnapshotProxy.js (97854 => 97855)
--- trunk/Source/WebCore/inspector/front-end/HeapSnapshotProxy.js 2011-10-19 13:31:03 UTC (rev 97854)
+++ trunk/Source/WebCore/inspector/front-end/HeapSnapshotProxy.js 2011-10-19 13:36:47 UTC (rev 97855)
@@ -310,9 +310,9 @@
}
WebInspector.HeapSnapshotProxy.prototype = {
- aggregates: function(sortedIndexes, callback)
+ aggregates: function(sortedIndexes, key, filter, callback)
{
- this.callMethod(callback, "aggregates", sortedIndexes);
+ this.callMethod(callback, "aggregates", sortedIndexes, key, filter);
},
createDiff: function(className)
@@ -330,9 +330,9 @@
return this.callFactoryMethod(null, "createNodesProvider", "WebInspector.HeapSnapshotProviderProxy", filter);
},
- createNodesProviderForClass: function(className)
+ createNodesProviderForClass: function(className, aggregatesKey)
{
- return this.callFactoryMethod(null, "createNodesProviderForClass", "WebInspector.HeapSnapshotProviderProxy", className);
+ return this.callFactoryMethod(null, "createNodesProviderForClass", "WebInspector.HeapSnapshotProviderProxy", className, aggregatesKey);
},
createNodesProviderForDominator: function(nodeIndex, filter)
@@ -360,6 +360,11 @@
return !!this._objectId;
},
+ get maxNodeId()
+ {
+ return this._staticData.maxNodeId;
+ },
+
get nodeCount()
{
return this._staticData.nodeCount;
@@ -397,7 +402,7 @@
startLoading: function(callback)
{
- setTimeout(callback, 0);
+ setTimeout(callback.bind(null, this), 0);
return false;
},