2 * Copyright (C) 2011 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 * @extends {WebInspector.VBox}
34 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
35 * @param {!WebInspector.HeapProfileHeader} profile
37 WebInspector.HeapSnapshotView = function(dataDisplayDelegate, profile)
39 WebInspector.VBox.call(this);
41 this.element.classList.add("heap-snapshot-view");
43 profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
44 profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
46 if (profile._profileType.id === WebInspector.TrackingHeapSnapshotProfileType.TypeId) {
47 this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
48 this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
51 this._splitView = new WebInspector.SplitView(false, true, "heapSnapshotSplitViewState", 200, 200);
52 this._splitView.show(this.element);
54 this._containmentView = new WebInspector.VBox();
55 this._containmentView.setMinimumSize(50, 25);
56 this._containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(dataDisplayDelegate);
57 this._containmentDataGrid.show(this._containmentView.element);
58 this._containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
60 this._statisticsView = new WebInspector.HeapSnapshotStatisticsView();
62 this._constructorsView = new WebInspector.VBox();
63 this._constructorsView.setMinimumSize(50, 25);
65 this._constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(dataDisplayDelegate);
66 this._constructorsDataGrid.show(this._constructorsView.element);
67 this._constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
69 this._diffView = new WebInspector.VBox();
70 this._diffView.setMinimumSize(50, 25);
72 this._diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(dataDisplayDelegate);
73 this._diffDataGrid.show(this._diffView.element);
74 this._diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
76 this._dominatorView = new WebInspector.VBox();
77 this._dominatorView.setMinimumSize(50, 25);
78 this._dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid(dataDisplayDelegate);
79 this._dominatorDataGrid.show(this._dominatorView.element);
80 this._dominatorDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
82 if (WebInspector.experimentsSettings.allocationProfiler.isEnabled() && profile.profileType() === WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType) {
83 this._allocationView = new WebInspector.VBox();
84 this._allocationView.setMinimumSize(50, 25);
85 this._allocationDataGrid = new WebInspector.AllocationDataGrid(dataDisplayDelegate);
86 this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
87 this._allocationDataGrid.show(this._allocationView.element);
89 this._allocationStackView = new WebInspector.HeapAllocationStackView();
90 this._allocationStackView.setMinimumSize(50, 25);
92 this._tabbedPane = new WebInspector.TabbedPane();
93 this._tabbedPane.closeableTabs = false;
94 this._tabbedPane.headerElement().classList.add("heap-object-details-header");
97 this._retainmentView = new WebInspector.VBox();
98 this._retainmentView.setMinimumSize(50, 21);
99 this._retainmentView.element.classList.add("retaining-paths-view");
101 var splitViewResizer;
102 if (this._allocationStackView) {
103 this._tabbedPane = new WebInspector.TabbedPane();
104 this._tabbedPane.closeableTabs = false;
105 this._tabbedPane.headerElement().classList.add("heap-object-details-header");
107 this._tabbedPane.appendTab("retainers", WebInspector.UIString("Retainers"), this._retainmentView);
108 this._tabbedPane.appendTab("allocation-stack", WebInspector.UIString("Allocation stack"), this._allocationStackView);
110 splitViewResizer = this._tabbedPane.headerElement();
111 this._objectDetailsView = this._tabbedPane;
113 var retainmentViewHeader = document.createElementWithClass("div", "heap-snapshot-view-resizer");
114 var retainingPathsTitleDiv = retainmentViewHeader.createChild("div", "title");
115 var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
116 retainingPathsTitle.textContent = WebInspector.UIString("Retainers");
117 this._retainmentView.element.appendChild(retainmentViewHeader);
119 splitViewResizer = retainmentViewHeader;
120 this._objectDetailsView = this._retainmentView;
122 this._splitView.hideDefaultResizer();
123 this._splitView.installResizer(splitViewResizer);
125 this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid(dataDisplayDelegate);
126 this._retainmentDataGrid.show(this._retainmentView.element);
127 this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
128 this._retainmentDataGrid.reset();
130 this._perspectives = [];
131 this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective());
132 if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
133 this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective());
134 this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective());
135 if (WebInspector.settings.showAdvancedHeapSnapshotProperties.get())
136 this._perspectives.push(new WebInspector.HeapSnapshotView.DominatorPerspective());
137 if (this._allocationView)
138 this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective());
139 if (WebInspector.experimentsSettings.heapSnapshotStatistics.isEnabled())
140 this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective());
142 this._perspectiveSelect = new WebInspector.StatusBarComboBox(this._onSelectedPerspectiveChanged.bind(this));
143 for (var i = 0; i < this._perspectives.length; ++i)
144 this._perspectiveSelect.createOption(this._perspectives[i].title());
146 this._profile = profile;
148 this._baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
149 this._baseSelect.visible = false;
150 this._updateBaseOptions();
152 this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
153 this._filterSelect.visible = false;
154 this._updateFilterOptions();
156 this._classNameFilter = new WebInspector.StatusBarInput("Class filter");
157 this._classNameFilter.visible = false;
158 this._constructorsDataGrid.setNameFilter(this._classNameFilter);
159 this._diffDataGrid.setNameFilter(this._classNameFilter);
161 this._selectedSizeText = new WebInspector.StatusBarText("");
163 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
165 this._currentPerspectiveIndex = 0;
166 this._currentPerspective = this._perspectives[0];
167 this._currentPerspective.activate(this);
168 this._dataGrid = this._currentPerspective.masterGrid(this);
175 * @param {string} title
177 WebInspector.HeapSnapshotView.Perspective = function(title)
182 WebInspector.HeapSnapshotView.Perspective.prototype = {
184 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
186 activate: function(heapSnapshotView) { },
189 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
191 deactivate: function(heapSnapshotView)
193 heapSnapshotView._baseSelect.visible = false;
194 heapSnapshotView._filterSelect.visible = false;
195 heapSnapshotView._classNameFilter.visible = false;
196 if (heapSnapshotView._trackingOverviewGrid)
197 heapSnapshotView._trackingOverviewGrid.detach();
198 if (heapSnapshotView._allocationView)
199 heapSnapshotView._allocationView.detach();
200 if (heapSnapshotView._statisticsView)
201 heapSnapshotView._statisticsView.detach();
203 heapSnapshotView._splitView.detach();
204 heapSnapshotView._splitView.detachChildViews();
208 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
209 * @return {?WebInspector.DataGrid}
211 masterGrid: function(heapSnapshotView)
227 supportsSearch: function()
235 * @extends {WebInspector.HeapSnapshotView.Perspective}
237 WebInspector.HeapSnapshotView.SummaryPerspective = function()
239 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Summary"));
242 WebInspector.HeapSnapshotView.SummaryPerspective.prototype = {
245 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
247 activate: function(heapSnapshotView)
249 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
250 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
251 heapSnapshotView._splitView.show(heapSnapshotView.element);
252 heapSnapshotView._filterSelect.visible = true;
253 heapSnapshotView._classNameFilter.visible = true;
254 if (heapSnapshotView._trackingOverviewGrid) {
255 heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView.element, heapSnapshotView._splitView.element);
256 heapSnapshotView._trackingOverviewGrid.update();
257 heapSnapshotView._trackingOverviewGrid._updateGrid();
263 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
264 * @return {?WebInspector.DataGrid}
266 masterGrid: function(heapSnapshotView)
268 return heapSnapshotView._constructorsDataGrid;
275 supportsSearch: function()
280 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
285 * @extends {WebInspector.HeapSnapshotView.Perspective}
287 WebInspector.HeapSnapshotView.ComparisonPerspective = function()
289 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Comparison"));
292 WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = {
295 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
297 activate: function(heapSnapshotView)
299 heapSnapshotView._diffView.show(heapSnapshotView._splitView.mainElement());
300 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
301 heapSnapshotView._splitView.show(heapSnapshotView.element);
302 heapSnapshotView._baseSelect.visible = true;
303 heapSnapshotView._classNameFilter.visible = true;
308 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
309 * @return {?WebInspector.DataGrid}
311 masterGrid: function(heapSnapshotView)
313 return heapSnapshotView._diffDataGrid;
320 supportsSearch: function()
325 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
330 * @extends {WebInspector.HeapSnapshotView.Perspective}
332 WebInspector.HeapSnapshotView.ContainmentPerspective = function()
334 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Containment"));
337 WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = {
340 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
342 activate: function(heapSnapshotView)
344 heapSnapshotView._containmentView.show(heapSnapshotView._splitView.mainElement());
345 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
346 heapSnapshotView._splitView.show(heapSnapshotView.element);
351 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
352 * @return {?WebInspector.DataGrid}
354 masterGrid: function(heapSnapshotView)
356 return heapSnapshotView._containmentDataGrid;
358 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
363 * @extends {WebInspector.HeapSnapshotView.Perspective}
365 WebInspector.HeapSnapshotView.DominatorPerspective = function()
367 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Dominators"));
370 WebInspector.HeapSnapshotView.DominatorPerspective.prototype = {
373 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
375 activate: function(heapSnapshotView)
377 heapSnapshotView._dominatorView.show(heapSnapshotView._splitView.mainElement());
378 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
379 heapSnapshotView._splitView.show(heapSnapshotView.element);
384 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
385 * @return {?WebInspector.DataGrid}
387 masterGrid: function(heapSnapshotView)
389 return heapSnapshotView._dominatorDataGrid;
392 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
397 * @extends {WebInspector.HeapSnapshotView.Perspective}
399 WebInspector.HeapSnapshotView.AllocationPerspective = function()
401 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Allocation"));
402 this._allocationSplitView = new WebInspector.SplitView(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
404 var resizer = document.createElementWithClass("div", "heap-snapshot-view-resizer");
405 var title = resizer.createChild("div", "title").createChild("span");
406 title.textContent = WebInspector.UIString("Live objects");
407 this._allocationSplitView.hideDefaultResizer();
408 this._allocationSplitView.installResizer(resizer);
410 this._allocationSplitView.sidebarElement().appendChild(resizer);
413 WebInspector.HeapSnapshotView.AllocationPerspective.prototype = {
416 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
418 activate: function(heapSnapshotView)
420 heapSnapshotView._allocationView.show(this._allocationSplitView.mainElement());
421 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
422 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
423 heapSnapshotView._splitView.show(this._allocationSplitView.sidebarElement());
424 this._allocationSplitView.show(heapSnapshotView.element);
426 heapSnapshotView._constructorsDataGrid.clear();
427 var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode;
429 heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
434 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
436 deactivate: function(heapSnapshotView)
438 this._allocationSplitView.detach();
439 WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView);
444 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
445 * @return {?WebInspector.DataGrid}
447 masterGrid: function(heapSnapshotView)
449 return heapSnapshotView._allocationDataGrid;
452 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
457 * @extends {WebInspector.HeapSnapshotView.Perspective}
459 WebInspector.HeapSnapshotView.StatisticsPerspective = function()
461 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Statistics"));
464 WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = {
467 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
469 activate: function(heapSnapshotView)
471 heapSnapshotView._statisticsView.show(heapSnapshotView.element);
476 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
477 * @return {?WebInspector.DataGrid}
479 masterGrid: function(heapSnapshotView)
484 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
488 WebInspector.HeapSnapshotView.prototype = {
489 _refreshView: function()
491 this._profile.load(profileCallback.bind(this));
494 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
495 * @this {WebInspector.HeapSnapshotView}
497 function profileCallback(heapSnapshotProxy)
499 heapSnapshotProxy.getStatistics(this._gotStatistics.bind(this));
500 var list = this._profiles();
501 var profileIndex = list.indexOf(this._profile);
502 this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
503 this._dataGrid.setDataSource(heapSnapshotProxy);
504 if (this._trackingOverviewGrid)
505 this._trackingOverviewGrid._updateGrid();
510 * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
512 _gotStatistics: function(statistics) {
513 this._statisticsView.setTotal(statistics.total);
514 this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77");
515 this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5");
516 this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af");
517 this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5");
518 this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total"));
521 _onIdsRangeChanged: function(event)
523 var minId = event.data.minId;
524 var maxId = event.data.maxId;
525 this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
526 if (this._constructorsDataGrid.snapshot)
527 this._constructorsDataGrid.setSelectionRange(minId, maxId);
532 var result = [this._perspectiveSelect.element, this._classNameFilter.element];
533 if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
534 result.push(this._baseSelect.element, this._filterSelect.element);
535 result.push(this._selectedSizeText.element);
541 // FIXME: load base and current snapshots in parallel
542 this._profile.load(profileCallback.bind(this));
545 * @this {WebInspector.HeapSnapshotView}
547 function profileCallback() {
548 this._profile._wasShown();
549 if (this._baseProfile)
550 this._baseProfile.load(function() { });
556 this._currentSearchResultIndex = -1;
557 this._popoverHelper.hidePopover();
558 if (this.helpPopover && this.helpPopover.isShowing())
559 this.helpPopover.hide();
562 searchCanceled: function()
564 if (this._searchResults) {
565 for (var i = 0; i < this._searchResults.length; ++i) {
566 var node = this._searchResults[i].node;
567 delete node._searchMatched;
572 delete this._searchFinishedCallback;
573 this._currentSearchResultIndex = -1;
574 this._searchResults = [];
578 * @param {string} query
579 * @param {function(!WebInspector.View, number)} finishedCallback
581 performSearch: function(query, finishedCallback)
583 // Call searchCanceled since it will reset everything we need before doing a new search.
584 this.searchCanceled();
586 query = query.trim();
590 if (!this._currentPerspective.supportsSearch())
594 * @param {boolean} found
595 * @this {WebInspector.HeapSnapshotView}
597 function didHighlight(found)
599 finishedCallback(this, found ? 1 : 0);
602 if (query.charAt(0) === "@") {
603 var snapshotNodeId = parseInt(query.substring(1), 10);
604 if (!isNaN(snapshotNodeId))
605 this._dataGrid.highlightObjectByHeapSnapshotId(String(snapshotNodeId), didHighlight.bind(this));
607 finishedCallback(this, 0);
611 this._searchFinishedCallback = finishedCallback;
612 var nameRegExp = createPlainTextSearchRegex(query, "i");
614 function matchesByName(gridNode) {
615 return ("_name" in gridNode) && nameRegExp.test(gridNode._name);
618 function matchesQuery(gridNode)
620 delete gridNode._searchMatched;
621 if (matchesByName(gridNode)) {
622 gridNode._searchMatched = true;
629 var current = this._dataGrid.rootNode().children[0];
633 // Restrict to type nodes and instances.
637 if (matchesQuery(current))
638 this._searchResults.push({ node: current });
639 current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
640 depth += info.depthChange;
643 finishedCallback(this, this._searchResults.length);
646 jumpToFirstSearchResult: function()
648 if (!this._searchResults || !this._searchResults.length)
650 this._currentSearchResultIndex = 0;
651 this._jumpToSearchResult(this._currentSearchResultIndex);
654 jumpToLastSearchResult: function()
656 if (!this._searchResults || !this._searchResults.length)
658 this._currentSearchResultIndex = (this._searchResults.length - 1);
659 this._jumpToSearchResult(this._currentSearchResultIndex);
662 jumpToNextSearchResult: function()
664 if (!this._searchResults || !this._searchResults.length)
666 if (++this._currentSearchResultIndex >= this._searchResults.length)
667 this._currentSearchResultIndex = 0;
668 this._jumpToSearchResult(this._currentSearchResultIndex);
671 jumpToPreviousSearchResult: function()
673 if (!this._searchResults || !this._searchResults.length)
675 if (--this._currentSearchResultIndex < 0)
676 this._currentSearchResultIndex = (this._searchResults.length - 1);
677 this._jumpToSearchResult(this._currentSearchResultIndex);
683 showingFirstSearchResult: function()
685 return (this._currentSearchResultIndex === 0);
691 showingLastSearchResult: function()
693 return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
699 currentSearchResultIndex: function() {
700 return this._currentSearchResultIndex;
703 _jumpToSearchResult: function(index)
705 var searchResult = this._searchResults[index];
709 var node = searchResult.node;
710 node.revealAndSelect();
713 refreshVisibleData: function()
717 var child = this._dataGrid.rootNode().children[0];
720 child = child.traverseNextNode(false, null, true);
724 _changeBase: function()
726 if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()])
729 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
730 var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid);
731 // Change set base data source only if main data source is already set.
732 if (dataGrid.snapshot)
733 this._baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid));
735 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
738 // The current search needs to be performed again. First negate out previous match
739 // count by calling the search finished callback with a negative number of matches.
740 // Then perform the search again with the same query and callback.
741 this._searchFinishedCallback(this, -this._searchResults.length);
742 this.performSearch(this.currentQuery, this._searchFinishedCallback);
745 _changeFilter: function()
747 var profileIndex = this._filterSelect.selectedIndex() - 1;
748 this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
750 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
751 action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
752 label: this._filterSelect.selectedOption().label
755 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
758 // The current search needs to be performed again. First negate out previous match
759 // count by calling the search finished callback with a negative number of matches.
760 // Then perform the search again with the same query and callback.
761 this._searchFinishedCallback(this, -this._searchResults.length);
762 this.performSearch(this.currentQuery, this._searchFinishedCallback);
766 * @return {!Array.<!WebInspector.ProfileHeader>}
768 _profiles: function()
770 return this._profile.profileType().getProfiles();
774 * @param {!WebInspector.ContextMenu} contextMenu
775 * @param {?Event} event
777 populateContextMenu: function(contextMenu, event)
780 this._dataGrid.populateContextMenu(contextMenu, event);
783 _selectionChanged: function(event)
785 var selectedNode = event.target.selectedNode;
786 this._setSelectedNodeForDetailsView(selectedNode);
787 this._inspectedObjectChanged(event);
790 _onSelectAllocationNode: function(event)
792 var selectedNode = event.target.selectedNode;
793 this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
794 this._setSelectedNodeForDetailsView(null);
797 _inspectedObjectChanged: function(event)
799 var selectedNode = event.target.selectedNode;
800 if (!this._profile.fromFile() && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
801 ConsoleAgent.addInspectedHeapObject(selectedNode.snapshotNodeId);
805 * @param {?WebInspector.HeapSnapshotGridNode} nodeItem
807 _setSelectedNodeForDetailsView: function(nodeItem)
809 var dataSource = nodeItem && nodeItem.retainersDataSource();
811 this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
812 if (this._allocationStackView)
813 this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex)
815 if (this._allocationStackView)
816 this._allocationStackView.clear();
817 this._retainmentDataGrid.reset();
822 * @param {string} perspectiveTitle
823 * @param {function()} callback
825 _changePerspectiveAndWait: function(perspectiveTitle, callback)
827 var perspectiveIndex = null;
828 for (var i = 0; i < this._perspectives.length; ++i) {
829 if (this._perspectives[i].title() === perspectiveTitle) {
830 perspectiveIndex = i;
834 if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) {
835 setTimeout(callback, 0);
840 * @this {WebInspector.HeapSnapshotView}
842 function dataGridContentShown(event)
844 var dataGrid = event.data;
845 dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
846 if (dataGrid === this._dataGrid)
849 this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
851 this._perspectiveSelect.setSelectedIndex(perspectiveIndex);
852 this._changePerspective(perspectiveIndex);
855 _updateDataSourceAndView: function()
857 var dataGrid = this._dataGrid;
858 if (!dataGrid || dataGrid.snapshot)
861 this._profile.load(didLoadSnapshot.bind(this));
864 * @this {WebInspector.HeapSnapshotView}
866 function didLoadSnapshot(snapshotProxy)
868 if (this._dataGrid !== dataGrid)
870 if (dataGrid.snapshot !== snapshotProxy)
871 dataGrid.setDataSource(snapshotProxy);
872 if (dataGrid === this._diffDataGrid) {
873 if (!this._baseProfile)
874 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
875 this._baseProfile.load(didLoadBaseSnaphot.bind(this));
880 * @this {WebInspector.HeapSnapshotView}
882 function didLoadBaseSnaphot(baseSnapshotProxy)
884 if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy)
885 this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
889 _onSelectedPerspectiveChanged: function(event)
891 this._changePerspective(event.target.selectedIndex);
892 // FIXME: This is needed by CodeSchool extension.
893 this._onSelectedViewChanged(event);
896 _onSelectedViewChanged: function(event)
900 _changePerspective: function(selectedIndex)
902 if (selectedIndex === this._currentPerspectiveIndex)
905 this._currentPerspectiveIndex = selectedIndex;
907 this._currentPerspective.deactivate(this);
908 var perspective = this._perspectives[selectedIndex];
909 this._currentPerspective = perspective;
910 this._dataGrid = perspective.masterGrid(this);
911 perspective.activate(this);
913 this.refreshVisibleData();
915 this._dataGrid.updateWidths();
917 this._updateDataSourceAndView();
919 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
922 // The current search needs to be performed again. First negate out previous match
923 // count by calling the search finished callback with a negative number of matches.
924 // Then perform the search again the with same query and callback.
925 this._searchFinishedCallback(this, -this._searchResults.length);
926 this.performSearch(this.currentQuery, this._searchFinishedCallback);
930 * @param {string} perspectiveName
931 * @param {number} snapshotObjectId
933 highlightLiveObject: function(perspectiveName, snapshotObjectId)
935 this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this));
938 * @this {WebInspector.HeapSnapshotView}
940 function didChangePerspective()
942 this._dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId, didHighlightObject);
945 function didHighlightObject(found)
948 WebInspector.console.log("Cannot find corresponding heap snapshot node", WebInspector.ConsoleMessage.MessageLevel.Error, true);
952 _getHoverAnchor: function(target)
954 var span = target.enclosingNodeOrSelfWithNodeName("span");
957 var row = target.enclosingNodeOrSelfWithNodeName("tr");
960 span.node = row._dataGridNode;
964 _resolveObjectForPopover: function(element, showCallback, objectGroupName)
966 if (this._profile.fromFile())
968 element.node.queryObjectContent(showCallback, objectGroupName);
971 _updateBaseOptions: function()
973 var list = this._profiles();
974 // We're assuming that snapshots can only be added.
975 if (this._baseSelect.size() === list.length)
978 for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) {
979 var title = list[i].title;
980 this._baseSelect.createOption(title);
984 _updateFilterOptions: function()
986 var list = this._profiles();
987 // We're assuming that snapshots can only be added.
988 if (this._filterSelect.size() - 1 === list.length)
991 if (!this._filterSelect.size())
992 this._filterSelect.createOption(WebInspector.UIString("All objects"));
994 for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
995 var title = list[i].title;
997 title = WebInspector.UIString("Objects allocated before %s", title);
999 title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
1000 this._filterSelect.createOption(title);
1004 _updateControls: function()
1006 this._updateBaseOptions();
1007 this._updateFilterOptions();
1011 * @param {!WebInspector.Event} event
1013 _onReceiveSnapshot: function(event)
1015 this._updateControls();
1019 * @param {!WebInspector.Event} event
1021 _onProfileHeaderRemoved: function(event)
1023 var profile = event.data;
1024 if (this._profile === profile) {
1026 this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
1027 this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
1030 this._updateControls();
1036 if (this._allocationStackView) {
1037 this._allocationStackView.clear();
1038 this._allocationDataGrid.dispose();
1042 __proto__: WebInspector.VBox.prototype
1047 * @implements {HeapProfilerAgent.Dispatcher}
1049 WebInspector.HeapProfilerDispatcher = function()
1051 this._dispatchers = [];
1052 InspectorBackend.registerHeapProfilerDispatcher(this);
1055 WebInspector.HeapProfilerDispatcher.prototype = {
1057 * @param {!HeapProfilerAgent.Dispatcher} dispatcher
1059 register: function(dispatcher)
1061 this._dispatchers.push(dispatcher);
1064 _genericCaller: function(eventName)
1066 var args = Array.prototype.slice.call(arguments.callee.caller.arguments);
1067 for (var i = 0; i < this._dispatchers.length; ++i)
1068 this._dispatchers[i][eventName].apply(this._dispatchers[i], args);
1073 * @param {!Array.<number>} samples
1075 heapStatsUpdate: function(samples)
1077 this._genericCaller("heapStatsUpdate");
1082 * @param {number} lastSeenObjectId
1083 * @param {number} timestamp
1085 lastSeenObjectId: function(lastSeenObjectId, timestamp)
1087 this._genericCaller("lastSeenObjectId");
1092 * @param {string} chunk
1094 addHeapSnapshotChunk: function(chunk)
1096 this._genericCaller("addHeapSnapshotChunk");
1101 * @param {number} done
1102 * @param {number} total
1103 * @param {boolean=} finished
1105 reportHeapSnapshotProgress: function(done, total, finished)
1107 this._genericCaller("reportHeapSnapshotProgress");
1113 resetProfiles: function()
1115 this._genericCaller("resetProfiles");
1119 WebInspector.HeapProfilerDispatcher._dispatcher = new WebInspector.HeapProfilerDispatcher();
1123 * @extends {WebInspector.ProfileType}
1124 * @implements {HeapProfilerAgent.Dispatcher}
1125 * @param {string=} id
1126 * @param {string=} title
1128 WebInspector.HeapSnapshotProfileType = function(id, title)
1130 WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
1131 WebInspector.HeapProfilerDispatcher._dispatcher.register(this);
1134 WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
1135 WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
1137 WebInspector.HeapSnapshotProfileType.prototype = {
1142 fileExtension: function()
1144 return ".heapsnapshot";
1149 return WebInspector.UIString("Take heap snapshot.");
1156 isInstantProfile: function()
1165 buttonClicked: function()
1167 this._takeHeapSnapshot(function() {});
1168 WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
1174 * @param {!Array.<number>} samples
1176 heapStatsUpdate: function(samples)
1182 * @param {number} lastSeenObjectId
1183 * @param {number} timestamp
1185 lastSeenObjectId: function(lastSeenObjectId, timestamp)
1191 return WebInspector.UIString("HEAP SNAPSHOTS");
1196 return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
1201 * @param {!string} title
1202 * @return {!WebInspector.ProfileHeader}
1204 createProfileLoadedFromFile: function(title)
1206 var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
1207 return new WebInspector.HeapProfileHeader(target, this, title);
1210 _takeHeapSnapshot: function(callback)
1212 if (this.profileBeingRecorded())
1214 var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
1215 var profile = new WebInspector.HeapProfileHeader(target, this);
1216 this.setProfileBeingRecorded(profile);
1217 this.addProfile(profile);
1218 profile.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1221 * @param {?string} error
1222 * @this {WebInspector.HeapSnapshotProfileType}
1224 function didTakeHeapSnapshot(error)
1226 var profile = this._profileBeingRecorded;
1227 profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
1228 profile._finishLoad();
1229 this.setProfileBeingRecorded(null);
1230 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
1233 HeapProfilerAgent.takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
1238 * @param {string} chunk
1240 addHeapSnapshotChunk: function(chunk)
1242 if (!this.profileBeingRecorded())
1244 this.profileBeingRecorded().transferChunk(chunk);
1249 * @param {number} done
1250 * @param {number} total
1251 * @param {boolean=} finished
1253 reportHeapSnapshotProgress: function(done, total, finished)
1255 var profile = this.profileBeingRecorded();
1258 profile.updateStatus(WebInspector.UIString("%.0f%", (done / total) * 100), true);
1260 profile._prepareToLoad();
1266 resetProfiles: function()
1271 _snapshotReceived: function(profile)
1273 if (this._profileBeingRecorded === profile)
1274 this.setProfileBeingRecorded(null);
1275 this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
1278 __proto__: WebInspector.ProfileType.prototype
1284 * @extends {WebInspector.HeapSnapshotProfileType}
1286 WebInspector.TrackingHeapSnapshotProfileType = function()
1288 WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
1291 WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
1293 WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
1294 WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
1295 WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
1297 WebInspector.TrackingHeapSnapshotProfileType.prototype = {
1301 * @param {!Array.<number>} samples
1303 heapStatsUpdate: function(samples)
1305 if (!this._profileSamples)
1308 for (var i = 0; i < samples.length; i += 3) {
1310 var count = samples[i+1];
1311 var size = samples[i+2];
1312 this._profileSamples.sizes[index] = size;
1313 if (!this._profileSamples.max[index])
1314 this._profileSamples.max[index] = size;
1320 * @param {number} lastSeenObjectId
1321 * @param {number} timestamp
1323 lastSeenObjectId: function(lastSeenObjectId, timestamp)
1325 var profileSamples = this._profileSamples;
1326 if (!profileSamples)
1328 var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
1329 profileSamples.ids[currentIndex] = lastSeenObjectId;
1330 if (!profileSamples.max[currentIndex]) {
1331 profileSamples.max[currentIndex] = 0;
1332 profileSamples.sizes[currentIndex] = 0;
1334 profileSamples.timestamps[currentIndex] = timestamp;
1335 if (profileSamples.totalTime < timestamp - profileSamples.timestamps[0])
1336 profileSamples.totalTime *= 2;
1337 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
1338 this._profileBeingRecorded.updateStatus(null, true);
1345 hasTemporaryView: function()
1352 return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
1359 isInstantProfile: function()
1368 buttonClicked: function()
1370 return this._toggleRecording();
1373 _startRecordingProfile: function()
1375 if (this.profileBeingRecorded())
1377 this._addNewProfile();
1378 HeapProfilerAgent.startTrackingHeapObjects(WebInspector.experimentsSettings.allocationProfiler.isEnabled());
1381 _addNewProfile: function()
1383 var target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
1384 this.setProfileBeingRecorded(new WebInspector.HeapProfileHeader(target, this));
1385 this._lastSeenIndex = -1;
1386 this._profileSamples = {
1393 this._profileBeingRecorded._profileSamples = this._profileSamples;
1394 this._recording = true;
1395 this.addProfile(this._profileBeingRecorded);
1396 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
1397 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
1400 _stopRecordingProfile: function()
1402 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1404 * @param {?string} error
1405 * @this {WebInspector.HeapSnapshotProfileType}
1407 function didTakeHeapSnapshot(error)
1409 var profile = this._profileBeingRecorded;
1412 profile._finishLoad();
1413 this._profileSamples = null;
1414 this.setProfileBeingRecorded(null);
1415 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
1418 HeapProfilerAgent.stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
1419 this._recording = false;
1420 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
1423 _toggleRecording: function()
1425 if (this._recording)
1426 this._stopRecordingProfile();
1428 this._startRecordingProfile();
1429 return this._recording;
1434 return WebInspector.UIString("HEAP TIMELINES");
1439 return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
1445 resetProfiles: function()
1447 var wasRecording = this._recording;
1448 // Clear current profile to avoid stopping backend.
1449 this.setProfileBeingRecorded(null);
1450 WebInspector.HeapSnapshotProfileType.prototype.resetProfiles.call(this);
1451 this._profileSamples = null;
1452 this._lastSeenIndex = -1;
1454 this._addNewProfile();
1460 profileBeingRecordedRemoved: function()
1462 this._stopRecordingProfile();
1463 this._profileSamples = null;
1466 __proto__: WebInspector.HeapSnapshotProfileType.prototype
1471 * @extends {WebInspector.ProfileHeader}
1472 * @param {!WebInspector.Target} target
1473 * @param {!WebInspector.HeapSnapshotProfileType} type
1474 * @param {string=} title
1476 WebInspector.HeapProfileHeader = function(target, type, title)
1478 WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Snapshot %d", type._nextProfileUid));
1479 this.maxJSObjectId = -1;
1481 * @type {?WebInspector.HeapSnapshotWorkerProxy}
1483 this._workerProxy = null;
1485 * @type {?WebInspector.OutputStream}
1487 this._receiver = null;
1489 * @type {?WebInspector.HeapSnapshotProxy}
1491 this._snapshotProxy = null;
1493 * @type {?Array.<function(!WebInspector.HeapSnapshotProxy)>}
1495 this._loadCallbacks = [];
1496 this._totalNumberOfChunks = 0;
1497 this._bufferedWriter = null;
1500 WebInspector.HeapProfileHeader.prototype = {
1503 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1504 * @return {!WebInspector.ProfileSidebarTreeElement}
1506 createSidebarTreeElement: function(dataDisplayDelegate)
1508 return new WebInspector.ProfileSidebarTreeElement(dataDisplayDelegate, this, "heap-snapshot-sidebar-tree-item");
1513 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1514 * @return {!WebInspector.HeapSnapshotView}
1516 createView: function(dataDisplayDelegate)
1518 return new WebInspector.HeapSnapshotView(dataDisplayDelegate, this);
1523 * @param {function(!WebInspector.HeapSnapshotProxy):void} callback
1525 load: function(callback)
1527 if (this.uid === -1)
1529 if (this._snapshotProxy) {
1530 callback(this._snapshotProxy);
1533 this._loadCallbacks.push(callback);
1536 _prepareToLoad: function()
1538 console.assert(!this._receiver, "Already loading");
1539 this._setupWorker();
1540 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1543 _finishLoad: function()
1545 if (!this._wasDisposed)
1546 this._receiver.close(function() {});
1547 if (this._bufferedWriter) {
1548 this._bufferedWriter.close(this._didWriteToTempFile.bind(this));
1549 this._bufferedWriter = null;
1553 _didWriteToTempFile: function(tempFile)
1555 if (this._wasDisposed) {
1560 this._tempFile = tempFile;
1562 this._failedToCreateTempFile = true;
1563 if (this._onTempFileReady) {
1564 this._onTempFileReady();
1565 this._onTempFileReady = null;
1569 _setupWorker: function()
1572 * @this {WebInspector.HeapProfileHeader}
1574 function setProfileWait(event)
1576 this.updateStatus(null, event.data);
1578 console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists");
1579 this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this));
1580 this._workerProxy.addEventListener("wait", setProfileWait, this);
1581 this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this));
1585 * @param {string} eventName
1588 _handleWorkerEvent: function(eventName, data)
1590 if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName)
1592 var subtitle = /** @type {string} */ (data);
1593 this.updateStatus(subtitle);
1601 if (this._workerProxy)
1602 this._workerProxy.dispose();
1603 this.removeTempFile();
1604 this._wasDisposed = true;
1607 _didCompleteSnapshotTransfer: function()
1609 if (!this._snapshotProxy)
1611 this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false);
1615 * @param {string} chunk
1617 transferChunk: function(chunk)
1619 if (!this._bufferedWriter)
1620 this._bufferedWriter = new WebInspector.BufferedTempFileWriter("heap-profiler", this.uid);
1621 this._bufferedWriter.write(chunk);
1623 ++this._totalNumberOfChunks;
1624 this._receiver.write(chunk, function() {});
1627 _snapshotReceived: function(snapshotProxy)
1629 if (this._wasDisposed)
1631 this._receiver = null;
1632 this._snapshotProxy = snapshotProxy;
1633 this.maxJSObjectId = snapshotProxy.maxJSObjectId();
1634 this._didCompleteSnapshotTransfer();
1635 this._workerProxy.startCheckingForLongRunningCalls();
1636 this.notifySnapshotReceived();
1639 notifySnapshotReceived: function()
1641 for (var i = 0; i < this._loadCallbacks.length; i++)
1642 this._loadCallbacks[i](this._snapshotProxy);
1643 this._loadCallbacks = null;
1644 this._profileType._snapshotReceived(this);
1645 if (this.canSaveToFile())
1646 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
1649 // Hook point for tests.
1650 _wasShown: function()
1658 canSaveToFile: function()
1660 return !this.fromFile() && this._snapshotProxy;
1666 saveToFile: function()
1668 var fileOutputStream = new WebInspector.FileOutputStream();
1671 * @param {boolean} accepted
1672 * @this {WebInspector.HeapProfileHeader}
1674 function onOpen(accepted)
1678 if (this._failedToCreateTempFile) {
1679 WebInspector.console.log("Failed to open temp file with heap snapshot",
1680 WebInspector.ConsoleMessage.MessageLevel.Error);
1681 fileOutputStream.close();
1682 } else if (this._tempFile) {
1683 var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this);
1684 this._tempFile.writeToOutputSteam(fileOutputStream, delegate);
1686 this._onTempFileReady = onOpen.bind(this, accepted);
1687 this._updateSaveProgress(0, 1);
1690 this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
1691 fileOutputStream.open(this._fileName, onOpen.bind(this));
1694 _updateSaveProgress: function(value, total)
1696 var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
1697 this.updateStatus(WebInspector.UIString("Saving\u2026 %d\%", percentValue));
1702 * @param {!File} file
1704 loadFromFile: function(file)
1706 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1707 this._setupWorker();
1708 var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
1709 var fileReader = this._createFileReader(file, delegate);
1710 fileReader.start(this._receiver);
1713 _createFileReader: function(file, delegate)
1715 return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
1718 __proto__: WebInspector.ProfileHeader.prototype
1723 * @implements {WebInspector.OutputStreamDelegate}
1725 WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
1727 this._snapshotHeader = snapshotHeader;
1730 WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
1731 onTransferStarted: function()
1736 * @param {!WebInspector.ChunkedReader} reader
1738 onChunkTransferred: function(reader)
1742 onTransferFinished: function()
1747 * @param {!WebInspector.ChunkedReader} reader
1749 onError: function (reader, e)
1752 switch(e.target.error.code) {
1753 case e.target.error.NOT_FOUND_ERR:
1754 subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
1756 case e.target.error.NOT_READABLE_ERR:
1757 subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
1759 case e.target.error.ABORT_ERR:
1762 subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
1764 this._snapshotHeader.updateStatus(subtitle);
1770 * @implements {WebInspector.OutputStreamDelegate}
1771 * @param {!WebInspector.HeapProfileHeader} profileHeader
1773 WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
1775 this._profileHeader = profileHeader;
1778 WebInspector.SaveSnapshotOutputStreamDelegate.prototype = {
1779 onTransferStarted: function()
1781 this._profileHeader._updateSaveProgress(0, 1);
1784 onTransferFinished: function()
1786 this._profileHeader._didCompleteSnapshotTransfer();
1790 * @param {!WebInspector.ChunkedReader} reader
1792 onChunkTransferred: function(reader)
1794 this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize());
1798 * @param {!WebInspector.ChunkedReader} reader
1800 onError: function(reader, event)
1802 WebInspector.console.log("Failed to read heap snapshot from temp file: " + event.message,
1803 WebInspector.ConsoleMessage.MessageLevel.Error);
1804 this.onTransferFinished();
1810 * @extends {WebInspector.VBox}
1811 * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
1813 WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
1815 WebInspector.VBox.call(this);
1816 this.registerRequiredCSS("flameChart.css");
1817 this.element.id = "heap-recording-view";
1818 this.element.classList.add("heap-tracking-overview");
1820 this._overviewContainer = this.element.createChild("div", "overview-container");
1821 this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
1822 this._overviewGrid.element.classList.add("fill");
1824 this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
1825 this._overviewContainer.appendChild(this._overviewGrid.element);
1826 this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
1827 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
1829 this._profileSamples = heapProfileHeader._profileSamples;
1830 if (heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) {
1831 this._profileType = heapProfileHeader._profileType;
1832 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1833 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1835 var timestamps = this._profileSamples.timestamps;
1836 var totalTime = this._profileSamples.totalTime;
1837 this._windowLeft = 0.0;
1838 this._windowRight = totalTime && timestamps.length ? (timestamps[timestamps.length - 1] - timestamps[0]) / totalTime : 1.0;
1839 this._overviewGrid.setWindow(this._windowLeft, this._windowRight);
1840 this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1841 this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1844 WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
1846 WebInspector.HeapTrackingOverviewGrid.prototype = {
1847 _onStopTracking: function(event)
1849 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1850 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1853 _onHeapStatsUpdate: function(event)
1855 this._profileSamples = event.data;
1856 this._scheduleUpdate();
1860 * @param {number} width
1861 * @param {number} height
1863 _drawOverviewCanvas: function(width, height)
1865 if (!this._profileSamples)
1867 var profileSamples = this._profileSamples;
1868 var sizes = profileSamples.sizes;
1869 var topSizes = profileSamples.max;
1870 var timestamps = profileSamples.timestamps;
1871 var startTime = timestamps[0];
1872 var endTime = timestamps[timestamps.length - 1];
1874 var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime);
1877 * @param {!Array.<number>} sizes
1878 * @param {function(number, number):void} callback
1880 function aggregateAndCall(sizes, callback)
1884 for (var i = 1; i < timestamps.length; ++i) {
1885 var x = Math.floor((timestamps[i] - startTime) * scaleFactor);
1886 if (x !== currentX) {
1888 callback(currentX, size);
1894 callback(currentX, size);
1899 * @param {number} size
1901 function maxSizeCallback(x, size)
1903 maxSize = Math.max(maxSize, size);
1906 aggregateAndCall(sizes, maxSizeCallback);
1908 var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0);
1910 this._overviewCanvas.width = width * window.devicePixelRatio;
1911 this._overviewCanvas.height = height * window.devicePixelRatio;
1912 this._overviewCanvas.style.width = width + "px";
1913 this._overviewCanvas.style.height = height + "px";
1915 var context = this._overviewCanvas.getContext("2d");
1916 context.scale(window.devicePixelRatio, window.devicePixelRatio);
1918 context.beginPath();
1919 context.lineWidth = 2;
1920 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1921 var currentX = (endTime - startTime) * scaleFactor;
1922 context.moveTo(currentX, height - 1);
1923 context.lineTo(currentX, 0);
1925 context.closePath();
1929 var gridLabelHeight = 14;
1931 const maxGridValue = (height - gridLabelHeight) / yScaleFactor;
1932 // The round value calculation is a bit tricky, because
1933 // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
1934 // e.g. a round value 10KB is 10240 bytes.
1935 gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024)));
1936 gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10));
1937 if (gridValue * 5 <= maxGridValue)
1939 gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5;
1940 context.beginPath();
1941 context.lineWidth = 1;
1942 context.strokeStyle = "rgba(0, 0, 0, 0.2)";
1943 context.moveTo(0, gridY);
1944 context.lineTo(width, gridY);
1946 context.closePath();
1951 * @param {number} size
1953 function drawBarCallback(x, size)
1955 context.moveTo(x, height - 1);
1956 context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
1959 context.beginPath();
1960 context.lineWidth = 2;
1961 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1962 aggregateAndCall(topSizes, drawBarCallback);
1964 context.closePath();
1966 context.beginPath();
1967 context.lineWidth = 2;
1968 context.strokeStyle = "rgba(0, 0, 192, 0.8)";
1969 aggregateAndCall(sizes, drawBarCallback);
1971 context.closePath();
1974 var label = Number.bytesToString(gridValue);
1975 var labelPadding = 4;
1977 var labelY = gridY - 0.5;
1978 var labelWidth = 2 * labelPadding + context.measureText(label).width;
1979 context.beginPath();
1980 context.textBaseline = "bottom";
1981 context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
1982 context.fillStyle = "rgba(255, 255, 255, 0.75)";
1983 context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight);
1984 context.fillStyle = "rgb(64, 64, 64)";
1985 context.fillText(label, labelX + labelPadding, labelY);
1987 context.closePath();
1991 onResize: function()
1993 this._updateOverviewCanvas = true;
1994 this._scheduleUpdate();
1997 _onWindowChanged: function()
1999 if (!this._updateGridTimerId)
2000 this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
2003 _scheduleUpdate: function()
2005 if (this._updateTimerId)
2007 this._updateTimerId = setTimeout(this.update.bind(this), 10);
2010 _updateBoundaries: function()
2012 this._windowLeft = this._overviewGrid.windowLeft();
2013 this._windowRight = this._overviewGrid.windowRight();
2014 this._windowWidth = this._windowRight - this._windowLeft;
2019 this._updateTimerId = null;
2020 if (!this.isShowing())
2022 this._updateBoundaries();
2023 this._overviewCalculator._updateBoundaries(this);
2024 this._overviewGrid.updateDividers(this._overviewCalculator);
2025 this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
2028 _updateGrid: function()
2030 this._updateGridTimerId = 0;
2031 this._updateBoundaries();
2032 var ids = this._profileSamples.ids;
2033 var timestamps = this._profileSamples.timestamps;
2034 var sizes = this._profileSamples.sizes;
2035 var startTime = timestamps[0];
2036 var totalTime = this._profileSamples.totalTime;
2037 var timeLeft = startTime + totalTime * this._windowLeft;
2038 var timeRight = startTime + totalTime * this._windowRight;
2040 var maxId = ids[ids.length - 1] + 1;
2042 for (var i = 0; i < timestamps.length; ++i) {
2045 if (timestamps[i] > timeRight)
2048 if (timestamps[i] < timeLeft) {
2055 this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
2058 __proto__: WebInspector.VBox.prototype
2065 WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
2067 this._lastUpdate = 0;
2068 this._currentScale = 0.0;
2071 WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = {
2073 * @param {number} target
2076 nextScale: function(target) {
2077 target = target || this._currentScale;
2078 if (this._currentScale) {
2079 var now = Date.now();
2080 var timeDeltaMs = now - this._lastUpdate;
2081 this._lastUpdate = now;
2082 var maxChangePerSec = 20;
2083 var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000);
2084 var scaleChange = target / this._currentScale;
2085 this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta);
2087 this._currentScale = target;
2088 return this._currentScale;
2095 * @implements {WebInspector.TimelineGrid.Calculator}
2097 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
2101 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
2105 paddingLeft: function()
2111 * @param {!WebInspector.HeapTrackingOverviewGrid} chart
2113 _updateBoundaries: function(chart)
2115 this._minimumBoundaries = 0;
2116 this._maximumBoundaries = chart._profileSamples.totalTime;
2117 this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
2121 * @param {number} time
2124 computePosition: function(time)
2126 return (time - this._minimumBoundaries) * this._xScaleFactor;
2130 * @param {number} value
2131 * @param {number=} precision
2134 formatTime: function(value, precision)
2136 return Number.secondsToString(value / 1000, !!precision);
2142 maximumBoundary: function()
2144 return this._maximumBoundaries;
2150 minimumBoundary: function()
2152 return this._minimumBoundaries;
2158 zeroTime: function()
2160 return this._minimumBoundaries;
2166 boundarySpan: function()
2168 return this._maximumBoundaries - this._minimumBoundaries;
2175 * @extends {WebInspector.VBox}
2177 WebInspector.HeapSnapshotStatisticsView = function()
2179 WebInspector.VBox.call(this);
2180 this.setMinimumSize(50, 25);
2181 this._pieChart = new WebInspector.PieChart();
2182 this._pieChart.setSize(150);
2183 this.element.appendChild(this._pieChart.element);
2184 this._labels = this.element.createChild("div", "heap-snapshot-stats-legend");
2187 WebInspector.HeapSnapshotStatisticsView.prototype = {
2189 * @param {number} value
2191 setTotal: function(value)
2193 this._pieChart.setTotal(value);
2197 * @param {number} value
2198 * @param {string} name
2199 * @param {string=} color
2201 addRecord: function(value, name, color)
2204 this._pieChart.addSlice(value, color);
2206 var node = this._labels.createChild("div");
2207 var swatchDiv = node.createChild("div", "heap-snapshot-stats-swatch");
2208 var nameDiv = node.createChild("div", "heap-snapshot-stats-name");
2209 var sizeDiv = node.createChild("div", "heap-snapshot-stats-size");
2211 swatchDiv.style.backgroundColor = color;
2213 swatchDiv.classList.add("heap-snapshot-stats-empty-swatch");
2214 nameDiv.textContent = name;
2215 sizeDiv.textContent = WebInspector.UIString("%s KB", Number.withThousandsSeparator(Math.round(value / 1024)));
2218 __proto__: WebInspector.VBox.prototype
2223 * @extends {WebInspector.View}
2225 WebInspector.HeapAllocationStackView = function()
2227 WebInspector.View.call(this);
2228 this._target = /** @type {!WebInspector.Target} */ (WebInspector.targetManager.activeTarget());
2229 this._linkifier = new WebInspector.Linkifier();
2232 WebInspector.HeapAllocationStackView.prototype = {
2234 * @param {!WebInspector.HeapSnapshotProxy} snapshot
2235 * @param {number} snapshotNodeIndex
2237 setAllocatedObject: function(snapshot, snapshotNodeIndex)
2240 snapshot.allocationStack(snapshotNodeIndex, this._didReceiveAllocationStack.bind(this));
2245 this.element.removeChildren();
2246 this._linkifier.reset();
2250 * @param {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} frames
2252 _didReceiveAllocationStack: function(frames)
2255 var stackDiv = this.element.createChild("div", "no-heap-allocation-stack");
2256 stackDiv.createTextChild(WebInspector.UIString("Stack was not recorded for this object because it had been allocated before this profile recording started."));
2260 var stackDiv = this.element.createChild("div", "heap-allocation-stack");
2261 for (var i = 0; i < frames.length; i++) {
2262 var frame = frames[i];
2263 var frameDiv = stackDiv.createChild("div", "stack-frame");
2264 var name = frameDiv.createChild("div");
2265 name.textContent = frame.functionName;
2266 if (frame.scriptId) {
2268 var rawLocation = new WebInspector.DebuggerModel.Location(this._target, String(frame.scriptId), frame.line - 1, frame.column - 1);
2270 urlElement = this._linkifier.linkifyRawLocation(rawLocation);
2272 urlElement = this._linkifier.linkifyLocation(this._target, frame.scriptName, frame.line - 1, frame.column - 1);
2273 frameDiv.appendChild(urlElement);
2278 __proto__: WebInspector.View.prototype