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 if (profile._hasAllocationStacks) {
77 this._allocationView = new WebInspector.VBox();
78 this._allocationView.setMinimumSize(50, 25);
79 this._allocationDataGrid = new WebInspector.AllocationDataGrid(profile.weakTarget() , dataDisplayDelegate);
80 this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
81 this._allocationDataGrid.show(this._allocationView.element);
83 this._allocationStackView = new WebInspector.HeapAllocationStackView(profile.weakTarget());
84 this._allocationStackView.setMinimumSize(50, 25);
86 this._tabbedPane = new WebInspector.TabbedPane();
87 this._tabbedPane.closeableTabs = false;
88 this._tabbedPane.headerElement().classList.add("heap-object-details-header");
91 this._retainmentView = new WebInspector.VBox();
92 this._retainmentView.setMinimumSize(50, 21);
93 this._retainmentView.element.classList.add("retaining-paths-view");
96 if (this._allocationStackView) {
97 this._tabbedPane = new WebInspector.TabbedPane();
98 this._tabbedPane.closeableTabs = false;
99 this._tabbedPane.headerElement().classList.add("heap-object-details-header");
101 this._tabbedPane.appendTab("retainers", WebInspector.UIString("Retainers"), this._retainmentView);
102 this._tabbedPane.appendTab("allocation-stack", WebInspector.UIString("Allocation stack"), this._allocationStackView);
104 splitViewResizer = this._tabbedPane.headerElement();
105 this._objectDetailsView = this._tabbedPane;
107 var retainmentViewHeader = document.createElementWithClass("div", "heap-snapshot-view-resizer");
108 var retainingPathsTitleDiv = retainmentViewHeader.createChild("div", "title");
109 var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
110 retainingPathsTitle.textContent = WebInspector.UIString("Retainers");
111 this._retainmentView.element.appendChild(retainmentViewHeader);
113 splitViewResizer = retainmentViewHeader;
114 this._objectDetailsView = this._retainmentView;
116 this._splitView.hideDefaultResizer();
117 this._splitView.installResizer(splitViewResizer);
119 this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid(dataDisplayDelegate);
120 this._retainmentDataGrid.show(this._retainmentView.element);
121 this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
122 this._retainmentDataGrid.reset();
124 this._perspectives = [];
125 this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective());
126 if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
127 this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective());
128 this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective());
129 if (this._allocationView)
130 this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective());
131 this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective());
133 this._perspectiveSelect = new WebInspector.StatusBarComboBox(this._onSelectedPerspectiveChanged.bind(this));
134 for (var i = 0; i < this._perspectives.length; ++i)
135 this._perspectiveSelect.createOption(this._perspectives[i].title());
137 this._profile = profile;
139 this._baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
140 this._baseSelect.visible = false;
141 this._updateBaseOptions();
143 this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
144 this._filterSelect.visible = false;
145 this._updateFilterOptions();
147 this._classNameFilter = new WebInspector.StatusBarInput("Class filter");
148 this._classNameFilter.visible = false;
149 this._constructorsDataGrid.setNameFilter(this._classNameFilter);
150 this._diffDataGrid.setNameFilter(this._classNameFilter);
152 this._selectedSizeText = new WebInspector.StatusBarText("");
154 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
156 this._currentPerspectiveIndex = 0;
157 this._currentPerspective = this._perspectives[0];
158 this._currentPerspective.activate(this);
159 this._dataGrid = this._currentPerspective.masterGrid(this);
166 * @param {string} title
168 WebInspector.HeapSnapshotView.Perspective = function(title)
173 WebInspector.HeapSnapshotView.Perspective.prototype = {
175 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
177 activate: function(heapSnapshotView) { },
180 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
182 deactivate: function(heapSnapshotView)
184 heapSnapshotView._baseSelect.visible = false;
185 heapSnapshotView._filterSelect.visible = false;
186 heapSnapshotView._classNameFilter.visible = false;
187 if (heapSnapshotView._trackingOverviewGrid)
188 heapSnapshotView._trackingOverviewGrid.detach();
189 if (heapSnapshotView._allocationView)
190 heapSnapshotView._allocationView.detach();
191 if (heapSnapshotView._statisticsView)
192 heapSnapshotView._statisticsView.detach();
194 heapSnapshotView._splitView.detach();
195 heapSnapshotView._splitView.detachChildViews();
199 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
200 * @return {?WebInspector.DataGrid}
202 masterGrid: function(heapSnapshotView)
218 supportsSearch: function()
226 * @extends {WebInspector.HeapSnapshotView.Perspective}
228 WebInspector.HeapSnapshotView.SummaryPerspective = function()
230 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Summary"));
233 WebInspector.HeapSnapshotView.SummaryPerspective.prototype = {
236 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
238 activate: function(heapSnapshotView)
240 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
241 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
242 heapSnapshotView._splitView.show(heapSnapshotView.element);
243 heapSnapshotView._filterSelect.visible = true;
244 heapSnapshotView._classNameFilter.visible = true;
245 if (heapSnapshotView._trackingOverviewGrid) {
246 heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView.element, heapSnapshotView._splitView.element);
247 heapSnapshotView._trackingOverviewGrid.update();
248 heapSnapshotView._trackingOverviewGrid._updateGrid();
254 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
255 * @return {?WebInspector.DataGrid}
257 masterGrid: function(heapSnapshotView)
259 return heapSnapshotView._constructorsDataGrid;
266 supportsSearch: function()
271 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
276 * @extends {WebInspector.HeapSnapshotView.Perspective}
278 WebInspector.HeapSnapshotView.ComparisonPerspective = function()
280 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Comparison"));
283 WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = {
286 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
288 activate: function(heapSnapshotView)
290 heapSnapshotView._diffView.show(heapSnapshotView._splitView.mainElement());
291 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
292 heapSnapshotView._splitView.show(heapSnapshotView.element);
293 heapSnapshotView._baseSelect.visible = true;
294 heapSnapshotView._classNameFilter.visible = true;
299 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
300 * @return {?WebInspector.DataGrid}
302 masterGrid: function(heapSnapshotView)
304 return heapSnapshotView._diffDataGrid;
311 supportsSearch: function()
316 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
321 * @extends {WebInspector.HeapSnapshotView.Perspective}
323 WebInspector.HeapSnapshotView.ContainmentPerspective = function()
325 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Containment"));
328 WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = {
331 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
333 activate: function(heapSnapshotView)
335 heapSnapshotView._containmentView.show(heapSnapshotView._splitView.mainElement());
336 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
337 heapSnapshotView._splitView.show(heapSnapshotView.element);
342 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
343 * @return {?WebInspector.DataGrid}
345 masterGrid: function(heapSnapshotView)
347 return heapSnapshotView._containmentDataGrid;
349 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
354 * @extends {WebInspector.HeapSnapshotView.Perspective}
356 WebInspector.HeapSnapshotView.AllocationPerspective = function()
358 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Allocation"));
359 this._allocationSplitView = new WebInspector.SplitView(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
361 var resizer = document.createElementWithClass("div", "heap-snapshot-view-resizer");
362 var title = resizer.createChild("div", "title").createChild("span");
363 title.textContent = WebInspector.UIString("Live objects");
364 this._allocationSplitView.hideDefaultResizer();
365 this._allocationSplitView.installResizer(resizer);
367 this._allocationSplitView.sidebarElement().appendChild(resizer);
370 WebInspector.HeapSnapshotView.AllocationPerspective.prototype = {
373 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
375 activate: function(heapSnapshotView)
377 heapSnapshotView._allocationView.show(this._allocationSplitView.mainElement());
378 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
379 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
380 heapSnapshotView._splitView.show(this._allocationSplitView.sidebarElement());
381 this._allocationSplitView.show(heapSnapshotView.element);
383 heapSnapshotView._constructorsDataGrid.clear();
384 var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode;
386 heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
391 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
393 deactivate: function(heapSnapshotView)
395 this._allocationSplitView.detach();
396 WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView);
401 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
402 * @return {?WebInspector.DataGrid}
404 masterGrid: function(heapSnapshotView)
406 return heapSnapshotView._allocationDataGrid;
409 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
414 * @extends {WebInspector.HeapSnapshotView.Perspective}
416 WebInspector.HeapSnapshotView.StatisticsPerspective = function()
418 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Statistics"));
421 WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = {
424 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
426 activate: function(heapSnapshotView)
428 heapSnapshotView._statisticsView.show(heapSnapshotView.element);
433 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
434 * @return {?WebInspector.DataGrid}
436 masterGrid: function(heapSnapshotView)
441 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
445 WebInspector.HeapSnapshotView.prototype = {
446 _refreshView: function()
448 this._profile.load(profileCallback.bind(this));
451 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
452 * @this {WebInspector.HeapSnapshotView}
454 function profileCallback(heapSnapshotProxy)
456 heapSnapshotProxy.getStatistics(this._gotStatistics.bind(this));
457 var list = this._profiles();
458 var profileIndex = list.indexOf(this._profile);
459 this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
460 this._dataGrid.setDataSource(heapSnapshotProxy);
461 if (this._trackingOverviewGrid)
462 this._trackingOverviewGrid._updateGrid();
467 * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
469 _gotStatistics: function(statistics) {
470 this._statisticsView.setTotal(statistics.total);
471 this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77");
472 this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5");
473 this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af");
474 this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5");
475 this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total"));
478 _onIdsRangeChanged: function(event)
480 var minId = event.data.minId;
481 var maxId = event.data.maxId;
482 this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
483 if (this._constructorsDataGrid.snapshot)
484 this._constructorsDataGrid.setSelectionRange(minId, maxId);
489 var result = [this._perspectiveSelect.element, this._classNameFilter.element];
490 if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
491 result.push(this._baseSelect.element, this._filterSelect.element);
492 result.push(this._selectedSizeText.element);
498 // FIXME: load base and current snapshots in parallel
499 this._profile.load(profileCallback.bind(this));
502 * @this {WebInspector.HeapSnapshotView}
504 function profileCallback() {
505 this._profile._wasShown();
506 if (this._baseProfile)
507 this._baseProfile.load(function() { });
513 this._currentSearchResultIndex = -1;
514 this._popoverHelper.hidePopover();
515 if (this.helpPopover && this.helpPopover.isShowing())
516 this.helpPopover.hide();
519 searchCanceled: function()
521 if (this._searchResults) {
522 for (var i = 0; i < this._searchResults.length; ++i) {
523 var node = this._searchResults[i].node;
524 delete node._searchMatched;
529 delete this._searchFinishedCallback;
530 this._currentSearchResultIndex = -1;
531 this._searchResults = [];
535 * @param {string} query
536 * @param {function(!WebInspector.View, number)} finishedCallback
538 performSearch: function(query, finishedCallback)
540 // Call searchCanceled since it will reset everything we need before doing a new search.
541 this.searchCanceled();
543 query = query.trim();
547 if (!this._currentPerspective.supportsSearch())
551 * @param {boolean} found
552 * @this {WebInspector.HeapSnapshotView}
554 function didHighlight(found)
556 finishedCallback(this, found ? 1 : 0);
559 if (query.charAt(0) === "@") {
560 var snapshotNodeId = parseInt(query.substring(1), 10);
561 if (!isNaN(snapshotNodeId))
562 this._dataGrid.highlightObjectByHeapSnapshotId(String(snapshotNodeId), didHighlight.bind(this));
564 finishedCallback(this, 0);
568 this._searchFinishedCallback = finishedCallback;
569 var nameRegExp = createPlainTextSearchRegex(query, "i");
571 function matchesByName(gridNode) {
572 return ("_name" in gridNode) && nameRegExp.test(gridNode._name);
575 function matchesQuery(gridNode)
577 delete gridNode._searchMatched;
578 if (matchesByName(gridNode)) {
579 gridNode._searchMatched = true;
586 var current = this._dataGrid.rootNode().children[0];
590 // Restrict to type nodes and instances.
594 if (matchesQuery(current))
595 this._searchResults.push({ node: current });
596 current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
597 depth += info.depthChange;
600 finishedCallback(this, this._searchResults.length);
603 jumpToFirstSearchResult: function()
605 if (!this._searchResults || !this._searchResults.length)
607 this._currentSearchResultIndex = 0;
608 this._jumpToSearchResult(this._currentSearchResultIndex);
611 jumpToLastSearchResult: function()
613 if (!this._searchResults || !this._searchResults.length)
615 this._currentSearchResultIndex = (this._searchResults.length - 1);
616 this._jumpToSearchResult(this._currentSearchResultIndex);
619 jumpToNextSearchResult: function()
621 if (!this._searchResults || !this._searchResults.length)
623 if (++this._currentSearchResultIndex >= this._searchResults.length)
624 this._currentSearchResultIndex = 0;
625 this._jumpToSearchResult(this._currentSearchResultIndex);
628 jumpToPreviousSearchResult: function()
630 if (!this._searchResults || !this._searchResults.length)
632 if (--this._currentSearchResultIndex < 0)
633 this._currentSearchResultIndex = (this._searchResults.length - 1);
634 this._jumpToSearchResult(this._currentSearchResultIndex);
640 showingFirstSearchResult: function()
642 return (this._currentSearchResultIndex === 0);
648 showingLastSearchResult: function()
650 return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
656 currentSearchResultIndex: function() {
657 return this._currentSearchResultIndex;
660 _jumpToSearchResult: function(index)
662 var searchResult = this._searchResults[index];
666 var node = searchResult.node;
667 node.revealAndSelect();
670 refreshVisibleData: function()
674 var child = this._dataGrid.rootNode().children[0];
677 child = child.traverseNextNode(false, null, true);
681 _changeBase: function()
683 if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()])
686 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
687 var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid);
688 // Change set base data source only if main data source is already set.
689 if (dataGrid.snapshot)
690 this._baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid));
692 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
695 // The current search needs to be performed again. First negate out previous match
696 // count by calling the search finished callback with a negative number of matches.
697 // Then perform the search again with the same query and callback.
698 this._searchFinishedCallback(this, -this._searchResults.length);
699 this.performSearch(this.currentQuery, this._searchFinishedCallback);
702 _changeFilter: function()
704 var profileIndex = this._filterSelect.selectedIndex() - 1;
705 this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
707 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
708 action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
709 label: this._filterSelect.selectedOption().label
712 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
715 // The current search needs to be performed again. First negate out previous match
716 // count by calling the search finished callback with a negative number of matches.
717 // Then perform the search again with the same query and callback.
718 this._searchFinishedCallback(this, -this._searchResults.length);
719 this.performSearch(this.currentQuery, this._searchFinishedCallback);
723 * @return {!Array.<!WebInspector.ProfileHeader>}
725 _profiles: function()
727 return this._profile.profileType().getProfiles();
731 * @param {!WebInspector.ContextMenu} contextMenu
732 * @param {!Event} event
734 populateContextMenu: function(contextMenu, event)
737 this._dataGrid.populateContextMenu(contextMenu, event);
740 _selectionChanged: function(event)
742 var selectedNode = event.target.selectedNode;
743 this._setSelectedNodeForDetailsView(selectedNode);
744 this._inspectedObjectChanged(event);
747 _onSelectAllocationNode: function(event)
749 var selectedNode = event.target.selectedNode;
750 this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
751 this._setSelectedNodeForDetailsView(null);
754 _inspectedObjectChanged: function(event)
756 var selectedNode = event.target.selectedNode;
757 var target = this._profile.target();
758 if (!target && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
759 target.consoleAgent().addInspectedHeapObject(selectedNode.snapshotNodeId);
763 * @param {?WebInspector.HeapSnapshotGridNode} nodeItem
765 _setSelectedNodeForDetailsView: function(nodeItem)
767 var dataSource = nodeItem && nodeItem.retainersDataSource();
769 this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
770 if (this._allocationStackView)
771 this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex)
773 if (this._allocationStackView)
774 this._allocationStackView.clear();
775 this._retainmentDataGrid.reset();
780 * @param {string} perspectiveTitle
781 * @param {function()} callback
783 _changePerspectiveAndWait: function(perspectiveTitle, callback)
785 var perspectiveIndex = null;
786 for (var i = 0; i < this._perspectives.length; ++i) {
787 if (this._perspectives[i].title() === perspectiveTitle) {
788 perspectiveIndex = i;
792 if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) {
793 setTimeout(callback, 0);
798 * @this {WebInspector.HeapSnapshotView}
800 function dataGridContentShown(event)
802 var dataGrid = event.data;
803 dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
804 if (dataGrid === this._dataGrid)
807 this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
809 this._perspectiveSelect.setSelectedIndex(perspectiveIndex);
810 this._changePerspective(perspectiveIndex);
813 _updateDataSourceAndView: function()
815 var dataGrid = this._dataGrid;
816 if (!dataGrid || dataGrid.snapshot)
819 this._profile.load(didLoadSnapshot.bind(this));
822 * @this {WebInspector.HeapSnapshotView}
824 function didLoadSnapshot(snapshotProxy)
826 if (this._dataGrid !== dataGrid)
828 if (dataGrid.snapshot !== snapshotProxy)
829 dataGrid.setDataSource(snapshotProxy);
830 if (dataGrid === this._diffDataGrid) {
831 if (!this._baseProfile)
832 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
833 this._baseProfile.load(didLoadBaseSnaphot.bind(this));
838 * @this {WebInspector.HeapSnapshotView}
840 function didLoadBaseSnaphot(baseSnapshotProxy)
842 if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy)
843 this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
847 _onSelectedPerspectiveChanged: function(event)
849 this._changePerspective(event.target.selectedIndex);
850 // FIXME: This is needed by CodeSchool extension.
851 this._onSelectedViewChanged(event);
854 _onSelectedViewChanged: function(event)
858 _changePerspective: function(selectedIndex)
860 if (selectedIndex === this._currentPerspectiveIndex)
863 this._currentPerspectiveIndex = selectedIndex;
865 this._currentPerspective.deactivate(this);
866 var perspective = this._perspectives[selectedIndex];
867 this._currentPerspective = perspective;
868 this._dataGrid = perspective.masterGrid(this);
869 perspective.activate(this);
871 this.refreshVisibleData();
873 this._dataGrid.updateWidths();
875 this._updateDataSourceAndView();
877 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
880 // The current search needs to be performed again. First negate out previous match
881 // count by calling the search finished callback with a negative number of matches.
882 // Then perform the search again the with same query and callback.
883 this._searchFinishedCallback(this, -this._searchResults.length);
884 this.performSearch(this.currentQuery, this._searchFinishedCallback);
888 * @param {string} perspectiveName
889 * @param {number} snapshotObjectId
891 highlightLiveObject: function(perspectiveName, snapshotObjectId)
893 this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this));
896 * @this {WebInspector.HeapSnapshotView}
898 function didChangePerspective()
900 this._dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId, didHighlightObject);
903 function didHighlightObject(found)
906 WebInspector.console.error("Cannot find corresponding heap snapshot node");
910 _getHoverAnchor: function(target)
912 var span = target.enclosingNodeOrSelfWithNodeName("span");
915 var row = target.enclosingNodeOrSelfWithNodeName("tr");
918 span.node = row._dataGridNode;
922 _resolveObjectForPopover: function(element, showCallback, objectGroupName)
924 if (!this._profile.target())
926 element.node.queryObjectContent(this._profile.target(), showCallback, objectGroupName);
929 _updateBaseOptions: function()
931 var list = this._profiles();
932 // We're assuming that snapshots can only be added.
933 if (this._baseSelect.size() === list.length)
936 for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) {
937 var title = list[i].title;
938 this._baseSelect.createOption(title);
942 _updateFilterOptions: function()
944 var list = this._profiles();
945 // We're assuming that snapshots can only be added.
946 if (this._filterSelect.size() - 1 === list.length)
949 if (!this._filterSelect.size())
950 this._filterSelect.createOption(WebInspector.UIString("All objects"));
952 for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
953 var title = list[i].title;
955 title = WebInspector.UIString("Objects allocated before %s", title);
957 title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
958 this._filterSelect.createOption(title);
962 _updateControls: function()
964 this._updateBaseOptions();
965 this._updateFilterOptions();
969 * @param {!WebInspector.Event} event
971 _onReceiveSnapshot: function(event)
973 this._updateControls();
977 * @param {!WebInspector.Event} event
979 _onProfileHeaderRemoved: function(event)
981 var profile = event.data;
982 if (this._profile === profile) {
984 this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
985 this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
988 this._updateControls();
994 if (this._allocationStackView) {
995 this._allocationStackView.clear();
996 this._allocationDataGrid.dispose();
1000 __proto__: WebInspector.VBox.prototype
1005 * @extends {WebInspector.ProfileType}
1006 * @implements {WebInspector.TargetManager.Observer}
1007 * @param {string=} id
1008 * @param {string=} title
1010 WebInspector.HeapSnapshotProfileType = function(id, title)
1012 WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
1013 WebInspector.targetManager.observeTargets(this);
1014 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ResetProfiles, this._resetProfiles, this);
1015 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.AddHeapSnapshotChunk, this._addHeapSnapshotChunk, this);
1016 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ReportHeapSnapshotProgress, this._reportHeapSnapshotProgress, this);
1019 WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
1020 WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
1022 WebInspector.HeapSnapshotProfileType.prototype = {
1024 * @param {!WebInspector.Target} target
1026 targetAdded: function(target)
1028 target.heapProfilerModel.enable();
1032 * @param {!WebInspector.Target} target
1034 targetRemoved: function(target)
1042 fileExtension: function()
1044 return ".heapsnapshot";
1049 return WebInspector.UIString("Take heap snapshot.");
1056 isInstantProfile: function()
1065 buttonClicked: function()
1067 this._takeHeapSnapshot(function() {});
1068 WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
1074 return WebInspector.UIString("HEAP SNAPSHOTS");
1079 return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
1084 * @param {string} title
1085 * @return {!WebInspector.ProfileHeader}
1087 createProfileLoadedFromFile: function(title)
1089 return new WebInspector.HeapProfileHeader(null, this, title);
1092 _takeHeapSnapshot: function(callback)
1094 if (this.profileBeingRecorded())
1096 var target = /** @type {!WebInspector.Target} */ (WebInspector.context.flavor(WebInspector.Target));
1097 var profile = new WebInspector.HeapProfileHeader(target, this);
1098 this.setProfileBeingRecorded(profile);
1099 this.addProfile(profile);
1100 profile.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1103 * @param {?string} error
1104 * @this {WebInspector.HeapSnapshotProfileType}
1106 function didTakeHeapSnapshot(error)
1108 var profile = this._profileBeingRecorded;
1109 profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
1110 profile._finishLoad();
1111 this.setProfileBeingRecorded(null);
1112 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
1115 target.heapProfilerAgent().takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
1119 * @param {!WebInspector.Event} event
1121 _addHeapSnapshotChunk: function(event)
1123 if (!this.profileBeingRecorded())
1125 var chunk = /** @type {string} */(event.data);
1126 this.profileBeingRecorded().transferChunk(chunk);
1130 * @param {!WebInspector.Event} event
1132 _reportHeapSnapshotProgress: function(event)
1134 var profile = this.profileBeingRecorded();
1137 var data = /** @type {{done: number, total: number, finished: boolean}} */ (event.data);
1138 profile.updateStatus(WebInspector.UIString("%.0f%", (data.done / data.total) * 100), true);
1140 profile._prepareToLoad();
1143 _resetProfiles: function()
1148 _snapshotReceived: function(profile)
1150 if (this._profileBeingRecorded === profile)
1151 this.setProfileBeingRecorded(null);
1152 this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
1155 __proto__: WebInspector.ProfileType.prototype
1161 * @extends {WebInspector.HeapSnapshotProfileType}
1163 WebInspector.TrackingHeapSnapshotProfileType = function()
1165 WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
1168 WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
1170 WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
1171 WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
1172 WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
1174 WebInspector.TrackingHeapSnapshotProfileType.prototype = {
1177 * @param {!WebInspector.Target} target
1179 targetAdded: function(target)
1181 WebInspector.HeapSnapshotProfileType.prototype.targetAdded.call(this, target);
1182 target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
1183 target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
1187 * @param {!WebInspector.Target} target
1189 targetRemoved: function(target)
1191 WebInspector.HeapSnapshotProfileType.prototype.targetRemoved.call(this, target);
1192 target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
1193 target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
1197 * @param {!WebInspector.Event} event
1199 _heapStatsUpdate: function(event)
1201 if (!this._profileSamples)
1203 var samples = /** @type {!Array.<number>} */ (event.data);
1205 for (var i = 0; i < samples.length; i += 3) {
1207 var count = samples[i+1];
1208 var size = samples[i+2];
1209 this._profileSamples.sizes[index] = size;
1210 if (!this._profileSamples.max[index])
1211 this._profileSamples.max[index] = size;
1216 * @param {!WebInspector.Event} event
1218 _lastSeenObjectId: function(event)
1220 var profileSamples = this._profileSamples;
1221 if (!profileSamples)
1223 var data = /** @type {{lastSeenObjectId: number, timestamp: number}} */ (event.data);
1224 var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
1225 profileSamples.ids[currentIndex] = data.lastSeenObjectId;
1226 if (!profileSamples.max[currentIndex]) {
1227 profileSamples.max[currentIndex] = 0;
1228 profileSamples.sizes[currentIndex] = 0;
1230 profileSamples.timestamps[currentIndex] = data.timestamp;
1231 if (profileSamples.totalTime < data.timestamp - profileSamples.timestamps[0])
1232 profileSamples.totalTime *= 2;
1233 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
1234 this._profileBeingRecorded.updateStatus(null, true);
1241 hasTemporaryView: function()
1248 return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
1255 isInstantProfile: function()
1264 buttonClicked: function()
1266 return this._toggleRecording();
1269 _startRecordingProfile: function()
1271 if (this.profileBeingRecorded())
1273 var recordAllocationStacks = WebInspector.settings.recordAllocationStacks.get();
1274 this._addNewProfile(recordAllocationStacks);
1275 this.profileBeingRecorded().target().heapProfilerAgent().startTrackingHeapObjects(recordAllocationStacks);
1279 * @param {boolean} withAllocationStacks
1281 _addNewProfile: function(withAllocationStacks)
1283 var target = WebInspector.context.flavor(WebInspector.Target);
1284 this.setProfileBeingRecorded(new WebInspector.HeapProfileHeader(target, this, undefined, withAllocationStacks));
1285 this._lastSeenIndex = -1;
1286 this._profileSamples = {
1293 this._profileBeingRecorded._profileSamples = this._profileSamples;
1294 this._recording = true;
1295 this.addProfile(this._profileBeingRecorded);
1296 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
1297 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
1300 _stopRecordingProfile: function()
1302 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1304 * @param {?string} error
1305 * @this {WebInspector.HeapSnapshotProfileType}
1307 function didTakeHeapSnapshot(error)
1309 var profile = this.profileBeingRecorded();
1312 profile._finishLoad();
1313 this._profileSamples = null;
1314 this.setProfileBeingRecorded(null);
1315 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
1318 this._profileBeingRecorded.target().heapProfilerAgent().stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
1319 this._recording = false;
1320 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
1323 _toggleRecording: function()
1325 if (this._recording)
1326 this._stopRecordingProfile();
1328 this._startRecordingProfile();
1329 return this._recording;
1334 return WebInspector.UIString("HEAP TIMELINES");
1339 return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
1345 resetProfiles: function()
1347 var wasRecording = this._recording;
1348 var recordingAllocationStacks = wasRecording && this.profileBeingRecorded()._hasAllocationStacks;
1349 // Clear current profile to avoid stopping backend.
1350 this.setProfileBeingRecorded(null);
1351 WebInspector.HeapSnapshotProfileType.prototype.resetProfiles.call(this);
1352 this._profileSamples = null;
1353 this._lastSeenIndex = -1;
1355 this._addNewProfile(recordingAllocationStacks);
1361 profileBeingRecordedRemoved: function()
1363 this._stopRecordingProfile();
1364 this._profileSamples = null;
1367 __proto__: WebInspector.HeapSnapshotProfileType.prototype
1372 * @extends {WebInspector.ProfileHeader}
1373 * @param {?WebInspector.Target} target
1374 * @param {!WebInspector.HeapSnapshotProfileType} type
1375 * @param {string=} title
1376 * @param {boolean=} hasAllocationStacks
1378 WebInspector.HeapProfileHeader = function(target, type, title, hasAllocationStacks)
1380 WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Snapshot %d", type.nextProfileUid()));
1381 this._hasAllocationStacks = !!hasAllocationStacks;
1382 this.maxJSObjectId = -1;
1384 * @type {?WebInspector.HeapSnapshotWorkerProxy}
1386 this._workerProxy = null;
1388 * @type {?WebInspector.OutputStream}
1390 this._receiver = null;
1392 * @type {?WebInspector.HeapSnapshotProxy}
1394 this._snapshotProxy = null;
1396 * @type {?Array.<function(!WebInspector.HeapSnapshotProxy)>}
1398 this._loadCallbacks = [];
1399 this._totalNumberOfChunks = 0;
1400 this._bufferedWriter = null;
1403 WebInspector.HeapProfileHeader.prototype = {
1406 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1407 * @return {!WebInspector.ProfileSidebarTreeElement}
1409 createSidebarTreeElement: function(dataDisplayDelegate)
1411 return new WebInspector.ProfileSidebarTreeElement(dataDisplayDelegate, this, "heap-snapshot-sidebar-tree-item");
1416 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1417 * @return {!WebInspector.HeapSnapshotView}
1419 createView: function(dataDisplayDelegate)
1421 return new WebInspector.HeapSnapshotView(dataDisplayDelegate, this);
1426 * @param {function(!WebInspector.HeapSnapshotProxy):void} callback
1428 load: function(callback)
1430 if (this.uid === -1)
1432 if (this._snapshotProxy) {
1433 callback(this._snapshotProxy);
1436 this._loadCallbacks.push(callback);
1439 _prepareToLoad: function()
1441 console.assert(!this._receiver, "Already loading");
1442 this._setupWorker();
1443 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1446 _finishLoad: function()
1448 if (!this._wasDisposed)
1449 this._receiver.close(function() {});
1450 if (this._bufferedWriter) {
1451 this._bufferedWriter.close(this._didWriteToTempFile.bind(this));
1452 this._bufferedWriter = null;
1456 _didWriteToTempFile: function(tempFile)
1458 if (this._wasDisposed) {
1463 this._tempFile = tempFile;
1465 this._failedToCreateTempFile = true;
1466 if (this._onTempFileReady) {
1467 this._onTempFileReady();
1468 this._onTempFileReady = null;
1472 _setupWorker: function()
1475 * @this {WebInspector.HeapProfileHeader}
1477 function setProfileWait(event)
1479 this.updateStatus(null, event.data);
1481 console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists");
1482 this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this));
1483 this._workerProxy.addEventListener("wait", setProfileWait, this);
1484 this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this));
1488 * @param {string} eventName
1491 _handleWorkerEvent: function(eventName, data)
1493 if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName)
1495 var subtitle = /** @type {string} */ (data);
1496 this.updateStatus(subtitle);
1504 if (this._workerProxy)
1505 this._workerProxy.dispose();
1506 this.removeTempFile();
1507 this._wasDisposed = true;
1510 _didCompleteSnapshotTransfer: function()
1512 if (!this._snapshotProxy)
1514 this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false);
1518 * @param {string} chunk
1520 transferChunk: function(chunk)
1522 if (!this._bufferedWriter)
1523 this._bufferedWriter = new WebInspector.BufferedTempFileWriter("heap-profiler", this.uid);
1524 this._bufferedWriter.write(chunk);
1526 ++this._totalNumberOfChunks;
1527 this._receiver.write(chunk, function() {});
1530 _snapshotReceived: function(snapshotProxy)
1532 if (this._wasDisposed)
1534 this._receiver = null;
1535 this._snapshotProxy = snapshotProxy;
1536 this.maxJSObjectId = snapshotProxy.maxJSObjectId();
1537 this._didCompleteSnapshotTransfer();
1538 this._workerProxy.startCheckingForLongRunningCalls();
1539 this.notifySnapshotReceived();
1542 notifySnapshotReceived: function()
1544 for (var i = 0; i < this._loadCallbacks.length; i++)
1545 this._loadCallbacks[i](this._snapshotProxy);
1546 this._loadCallbacks = null;
1547 this._profileType._snapshotReceived(this);
1548 if (this.canSaveToFile())
1549 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
1552 // Hook point for tests.
1553 _wasShown: function()
1561 canSaveToFile: function()
1563 return !this.fromFile() && this._snapshotProxy;
1569 saveToFile: function()
1571 var fileOutputStream = new WebInspector.FileOutputStream();
1574 * @param {boolean} accepted
1575 * @this {WebInspector.HeapProfileHeader}
1577 function onOpen(accepted)
1581 if (this._failedToCreateTempFile) {
1582 WebInspector.console.error("Failed to open temp file with heap snapshot");
1583 fileOutputStream.close();
1584 } else if (this._tempFile) {
1585 var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this);
1586 this._tempFile.writeToOutputSteam(fileOutputStream, delegate);
1588 this._onTempFileReady = onOpen.bind(this, accepted);
1589 this._updateSaveProgress(0, 1);
1592 this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
1593 fileOutputStream.open(this._fileName, onOpen.bind(this));
1596 _updateSaveProgress: function(value, total)
1598 var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
1599 this.updateStatus(WebInspector.UIString("Saving\u2026 %d\%", percentValue));
1604 * @param {!File} file
1606 loadFromFile: function(file)
1608 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1609 this._setupWorker();
1610 var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
1611 var fileReader = this._createFileReader(file, delegate);
1612 fileReader.start(this._receiver);
1615 _createFileReader: function(file, delegate)
1617 return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
1620 __proto__: WebInspector.ProfileHeader.prototype
1625 * @implements {WebInspector.OutputStreamDelegate}
1627 WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
1629 this._snapshotHeader = snapshotHeader;
1632 WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
1633 onTransferStarted: function()
1638 * @param {!WebInspector.ChunkedReader} reader
1640 onChunkTransferred: function(reader)
1644 onTransferFinished: function()
1649 * @param {!WebInspector.ChunkedReader} reader
1652 onError: function (reader, e)
1655 switch(e.target.error.code) {
1656 case e.target.error.NOT_FOUND_ERR:
1657 subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
1659 case e.target.error.NOT_READABLE_ERR:
1660 subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
1662 case e.target.error.ABORT_ERR:
1665 subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
1667 this._snapshotHeader.updateStatus(subtitle);
1673 * @implements {WebInspector.OutputStreamDelegate}
1674 * @param {!WebInspector.HeapProfileHeader} profileHeader
1676 WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
1678 this._profileHeader = profileHeader;
1681 WebInspector.SaveSnapshotOutputStreamDelegate.prototype = {
1682 onTransferStarted: function()
1684 this._profileHeader._updateSaveProgress(0, 1);
1687 onTransferFinished: function()
1689 this._profileHeader._didCompleteSnapshotTransfer();
1693 * @param {!WebInspector.ChunkedReader} reader
1695 onChunkTransferred: function(reader)
1697 this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize());
1701 * @param {!WebInspector.ChunkedReader} reader
1702 * @param {!Event} event
1704 onError: function(reader, event)
1706 WebInspector.console.error("Failed to read heap snapshot from temp file: " + /** @type {!ErrorEvent} */ (event).message);
1707 this.onTransferFinished();
1713 * @extends {WebInspector.VBox}
1714 * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
1716 WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
1718 WebInspector.VBox.call(this);
1719 this.registerRequiredCSS("flameChart.css");
1720 this.element.id = "heap-recording-view";
1721 this.element.classList.add("heap-tracking-overview");
1723 this._overviewContainer = this.element.createChild("div", "overview-container");
1724 this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
1725 this._overviewGrid.element.classList.add("fill");
1727 this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
1728 this._overviewContainer.appendChild(this._overviewGrid.element);
1729 this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
1730 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
1732 this._profileSamples = heapProfileHeader._profileSamples;
1733 if (heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) {
1734 this._profileType = heapProfileHeader.profileType();
1735 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1736 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1738 var timestamps = this._profileSamples.timestamps;
1739 var totalTime = this._profileSamples.totalTime;
1740 this._windowLeft = 0.0;
1741 this._windowRight = totalTime && timestamps.length ? (timestamps[timestamps.length - 1] - timestamps[0]) / totalTime : 1.0;
1742 this._overviewGrid.setWindow(this._windowLeft, this._windowRight);
1743 this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1744 this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1747 WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
1749 WebInspector.HeapTrackingOverviewGrid.prototype = {
1750 _onStopTracking: function(event)
1752 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1753 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1756 _onHeapStatsUpdate: function(event)
1758 this._profileSamples = event.data;
1759 this._scheduleUpdate();
1763 * @param {number} width
1764 * @param {number} height
1766 _drawOverviewCanvas: function(width, height)
1768 if (!this._profileSamples)
1770 var profileSamples = this._profileSamples;
1771 var sizes = profileSamples.sizes;
1772 var topSizes = profileSamples.max;
1773 var timestamps = profileSamples.timestamps;
1774 var startTime = timestamps[0];
1775 var endTime = timestamps[timestamps.length - 1];
1777 var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime);
1780 * @param {!Array.<number>} sizes
1781 * @param {function(number, number):void} callback
1783 function aggregateAndCall(sizes, callback)
1787 for (var i = 1; i < timestamps.length; ++i) {
1788 var x = Math.floor((timestamps[i] - startTime) * scaleFactor);
1789 if (x !== currentX) {
1791 callback(currentX, size);
1797 callback(currentX, size);
1802 * @param {number} size
1804 function maxSizeCallback(x, size)
1806 maxSize = Math.max(maxSize, size);
1809 aggregateAndCall(sizes, maxSizeCallback);
1811 var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0);
1813 this._overviewCanvas.width = width * window.devicePixelRatio;
1814 this._overviewCanvas.height = height * window.devicePixelRatio;
1815 this._overviewCanvas.style.width = width + "px";
1816 this._overviewCanvas.style.height = height + "px";
1818 var context = this._overviewCanvas.getContext("2d");
1819 context.scale(window.devicePixelRatio, window.devicePixelRatio);
1821 context.beginPath();
1822 context.lineWidth = 2;
1823 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1824 var currentX = (endTime - startTime) * scaleFactor;
1825 context.moveTo(currentX, height - 1);
1826 context.lineTo(currentX, 0);
1828 context.closePath();
1832 var gridLabelHeight = 14;
1834 const maxGridValue = (height - gridLabelHeight) / yScaleFactor;
1835 // The round value calculation is a bit tricky, because
1836 // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
1837 // e.g. a round value 10KB is 10240 bytes.
1838 gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024)));
1839 gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10));
1840 if (gridValue * 5 <= maxGridValue)
1842 gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5;
1843 context.beginPath();
1844 context.lineWidth = 1;
1845 context.strokeStyle = "rgba(0, 0, 0, 0.2)";
1846 context.moveTo(0, gridY);
1847 context.lineTo(width, gridY);
1849 context.closePath();
1854 * @param {number} size
1856 function drawBarCallback(x, size)
1858 context.moveTo(x, height - 1);
1859 context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
1862 context.beginPath();
1863 context.lineWidth = 2;
1864 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1865 aggregateAndCall(topSizes, drawBarCallback);
1867 context.closePath();
1869 context.beginPath();
1870 context.lineWidth = 2;
1871 context.strokeStyle = "rgba(0, 0, 192, 0.8)";
1872 aggregateAndCall(sizes, drawBarCallback);
1874 context.closePath();
1877 var label = Number.bytesToString(gridValue);
1878 var labelPadding = 4;
1880 var labelY = gridY - 0.5;
1881 var labelWidth = 2 * labelPadding + context.measureText(label).width;
1882 context.beginPath();
1883 context.textBaseline = "bottom";
1884 context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
1885 context.fillStyle = "rgba(255, 255, 255, 0.75)";
1886 context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight);
1887 context.fillStyle = "rgb(64, 64, 64)";
1888 context.fillText(label, labelX + labelPadding, labelY);
1890 context.closePath();
1894 onResize: function()
1896 this._updateOverviewCanvas = true;
1897 this._scheduleUpdate();
1900 _onWindowChanged: function()
1902 if (!this._updateGridTimerId)
1903 this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
1906 _scheduleUpdate: function()
1908 if (this._updateTimerId)
1910 this._updateTimerId = setTimeout(this.update.bind(this), 10);
1913 _updateBoundaries: function()
1915 this._windowLeft = this._overviewGrid.windowLeft();
1916 this._windowRight = this._overviewGrid.windowRight();
1917 this._windowWidth = this._windowRight - this._windowLeft;
1922 this._updateTimerId = null;
1923 if (!this.isShowing())
1925 this._updateBoundaries();
1926 this._overviewCalculator._updateBoundaries(this);
1927 this._overviewGrid.updateDividers(this._overviewCalculator);
1928 this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
1931 _updateGrid: function()
1933 this._updateGridTimerId = 0;
1934 this._updateBoundaries();
1935 var ids = this._profileSamples.ids;
1936 var timestamps = this._profileSamples.timestamps;
1937 var sizes = this._profileSamples.sizes;
1938 var startTime = timestamps[0];
1939 var totalTime = this._profileSamples.totalTime;
1940 var timeLeft = startTime + totalTime * this._windowLeft;
1941 var timeRight = startTime + totalTime * this._windowRight;
1943 var maxId = ids[ids.length - 1] + 1;
1945 for (var i = 0; i < timestamps.length; ++i) {
1948 if (timestamps[i] > timeRight)
1951 if (timestamps[i] < timeLeft) {
1958 this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
1961 __proto__: WebInspector.VBox.prototype
1968 WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
1970 this._lastUpdate = 0;
1971 this._currentScale = 0.0;
1974 WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = {
1976 * @param {number} target
1979 nextScale: function(target) {
1980 target = target || this._currentScale;
1981 if (this._currentScale) {
1982 var now = Date.now();
1983 var timeDeltaMs = now - this._lastUpdate;
1984 this._lastUpdate = now;
1985 var maxChangePerSec = 20;
1986 var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000);
1987 var scaleChange = target / this._currentScale;
1988 this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta);
1990 this._currentScale = target;
1991 return this._currentScale;
1998 * @implements {WebInspector.TimelineGrid.Calculator}
2000 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
2004 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
2008 paddingLeft: function()
2014 * @param {!WebInspector.HeapTrackingOverviewGrid} chart
2016 _updateBoundaries: function(chart)
2018 this._minimumBoundaries = 0;
2019 this._maximumBoundaries = chart._profileSamples.totalTime;
2020 this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
2024 * @param {number} time
2027 computePosition: function(time)
2029 return (time - this._minimumBoundaries) * this._xScaleFactor;
2033 * @param {number} value
2034 * @param {number=} precision
2037 formatTime: function(value, precision)
2039 return Number.secondsToString(value / 1000, !!precision);
2045 maximumBoundary: function()
2047 return this._maximumBoundaries;
2053 minimumBoundary: function()
2055 return this._minimumBoundaries;
2061 zeroTime: function()
2063 return this._minimumBoundaries;
2069 boundarySpan: function()
2071 return this._maximumBoundaries - this._minimumBoundaries;
2078 * @extends {WebInspector.VBox}
2080 WebInspector.HeapSnapshotStatisticsView = function()
2082 WebInspector.VBox.call(this);
2083 this.setMinimumSize(50, 25);
2084 this._pieChart = new WebInspector.PieChart(150);
2085 this.element.appendChild(this._pieChart.element);
2086 this._labels = this.element.createChild("div", "heap-snapshot-stats-legend");
2089 WebInspector.HeapSnapshotStatisticsView.prototype = {
2091 * @param {number} value
2093 setTotal: function(value)
2095 this._pieChart.setTotal(value);
2099 * @param {number} value
2100 * @param {string} name
2101 * @param {string=} color
2103 addRecord: function(value, name, color)
2106 this._pieChart.addSlice(value, color);
2108 var node = this._labels.createChild("div");
2109 var swatchDiv = node.createChild("div", "heap-snapshot-stats-swatch");
2110 var nameDiv = node.createChild("div", "heap-snapshot-stats-name");
2111 var sizeDiv = node.createChild("div", "heap-snapshot-stats-size");
2113 swatchDiv.style.backgroundColor = color;
2115 swatchDiv.classList.add("heap-snapshot-stats-empty-swatch");
2116 nameDiv.textContent = name;
2117 sizeDiv.textContent = WebInspector.UIString("%s KB", Number.withThousandsSeparator(Math.round(value / 1024)));
2120 __proto__: WebInspector.VBox.prototype
2125 * @extends {WebInspector.View}
2126 * @param {!WeakReference.<!WebInspector.Target>} weakTarget
2128 WebInspector.HeapAllocationStackView = function(weakTarget)
2130 WebInspector.View.call(this);
2131 this._weakTarget = weakTarget;;
2132 this._linkifier = new WebInspector.Linkifier();
2135 WebInspector.HeapAllocationStackView.prototype = {
2137 * @param {!WebInspector.HeapSnapshotProxy} snapshot
2138 * @param {number} snapshotNodeIndex
2140 setAllocatedObject: function(snapshot, snapshotNodeIndex)
2143 snapshot.allocationStack(snapshotNodeIndex, this._didReceiveAllocationStack.bind(this));
2148 this.element.removeChildren();
2149 this._linkifier.reset();
2153 * @param {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} frames
2155 _didReceiveAllocationStack: function(frames)
2158 var stackDiv = this.element.createChild("div", "no-heap-allocation-stack");
2159 stackDiv.createTextChild(WebInspector.UIString("Stack was not recorded for this object because it had been allocated before this profile recording started."));
2163 var stackDiv = this.element.createChild("div", "heap-allocation-stack");
2164 for (var i = 0; i < frames.length; i++) {
2165 var frame = frames[i];
2166 var frameDiv = stackDiv.createChild("div", "stack-frame");
2167 var name = frameDiv.createChild("div");
2168 name.textContent = frame.functionName;
2169 if (frame.scriptId) {
2170 var urlElement = this._linkifier.linkifyScriptLocation(this._weakTarget.get(), String(frame.scriptId), frame.scriptName, frame.line - 1, frame.column - 1);
2171 frameDiv.appendChild(urlElement);
2176 __proto__: WebInspector.View.prototype