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 * @implements {WebInspector.ProfileType.DataDisplayDelegate}
34 * @extends {WebInspector.VBox}
35 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
36 * @param {!WebInspector.HeapProfileHeader} profile
38 WebInspector.HeapSnapshotView = function(dataDisplayDelegate, profile)
40 WebInspector.VBox.call(this);
42 this.element.classList.add("heap-snapshot-view");
44 profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
45 profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
47 if (profile.profileType().id === WebInspector.TrackingHeapSnapshotProfileType.TypeId) {
48 this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
49 this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
52 this._parentDataDisplayDelegate = dataDisplayDelegate;
54 this._splitView = new WebInspector.SplitView(false, true, "heapSnapshotSplitViewState", 200, 200);
55 this._splitView.show(this.element);
57 this._containmentView = new WebInspector.VBox();
58 this._containmentView.setMinimumSize(50, 25);
59 this._containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid(this);
60 this._containmentDataGrid.show(this._containmentView.element);
61 this._containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
63 this._statisticsView = new WebInspector.HeapSnapshotStatisticsView();
65 this._constructorsView = new WebInspector.VBox();
66 this._constructorsView.setMinimumSize(50, 25);
68 this._constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid(this);
69 this._constructorsDataGrid.show(this._constructorsView.element);
70 this._constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
72 this._diffView = new WebInspector.VBox();
73 this._diffView.setMinimumSize(50, 25);
75 this._diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid(this);
76 this._diffDataGrid.show(this._diffView.element);
77 this._diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
79 if (profile._hasAllocationStacks) {
80 this._allocationView = new WebInspector.VBox();
81 this._allocationView.setMinimumSize(50, 25);
82 this._allocationDataGrid = new WebInspector.AllocationDataGrid(profile.target() , this);
83 this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
84 this._allocationDataGrid.show(this._allocationView.element);
86 this._allocationStackView = new WebInspector.HeapAllocationStackView(profile.target());
87 this._allocationStackView.setMinimumSize(50, 25);
89 this._tabbedPane = new WebInspector.TabbedPane();
90 this._tabbedPane.closeableTabs = false;
91 this._tabbedPane.headerElement().classList.add("heap-object-details-header");
94 this._retainmentView = new WebInspector.VBox();
95 this._retainmentView.setMinimumSize(50, 21);
96 this._retainmentView.element.classList.add("retaining-paths-view");
99 if (this._allocationStackView) {
100 this._tabbedPane = new WebInspector.TabbedPane();
101 this._tabbedPane.closeableTabs = false;
102 this._tabbedPane.headerElement().classList.add("heap-object-details-header");
104 this._tabbedPane.appendTab("retainers", WebInspector.UIString("Retainers"), this._retainmentView);
105 this._tabbedPane.appendTab("allocation-stack", WebInspector.UIString("Allocation stack"), this._allocationStackView);
107 splitViewResizer = this._tabbedPane.headerElement();
108 this._objectDetailsView = this._tabbedPane;
110 var retainmentViewHeader = createElementWithClass("div", "heap-snapshot-view-resizer");
111 var retainingPathsTitleDiv = retainmentViewHeader.createChild("div", "title");
112 var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
113 retainingPathsTitle.textContent = WebInspector.UIString("Retainers");
114 this._retainmentView.element.appendChild(retainmentViewHeader);
116 splitViewResizer = retainmentViewHeader;
117 this._objectDetailsView = this._retainmentView;
119 this._splitView.hideDefaultResizer();
120 this._splitView.installResizer(splitViewResizer);
122 this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid(this);
123 this._retainmentDataGrid.show(this._retainmentView.element);
124 this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
125 this._retainmentDataGrid.reset();
127 this._perspectives = [];
128 this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective());
129 if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
130 this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective());
131 this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective());
132 if (this._allocationView)
133 this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective());
134 this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective());
136 this._perspectiveSelect = new WebInspector.StatusBarComboBox(this._onSelectedPerspectiveChanged.bind(this));
137 for (var i = 0; i < this._perspectives.length; ++i)
138 this._perspectiveSelect.createOption(this._perspectives[i].title());
140 this._profile = profile;
142 this._baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
143 this._baseSelect.visible = false;
144 this._updateBaseOptions();
146 this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
147 this._filterSelect.visible = false;
148 this._updateFilterOptions();
150 this._classNameFilter = new WebInspector.StatusBarInput("Class filter");
151 this._classNameFilter.visible = false;
152 this._constructorsDataGrid.setNameFilter(this._classNameFilter);
153 this._diffDataGrid.setNameFilter(this._classNameFilter);
155 this._selectedSizeText = new WebInspector.StatusBarText("");
157 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
159 this._currentPerspectiveIndex = 0;
160 this._currentPerspective = this._perspectives[0];
161 this._currentPerspective.activate(this);
162 this._dataGrid = this._currentPerspective.masterGrid(this);
169 * @param {string} title
171 WebInspector.HeapSnapshotView.Perspective = function(title)
176 WebInspector.HeapSnapshotView.Perspective.prototype = {
178 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
180 activate: function(heapSnapshotView) { },
183 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
185 deactivate: function(heapSnapshotView)
187 heapSnapshotView._baseSelect.visible = false;
188 heapSnapshotView._filterSelect.visible = false;
189 heapSnapshotView._classNameFilter.visible = false;
190 if (heapSnapshotView._trackingOverviewGrid)
191 heapSnapshotView._trackingOverviewGrid.detach();
192 if (heapSnapshotView._allocationView)
193 heapSnapshotView._allocationView.detach();
194 if (heapSnapshotView._statisticsView)
195 heapSnapshotView._statisticsView.detach();
197 heapSnapshotView._splitView.detach();
198 heapSnapshotView._splitView.detachChildViews();
202 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
203 * @return {?WebInspector.DataGrid}
205 masterGrid: function(heapSnapshotView)
221 supportsSearch: function()
229 * @extends {WebInspector.HeapSnapshotView.Perspective}
231 WebInspector.HeapSnapshotView.SummaryPerspective = function()
233 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Summary"));
236 WebInspector.HeapSnapshotView.SummaryPerspective.prototype = {
239 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
241 activate: function(heapSnapshotView)
243 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
244 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
245 heapSnapshotView._splitView.show(heapSnapshotView.element);
246 heapSnapshotView._filterSelect.visible = true;
247 heapSnapshotView._classNameFilter.visible = true;
248 if (heapSnapshotView._trackingOverviewGrid) {
249 heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView.element, heapSnapshotView._splitView.element);
250 heapSnapshotView._trackingOverviewGrid.update();
251 heapSnapshotView._trackingOverviewGrid._updateGrid();
257 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
258 * @return {?WebInspector.DataGrid}
260 masterGrid: function(heapSnapshotView)
262 return heapSnapshotView._constructorsDataGrid;
269 supportsSearch: function()
274 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
279 * @extends {WebInspector.HeapSnapshotView.Perspective}
281 WebInspector.HeapSnapshotView.ComparisonPerspective = function()
283 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Comparison"));
286 WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = {
289 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
291 activate: function(heapSnapshotView)
293 heapSnapshotView._diffView.show(heapSnapshotView._splitView.mainElement());
294 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
295 heapSnapshotView._splitView.show(heapSnapshotView.element);
296 heapSnapshotView._baseSelect.visible = true;
297 heapSnapshotView._classNameFilter.visible = true;
302 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
303 * @return {?WebInspector.DataGrid}
305 masterGrid: function(heapSnapshotView)
307 return heapSnapshotView._diffDataGrid;
314 supportsSearch: function()
319 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
324 * @extends {WebInspector.HeapSnapshotView.Perspective}
326 WebInspector.HeapSnapshotView.ContainmentPerspective = function()
328 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Containment"));
331 WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = {
334 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
336 activate: function(heapSnapshotView)
338 heapSnapshotView._containmentView.show(heapSnapshotView._splitView.mainElement());
339 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
340 heapSnapshotView._splitView.show(heapSnapshotView.element);
345 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
346 * @return {?WebInspector.DataGrid}
348 masterGrid: function(heapSnapshotView)
350 return heapSnapshotView._containmentDataGrid;
352 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
357 * @extends {WebInspector.HeapSnapshotView.Perspective}
359 WebInspector.HeapSnapshotView.AllocationPerspective = function()
361 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Allocation"));
362 this._allocationSplitView = new WebInspector.SplitView(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
364 var resizer = createElementWithClass("div", "heap-snapshot-view-resizer");
365 var title = resizer.createChild("div", "title").createChild("span");
366 title.textContent = WebInspector.UIString("Live objects");
367 this._allocationSplitView.hideDefaultResizer();
368 this._allocationSplitView.installResizer(resizer);
370 this._allocationSplitView.sidebarElement().appendChild(resizer);
373 WebInspector.HeapSnapshotView.AllocationPerspective.prototype = {
376 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
378 activate: function(heapSnapshotView)
380 heapSnapshotView._allocationView.show(this._allocationSplitView.mainElement());
381 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
382 heapSnapshotView._objectDetailsView.show(heapSnapshotView._splitView.sidebarElement());
383 heapSnapshotView._splitView.show(this._allocationSplitView.sidebarElement());
384 this._allocationSplitView.show(heapSnapshotView.element);
386 heapSnapshotView._constructorsDataGrid.clear();
387 var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode;
389 heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
394 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
396 deactivate: function(heapSnapshotView)
398 this._allocationSplitView.detach();
399 WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView);
404 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
405 * @return {?WebInspector.DataGrid}
407 masterGrid: function(heapSnapshotView)
409 return heapSnapshotView._allocationDataGrid;
412 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
417 * @extends {WebInspector.HeapSnapshotView.Perspective}
419 WebInspector.HeapSnapshotView.StatisticsPerspective = function()
421 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Statistics"));
424 WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = {
427 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
429 activate: function(heapSnapshotView)
431 heapSnapshotView._statisticsView.show(heapSnapshotView.element);
436 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
437 * @return {?WebInspector.DataGrid}
439 masterGrid: function(heapSnapshotView)
444 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
448 WebInspector.HeapSnapshotView.prototype = {
451 * @param {?WebInspector.ProfileHeader} profile
452 * @return {?WebInspector.View}
454 showProfile: function(profile)
456 return this._parentDataDisplayDelegate.showProfile(profile);
461 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
462 * @param {string} perspectiveName
464 showObject: function(snapshotObjectId, perspectiveName)
466 if (snapshotObjectId <= this._profile.maxJSObjectId)
467 this.highlightLiveObject(perspectiveName, snapshotObjectId);
469 this._parentDataDisplayDelegate.showObject(snapshotObjectId, perspectiveName);
472 _refreshView: function()
474 this._profile.load(profileCallback.bind(this));
477 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
478 * @this {WebInspector.HeapSnapshotView}
480 function profileCallback(heapSnapshotProxy)
482 heapSnapshotProxy.getStatistics(this._gotStatistics.bind(this));
483 var list = this._profiles();
484 var profileIndex = list.indexOf(this._profile);
485 this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
486 this._dataGrid.setDataSource(heapSnapshotProxy);
487 if (this._trackingOverviewGrid)
488 this._trackingOverviewGrid._updateGrid();
493 * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
495 _gotStatistics: function(statistics)
497 this._statisticsView.setTotal(statistics.total);
498 this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77");
499 this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5");
500 this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af");
501 this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5");
502 this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total"));
505 _onIdsRangeChanged: function(event)
507 var minId = event.data.minId;
508 var maxId = event.data.maxId;
509 this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
510 if (this._constructorsDataGrid.snapshot)
511 this._constructorsDataGrid.setSelectionRange(minId, maxId);
516 var result = [this._perspectiveSelect.element, this._classNameFilter.element];
517 if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
518 result.push(this._baseSelect.element, this._filterSelect.element);
519 result.push(this._selectedSizeText.element);
525 // FIXME: load base and current snapshots in parallel
526 this._profile.load(profileCallback.bind(this));
529 * @this {WebInspector.HeapSnapshotView}
531 function profileCallback() {
532 this._profile._wasShown();
533 if (this._baseProfile)
534 this._baseProfile.load(function() { });
540 this._currentSearchResultIndex = -1;
541 this._popoverHelper.hidePopover();
542 if (this.helpPopover && this.helpPopover.isShowing())
543 this.helpPopover.hide();
546 searchCanceled: function()
548 if (this._searchResults) {
549 for (var i = 0; i < this._searchResults.length; ++i) {
550 var node = this._searchResults[i].node;
551 delete node._searchMatched;
556 delete this._searchFinishedCallback;
557 this._currentSearchResultIndex = -1;
558 this._searchResults = [];
562 * @param {string} query
563 * @param {function(!WebInspector.View, number)} finishedCallback
565 performSearch: function(query, finishedCallback)
567 // Call searchCanceled since it will reset everything we need before doing a new search.
568 this.searchCanceled();
570 query = query.trim();
574 if (!this._currentPerspective.supportsSearch())
578 * @param {boolean} found
579 * @this {WebInspector.HeapSnapshotView}
581 function didHighlight(found)
583 finishedCallback(this, found ? 1 : 0);
586 if (query.charAt(0) === "@") {
587 var snapshotNodeId = parseInt(query.substring(1), 10);
588 if (!isNaN(snapshotNodeId))
589 this._dataGrid.highlightObjectByHeapSnapshotId(String(snapshotNodeId), didHighlight.bind(this));
591 finishedCallback(this, 0);
595 this._searchFinishedCallback = finishedCallback;
596 var nameRegExp = createPlainTextSearchRegex(query, "i");
598 function matchesByName(gridNode) {
599 return ("_name" in gridNode) && nameRegExp.test(gridNode._name);
602 function matchesQuery(gridNode)
604 delete gridNode._searchMatched;
605 if (matchesByName(gridNode)) {
606 gridNode._searchMatched = true;
613 var current = this._dataGrid.rootNode().children[0];
617 // Restrict to type nodes and instances.
621 if (matchesQuery(current))
622 this._searchResults.push({ node: current });
623 current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
624 depth += info.depthChange;
627 finishedCallback(this, this._searchResults.length);
630 jumpToFirstSearchResult: function()
632 if (!this._searchResults || !this._searchResults.length)
634 this._currentSearchResultIndex = 0;
635 this._jumpToSearchResult(this._currentSearchResultIndex);
638 jumpToLastSearchResult: function()
640 if (!this._searchResults || !this._searchResults.length)
642 this._currentSearchResultIndex = (this._searchResults.length - 1);
643 this._jumpToSearchResult(this._currentSearchResultIndex);
646 jumpToNextSearchResult: function()
648 if (!this._searchResults || !this._searchResults.length)
650 if (++this._currentSearchResultIndex >= this._searchResults.length)
651 this._currentSearchResultIndex = 0;
652 this._jumpToSearchResult(this._currentSearchResultIndex);
655 jumpToPreviousSearchResult: function()
657 if (!this._searchResults || !this._searchResults.length)
659 if (--this._currentSearchResultIndex < 0)
660 this._currentSearchResultIndex = (this._searchResults.length - 1);
661 this._jumpToSearchResult(this._currentSearchResultIndex);
667 currentSearchResultIndex: function() {
668 return this._currentSearchResultIndex;
671 _jumpToSearchResult: function(index)
673 var searchResult = this._searchResults[index];
677 var node = searchResult.node;
678 node.revealAndSelect();
681 refreshVisibleData: function()
685 var child = this._dataGrid.rootNode().children[0];
688 child = child.traverseNextNode(false, null, true);
692 _changeBase: function()
694 if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()])
697 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
698 var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid);
699 // Change set base data source only if main data source is already set.
700 if (dataGrid.snapshot)
701 this._baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid));
703 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
706 // The current search needs to be performed again. First negate out previous match
707 // count by calling the search finished callback with a negative number of matches.
708 // Then perform the search again with the same query and callback.
709 this._searchFinishedCallback(this, -this._searchResults.length);
710 this.performSearch(this.currentQuery, this._searchFinishedCallback);
713 _changeFilter: function()
715 var profileIndex = this._filterSelect.selectedIndex() - 1;
716 this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
718 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
719 action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
720 label: this._filterSelect.selectedOption().label
723 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
726 // The current search needs to be performed again. First negate out previous match
727 // count by calling the search finished callback with a negative number of matches.
728 // Then perform the search again with the same query and callback.
729 this._searchFinishedCallback(this, -this._searchResults.length);
730 this.performSearch(this.currentQuery, this._searchFinishedCallback);
734 * @return {!Array.<!WebInspector.ProfileHeader>}
736 _profiles: function()
738 return this._profile.profileType().getProfiles();
742 * @param {!WebInspector.ContextMenu} contextMenu
743 * @param {!Event} event
745 populateContextMenu: function(contextMenu, event)
748 this._dataGrid.populateContextMenu(contextMenu, event);
751 _selectionChanged: function(event)
753 var selectedNode = event.target.selectedNode;
754 this._setSelectedNodeForDetailsView(selectedNode);
755 this._inspectedObjectChanged(event);
758 _onSelectAllocationNode: function(event)
760 var selectedNode = event.target.selectedNode;
761 this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
762 this._setSelectedNodeForDetailsView(null);
765 _inspectedObjectChanged: function(event)
767 var selectedNode = event.target.selectedNode;
768 var target = this._profile.target();
769 if (target && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
770 target.consoleAgent().addInspectedHeapObject(selectedNode.snapshotNodeId);
774 * @param {?WebInspector.HeapSnapshotGridNode} nodeItem
776 _setSelectedNodeForDetailsView: function(nodeItem)
778 var dataSource = nodeItem && nodeItem.retainersDataSource();
780 this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
781 if (this._allocationStackView)
782 this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex)
784 if (this._allocationStackView)
785 this._allocationStackView.clear();
786 this._retainmentDataGrid.reset();
791 * @param {string} perspectiveTitle
792 * @param {function()} callback
794 _changePerspectiveAndWait: function(perspectiveTitle, callback)
796 var perspectiveIndex = null;
797 for (var i = 0; i < this._perspectives.length; ++i) {
798 if (this._perspectives[i].title() === perspectiveTitle) {
799 perspectiveIndex = i;
803 if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) {
804 setTimeout(callback, 0);
809 * @this {WebInspector.HeapSnapshotView}
811 function dataGridContentShown(event)
813 var dataGrid = event.data;
814 dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
815 if (dataGrid === this._dataGrid)
818 this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
820 this._perspectiveSelect.setSelectedIndex(perspectiveIndex);
821 this._changePerspective(perspectiveIndex);
824 _updateDataSourceAndView: function()
826 var dataGrid = this._dataGrid;
827 if (!dataGrid || dataGrid.snapshot)
830 this._profile.load(didLoadSnapshot.bind(this));
833 * @this {WebInspector.HeapSnapshotView}
835 function didLoadSnapshot(snapshotProxy)
837 if (this._dataGrid !== dataGrid)
839 if (dataGrid.snapshot !== snapshotProxy)
840 dataGrid.setDataSource(snapshotProxy);
841 if (dataGrid === this._diffDataGrid) {
842 if (!this._baseProfile)
843 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
844 this._baseProfile.load(didLoadBaseSnaphot.bind(this));
849 * @this {WebInspector.HeapSnapshotView}
851 function didLoadBaseSnaphot(baseSnapshotProxy)
853 if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy)
854 this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
858 _onSelectedPerspectiveChanged: function(event)
860 this._changePerspective(event.target.selectedIndex);
861 // FIXME: This is needed by CodeSchool extension.
862 this._onSelectedViewChanged(event);
865 _onSelectedViewChanged: function(event)
870 * @param {number} selectedIndex
872 _changePerspective: function(selectedIndex)
874 if (selectedIndex === this._currentPerspectiveIndex)
877 this._currentPerspectiveIndex = selectedIndex;
879 this._currentPerspective.deactivate(this);
880 var perspective = this._perspectives[selectedIndex];
881 this._currentPerspective = perspective;
882 this._dataGrid = perspective.masterGrid(this);
883 perspective.activate(this);
885 this.refreshVisibleData();
887 this._dataGrid.updateWidths();
889 this._updateDataSourceAndView();
891 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
894 // The current search needs to be performed again. First negate out previous match
895 // count by calling the search finished callback with a negative number of matches.
896 // Then perform the search again the with same query and callback.
897 this._searchFinishedCallback(this, -this._searchResults.length);
898 this.performSearch(this.currentQuery, this._searchFinishedCallback);
902 * @param {string} perspectiveName
903 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
905 highlightLiveObject: function(perspectiveName, snapshotObjectId)
907 this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this));
910 * @this {WebInspector.HeapSnapshotView}
912 function didChangePerspective()
914 this._dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId, didHighlightObject);
917 function didHighlightObject(found)
920 WebInspector.console.error("Cannot find corresponding heap snapshot node");
924 _getHoverAnchor: function(target)
926 var span = target.enclosingNodeOrSelfWithNodeName("span");
929 var row = target.enclosingNodeOrSelfWithNodeName("tr");
932 span.node = row._dataGridNode;
936 _resolveObjectForPopover: function(element, showCallback, objectGroupName)
938 if (!this._profile.target())
940 element.node.queryObjectContent(this._profile.target(), showCallback, objectGroupName);
943 _updateBaseOptions: function()
945 var list = this._profiles();
946 // We're assuming that snapshots can only be added.
947 if (this._baseSelect.size() === list.length)
950 for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) {
951 var title = list[i].title;
952 this._baseSelect.createOption(title);
956 _updateFilterOptions: function()
958 var list = this._profiles();
959 // We're assuming that snapshots can only be added.
960 if (this._filterSelect.size() - 1 === list.length)
963 if (!this._filterSelect.size())
964 this._filterSelect.createOption(WebInspector.UIString("All objects"));
966 for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
967 var title = list[i].title;
969 title = WebInspector.UIString("Objects allocated before %s", title);
971 title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
972 this._filterSelect.createOption(title);
976 _updateControls: function()
978 this._updateBaseOptions();
979 this._updateFilterOptions();
983 * @param {!WebInspector.Event} event
985 _onReceiveSnapshot: function(event)
987 this._updateControls();
991 * @param {!WebInspector.Event} event
993 _onProfileHeaderRemoved: function(event)
995 var profile = event.data;
996 if (this._profile === profile) {
998 this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
999 this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
1002 this._updateControls();
1008 if (this._allocationStackView) {
1009 this._allocationStackView.clear();
1010 this._allocationDataGrid.dispose();
1014 __proto__: WebInspector.VBox.prototype
1019 * @extends {WebInspector.ProfileType}
1020 * @implements {WebInspector.TargetManager.Observer}
1021 * @param {string=} id
1022 * @param {string=} title
1024 WebInspector.HeapSnapshotProfileType = function(id, title)
1026 WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
1027 WebInspector.targetManager.observeTargets(this);
1028 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ResetProfiles, this._resetProfiles, this);
1029 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.AddHeapSnapshotChunk, this._addHeapSnapshotChunk, this);
1030 WebInspector.targetManager.addModelListener(WebInspector.HeapProfilerModel, WebInspector.HeapProfilerModel.Events.ReportHeapSnapshotProgress, this._reportHeapSnapshotProgress, this);
1033 WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
1034 WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
1036 WebInspector.HeapSnapshotProfileType.prototype = {
1038 * @param {!WebInspector.Target} target
1040 targetAdded: function(target)
1042 target.heapProfilerModel.enable();
1046 * @param {!WebInspector.Target} target
1048 targetRemoved: function(target)
1056 fileExtension: function()
1058 return ".heapsnapshot";
1063 return WebInspector.UIString("Take heap snapshot.");
1070 isInstantProfile: function()
1079 buttonClicked: function()
1081 this._takeHeapSnapshot(function() {});
1082 WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
1088 return WebInspector.UIString("HEAP SNAPSHOTS");
1093 return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
1098 * @param {string} title
1099 * @return {!WebInspector.ProfileHeader}
1101 createProfileLoadedFromFile: function(title)
1103 return new WebInspector.HeapProfileHeader(null, this, title);
1106 _takeHeapSnapshot: function(callback)
1108 if (this.profileBeingRecorded())
1110 var target = /** @type {!WebInspector.Target} */ (WebInspector.context.flavor(WebInspector.Target));
1111 var profile = new WebInspector.HeapProfileHeader(target, this);
1112 this.setProfileBeingRecorded(profile);
1113 this.addProfile(profile);
1114 profile.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1117 * @param {?string} error
1118 * @this {WebInspector.HeapSnapshotProfileType}
1120 function didTakeHeapSnapshot(error)
1122 var profile = this._profileBeingRecorded;
1123 profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
1124 profile._finishLoad();
1125 this.setProfileBeingRecorded(null);
1126 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
1129 target.heapProfilerAgent().takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
1133 * @param {!WebInspector.Event} event
1135 _addHeapSnapshotChunk: function(event)
1137 if (!this.profileBeingRecorded())
1139 var chunk = /** @type {string} */(event.data);
1140 this.profileBeingRecorded().transferChunk(chunk);
1144 * @param {!WebInspector.Event} event
1146 _reportHeapSnapshotProgress: function(event)
1148 var profile = this.profileBeingRecorded();
1151 var data = /** @type {{done: number, total: number, finished: boolean}} */ (event.data);
1152 profile.updateStatus(WebInspector.UIString("%.0f%", (data.done / data.total) * 100), true);
1154 profile._prepareToLoad();
1157 _resetProfiles: function()
1162 _snapshotReceived: function(profile)
1164 if (this._profileBeingRecorded === profile)
1165 this.setProfileBeingRecorded(null);
1166 this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
1169 __proto__: WebInspector.ProfileType.prototype
1175 * @extends {WebInspector.HeapSnapshotProfileType}
1177 WebInspector.TrackingHeapSnapshotProfileType = function()
1179 WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
1182 WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
1184 WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
1185 WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
1186 WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
1188 WebInspector.TrackingHeapSnapshotProfileType.prototype = {
1191 * @param {!WebInspector.Target} target
1193 targetAdded: function(target)
1195 WebInspector.HeapSnapshotProfileType.prototype.targetAdded.call(this, target);
1196 target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
1197 target.heapProfilerModel.addEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
1201 * @param {!WebInspector.Target} target
1203 targetRemoved: function(target)
1205 WebInspector.HeapSnapshotProfileType.prototype.targetRemoved.call(this, target);
1206 target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.HeapStatsUpdate, this._heapStatsUpdate, this);
1207 target.heapProfilerModel.removeEventListener(WebInspector.HeapProfilerModel.Events.LastSeenObjectId, this._lastSeenObjectId, this);
1211 * @param {!WebInspector.Event} event
1213 _heapStatsUpdate: function(event)
1215 if (!this._profileSamples)
1217 var samples = /** @type {!Array.<number>} */ (event.data);
1219 for (var i = 0; i < samples.length; i += 3) {
1221 var count = samples[i+1];
1222 var size = samples[i+2];
1223 this._profileSamples.sizes[index] = size;
1224 if (!this._profileSamples.max[index])
1225 this._profileSamples.max[index] = size;
1230 * @param {!WebInspector.Event} event
1232 _lastSeenObjectId: function(event)
1234 var profileSamples = this._profileSamples;
1235 if (!profileSamples)
1237 var data = /** @type {{lastSeenObjectId: number, timestamp: number}} */ (event.data);
1238 var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
1239 profileSamples.ids[currentIndex] = data.lastSeenObjectId;
1240 if (!profileSamples.max[currentIndex]) {
1241 profileSamples.max[currentIndex] = 0;
1242 profileSamples.sizes[currentIndex] = 0;
1244 profileSamples.timestamps[currentIndex] = data.timestamp;
1245 if (profileSamples.totalTime < data.timestamp - profileSamples.timestamps[0])
1246 profileSamples.totalTime *= 2;
1247 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
1248 this._profileBeingRecorded.updateStatus(null, true);
1255 hasTemporaryView: function()
1262 return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
1269 isInstantProfile: function()
1278 buttonClicked: function()
1280 return this._toggleRecording();
1283 _startRecordingProfile: function()
1285 if (this.profileBeingRecorded())
1287 var recordAllocationStacks = WebInspector.settings.recordAllocationStacks.get();
1288 this._addNewProfile(recordAllocationStacks);
1289 this.profileBeingRecorded().target().heapProfilerAgent().startTrackingHeapObjects(recordAllocationStacks);
1293 * @param {boolean} withAllocationStacks
1295 _addNewProfile: function(withAllocationStacks)
1297 var target = WebInspector.context.flavor(WebInspector.Target);
1298 this.setProfileBeingRecorded(new WebInspector.HeapProfileHeader(target, this, undefined, withAllocationStacks));
1299 this._lastSeenIndex = -1;
1300 this._profileSamples = {
1307 this._profileBeingRecorded._profileSamples = this._profileSamples;
1308 this._recording = true;
1309 this.addProfile(this._profileBeingRecorded);
1310 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
1311 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
1314 _stopRecordingProfile: function()
1316 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1318 * @param {?string} error
1319 * @this {WebInspector.HeapSnapshotProfileType}
1321 function didTakeHeapSnapshot(error)
1323 var profile = this.profileBeingRecorded();
1326 profile._finishLoad();
1327 this._profileSamples = null;
1328 this.setProfileBeingRecorded(null);
1329 this.dispatchEventToListeners(WebInspector.ProfileType.Events.ProfileComplete, profile);
1332 this._profileBeingRecorded.target().heapProfilerAgent().stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
1333 this._recording = false;
1334 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
1337 _toggleRecording: function()
1339 if (this._recording)
1340 this._stopRecordingProfile();
1342 this._startRecordingProfile();
1343 return this._recording;
1348 return WebInspector.UIString("HEAP TIMELINES");
1353 return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
1359 resetProfiles: function()
1361 var wasRecording = this._recording;
1362 var recordingAllocationStacks = wasRecording && this.profileBeingRecorded()._hasAllocationStacks;
1363 // Clear current profile to avoid stopping backend.
1364 this.setProfileBeingRecorded(null);
1365 WebInspector.HeapSnapshotProfileType.prototype.resetProfiles.call(this);
1366 this._profileSamples = null;
1367 this._lastSeenIndex = -1;
1369 this._addNewProfile(recordingAllocationStacks);
1375 profileBeingRecordedRemoved: function()
1377 this._stopRecordingProfile();
1378 this._profileSamples = null;
1381 __proto__: WebInspector.HeapSnapshotProfileType.prototype
1386 * @extends {WebInspector.ProfileHeader}
1387 * @param {?WebInspector.Target} target
1388 * @param {!WebInspector.HeapSnapshotProfileType} type
1389 * @param {string=} title
1390 * @param {boolean=} hasAllocationStacks
1392 WebInspector.HeapProfileHeader = function(target, type, title, hasAllocationStacks)
1394 WebInspector.ProfileHeader.call(this, target, type, title || WebInspector.UIString("Snapshot %d", type.nextProfileUid()));
1395 this._hasAllocationStacks = !!hasAllocationStacks;
1396 this.maxJSObjectId = -1;
1398 * @type {?WebInspector.HeapSnapshotWorkerProxy}
1400 this._workerProxy = null;
1402 * @type {?WebInspector.OutputStream}
1404 this._receiver = null;
1406 * @type {?WebInspector.HeapSnapshotProxy}
1408 this._snapshotProxy = null;
1410 * @type {?Array.<function(!WebInspector.HeapSnapshotProxy)>}
1412 this._loadCallbacks = [];
1413 this._totalNumberOfChunks = 0;
1414 this._bufferedWriter = null;
1417 WebInspector.HeapProfileHeader.prototype = {
1420 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1421 * @return {!WebInspector.ProfileSidebarTreeElement}
1423 createSidebarTreeElement: function(dataDisplayDelegate)
1425 return new WebInspector.ProfileSidebarTreeElement(dataDisplayDelegate, this, "heap-snapshot-sidebar-tree-item");
1430 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1431 * @return {!WebInspector.HeapSnapshotView}
1433 createView: function(dataDisplayDelegate)
1435 return new WebInspector.HeapSnapshotView(dataDisplayDelegate, this);
1440 * @param {function(!WebInspector.HeapSnapshotProxy):void} callback
1442 load: function(callback)
1444 if (this.uid === -1)
1446 if (this._snapshotProxy) {
1447 callback(this._snapshotProxy);
1450 this._loadCallbacks.push(callback);
1453 _prepareToLoad: function()
1455 console.assert(!this._receiver, "Already loading");
1456 this._setupWorker();
1457 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1460 _finishLoad: function()
1462 if (!this._wasDisposed)
1463 this._receiver.close();
1464 if (this._bufferedWriter) {
1465 this._bufferedWriter.finishWriting(this._didWriteToTempFile.bind(this));
1466 this._bufferedWriter = null;
1470 _didWriteToTempFile: function(tempFile)
1472 if (this._wasDisposed) {
1477 this._tempFile = tempFile;
1479 this._failedToCreateTempFile = true;
1480 if (this._onTempFileReady) {
1481 this._onTempFileReady();
1482 this._onTempFileReady = null;
1486 _setupWorker: function()
1489 * @this {WebInspector.HeapProfileHeader}
1491 function setProfileWait(event)
1493 this.updateStatus(null, event.data);
1495 console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists");
1496 this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this));
1497 this._workerProxy.addEventListener("wait", setProfileWait, this);
1498 this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this));
1502 * @param {string} eventName
1505 _handleWorkerEvent: function(eventName, data)
1507 if (WebInspector.HeapSnapshotProgressEvent.BrokenSnapshot === eventName) {
1508 var error = /** @type {string} */ (data);
1509 WebInspector.console.error(error);
1513 if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName)
1515 var subtitle = /** @type {string} */ (data);
1516 this.updateStatus(subtitle);
1524 if (this._workerProxy)
1525 this._workerProxy.dispose();
1526 this.removeTempFile();
1527 this._wasDisposed = true;
1530 _didCompleteSnapshotTransfer: function()
1532 if (!this._snapshotProxy)
1534 this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false);
1538 * @param {string} chunk
1540 transferChunk: function(chunk)
1542 if (!this._bufferedWriter)
1543 this._bufferedWriter = new WebInspector.DeferredTempFile("heap-profiler", String(this.uid));
1544 this._bufferedWriter.write([chunk]);
1546 ++this._totalNumberOfChunks;
1547 this._receiver.write(chunk, function() {});
1550 _snapshotReceived: function(snapshotProxy)
1552 if (this._wasDisposed)
1554 this._receiver = null;
1555 this._snapshotProxy = snapshotProxy;
1556 this.maxJSObjectId = snapshotProxy.maxJSObjectId();
1557 this._didCompleteSnapshotTransfer();
1558 this._workerProxy.startCheckingForLongRunningCalls();
1559 this.notifySnapshotReceived();
1562 notifySnapshotReceived: function()
1564 for (var i = 0; i < this._loadCallbacks.length; i++)
1565 this._loadCallbacks[i](/** @type {!WebInspector.HeapSnapshotProxy} */ (this._snapshotProxy));
1566 this._loadCallbacks = null;
1567 this._profileType._snapshotReceived(this);
1568 if (this.canSaveToFile())
1569 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
1572 // Hook point for tests.
1573 _wasShown: function()
1581 canSaveToFile: function()
1583 return !this.fromFile() && !!this._snapshotProxy;
1589 saveToFile: function()
1591 var fileOutputStream = new WebInspector.FileOutputStream();
1594 * @param {boolean} accepted
1595 * @this {WebInspector.HeapProfileHeader}
1597 function onOpen(accepted)
1601 if (this._failedToCreateTempFile) {
1602 WebInspector.console.error("Failed to open temp file with heap snapshot");
1603 fileOutputStream.close();
1604 } else if (this._tempFile) {
1605 var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this);
1606 this._tempFile.writeToOutputSteam(fileOutputStream, delegate);
1608 this._onTempFileReady = onOpen.bind(this, accepted);
1609 this._updateSaveProgress(0, 1);
1612 this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
1613 fileOutputStream.open(this._fileName, onOpen.bind(this));
1616 _updateSaveProgress: function(value, total)
1618 var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
1619 this.updateStatus(WebInspector.UIString("Saving\u2026 %d\%", percentValue));
1624 * @param {!File} file
1626 loadFromFile: function(file)
1628 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1629 this._setupWorker();
1630 var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
1631 var fileReader = this._createFileReader(file, delegate);
1632 fileReader.start(this._receiver);
1635 _createFileReader: function(file, delegate)
1637 return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
1640 __proto__: WebInspector.ProfileHeader.prototype
1645 * @implements {WebInspector.OutputStreamDelegate}
1647 WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
1649 this._snapshotHeader = snapshotHeader;
1652 WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
1653 onTransferStarted: function()
1658 * @param {!WebInspector.ChunkedReader} reader
1660 onChunkTransferred: function(reader)
1664 onTransferFinished: function()
1669 * @param {!WebInspector.ChunkedReader} reader
1672 onError: function (reader, e)
1675 switch(e.target.error.code) {
1676 case e.target.error.NOT_FOUND_ERR:
1677 subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
1679 case e.target.error.NOT_READABLE_ERR:
1680 subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
1682 case e.target.error.ABORT_ERR:
1685 subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
1687 this._snapshotHeader.updateStatus(subtitle);
1693 * @implements {WebInspector.OutputStreamDelegate}
1694 * @param {!WebInspector.HeapProfileHeader} profileHeader
1696 WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
1698 this._profileHeader = profileHeader;
1701 WebInspector.SaveSnapshotOutputStreamDelegate.prototype = {
1702 onTransferStarted: function()
1704 this._profileHeader._updateSaveProgress(0, 1);
1707 onTransferFinished: function()
1709 this._profileHeader._didCompleteSnapshotTransfer();
1713 * @param {!WebInspector.ChunkedReader} reader
1715 onChunkTransferred: function(reader)
1717 this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize());
1721 * @param {!WebInspector.ChunkedReader} reader
1722 * @param {!Event} event
1724 onError: function(reader, event)
1726 WebInspector.console.error("Failed to read heap snapshot from temp file: " + /** @type {!ErrorEvent} */ (event).message);
1727 this.onTransferFinished();
1733 * @extends {WebInspector.VBox}
1734 * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
1736 WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
1738 WebInspector.VBox.call(this);
1739 this.element.id = "heap-recording-view";
1740 this.element.classList.add("heap-tracking-overview");
1742 this._overviewContainer = this.element.createChild("div", "heap-overview-container");
1743 this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
1744 this._overviewGrid.element.classList.add("fill");
1746 this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
1747 this._overviewContainer.appendChild(this._overviewGrid.element);
1748 this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
1749 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
1751 this._profileSamples = heapProfileHeader._profileSamples;
1752 if (heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) {
1753 this._profileType = heapProfileHeader.profileType();
1754 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1755 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1757 var timestamps = this._profileSamples.timestamps;
1758 var totalTime = this._profileSamples.totalTime;
1759 this._windowLeft = 0.0;
1760 this._windowRight = totalTime && timestamps.length ? (timestamps[timestamps.length - 1] - timestamps[0]) / totalTime : 1.0;
1761 this._overviewGrid.setWindow(this._windowLeft, this._windowRight);
1762 this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1763 this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1766 WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
1768 WebInspector.HeapTrackingOverviewGrid.prototype = {
1769 _onStopTracking: function(event)
1771 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1772 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1775 _onHeapStatsUpdate: function(event)
1777 this._profileSamples = event.data;
1778 this._scheduleUpdate();
1782 * @param {number} width
1783 * @param {number} height
1785 _drawOverviewCanvas: function(width, height)
1787 if (!this._profileSamples)
1789 var profileSamples = this._profileSamples;
1790 var sizes = profileSamples.sizes;
1791 var topSizes = profileSamples.max;
1792 var timestamps = profileSamples.timestamps;
1793 var startTime = timestamps[0];
1794 var endTime = timestamps[timestamps.length - 1];
1796 var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime);
1799 * @param {!Array.<number>} sizes
1800 * @param {function(number, number):void} callback
1802 function aggregateAndCall(sizes, callback)
1806 for (var i = 1; i < timestamps.length; ++i) {
1807 var x = Math.floor((timestamps[i] - startTime) * scaleFactor);
1808 if (x !== currentX) {
1810 callback(currentX, size);
1816 callback(currentX, size);
1821 * @param {number} size
1823 function maxSizeCallback(x, size)
1825 maxSize = Math.max(maxSize, size);
1828 aggregateAndCall(sizes, maxSizeCallback);
1830 var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0);
1832 this._overviewCanvas.width = width * window.devicePixelRatio;
1833 this._overviewCanvas.height = height * window.devicePixelRatio;
1834 this._overviewCanvas.style.width = width + "px";
1835 this._overviewCanvas.style.height = height + "px";
1837 var context = this._overviewCanvas.getContext("2d");
1838 context.scale(window.devicePixelRatio, window.devicePixelRatio);
1840 context.beginPath();
1841 context.lineWidth = 2;
1842 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1843 var currentX = (endTime - startTime) * scaleFactor;
1844 context.moveTo(currentX, height - 1);
1845 context.lineTo(currentX, 0);
1847 context.closePath();
1851 var gridLabelHeight = 14;
1853 const maxGridValue = (height - gridLabelHeight) / yScaleFactor;
1854 // The round value calculation is a bit tricky, because
1855 // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
1856 // e.g. a round value 10KB is 10240 bytes.
1857 gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024)));
1858 gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10));
1859 if (gridValue * 5 <= maxGridValue)
1861 gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5;
1862 context.beginPath();
1863 context.lineWidth = 1;
1864 context.strokeStyle = "rgba(0, 0, 0, 0.2)";
1865 context.moveTo(0, gridY);
1866 context.lineTo(width, gridY);
1868 context.closePath();
1873 * @param {number} size
1875 function drawBarCallback(x, size)
1877 context.moveTo(x, height - 1);
1878 context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
1881 context.beginPath();
1882 context.lineWidth = 2;
1883 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1884 aggregateAndCall(topSizes, drawBarCallback);
1886 context.closePath();
1888 context.beginPath();
1889 context.lineWidth = 2;
1890 context.strokeStyle = "rgba(0, 0, 192, 0.8)";
1891 aggregateAndCall(sizes, drawBarCallback);
1893 context.closePath();
1896 var label = Number.bytesToString(gridValue);
1897 var labelPadding = 4;
1899 var labelY = gridY - 0.5;
1900 var labelWidth = 2 * labelPadding + context.measureText(label).width;
1901 context.beginPath();
1902 context.textBaseline = "bottom";
1903 context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
1904 context.fillStyle = "rgba(255, 255, 255, 0.75)";
1905 context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight);
1906 context.fillStyle = "rgb(64, 64, 64)";
1907 context.fillText(label, labelX + labelPadding, labelY);
1909 context.closePath();
1913 onResize: function()
1915 this._updateOverviewCanvas = true;
1916 this._scheduleUpdate();
1919 _onWindowChanged: function()
1921 if (!this._updateGridTimerId)
1922 this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
1925 _scheduleUpdate: function()
1927 if (this._updateTimerId)
1929 this._updateTimerId = setTimeout(this.update.bind(this), 10);
1932 _updateBoundaries: function()
1934 this._windowLeft = this._overviewGrid.windowLeft();
1935 this._windowRight = this._overviewGrid.windowRight();
1936 this._windowWidth = this._windowRight - this._windowLeft;
1941 this._updateTimerId = null;
1942 if (!this.isShowing())
1944 this._updateBoundaries();
1945 this._overviewCalculator._updateBoundaries(this);
1946 this._overviewGrid.updateDividers(this._overviewCalculator);
1947 this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
1950 _updateGrid: function()
1952 this._updateGridTimerId = 0;
1953 this._updateBoundaries();
1954 var ids = this._profileSamples.ids;
1955 var timestamps = this._profileSamples.timestamps;
1956 var sizes = this._profileSamples.sizes;
1957 var startTime = timestamps[0];
1958 var totalTime = this._profileSamples.totalTime;
1959 var timeLeft = startTime + totalTime * this._windowLeft;
1960 var timeRight = startTime + totalTime * this._windowRight;
1962 var maxId = ids[ids.length - 1] + 1;
1964 for (var i = 0; i < timestamps.length; ++i) {
1967 if (timestamps[i] > timeRight)
1970 if (timestamps[i] < timeLeft) {
1977 this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
1980 __proto__: WebInspector.VBox.prototype
1987 WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
1989 this._lastUpdate = 0;
1990 this._currentScale = 0.0;
1993 WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = {
1995 * @param {number} target
1998 nextScale: function(target) {
1999 target = target || this._currentScale;
2000 if (this._currentScale) {
2001 var now = Date.now();
2002 var timeDeltaMs = now - this._lastUpdate;
2003 this._lastUpdate = now;
2004 var maxChangePerSec = 20;
2005 var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000);
2006 var scaleChange = target / this._currentScale;
2007 this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta);
2009 this._currentScale = target;
2010 return this._currentScale;
2017 * @implements {WebInspector.TimelineGrid.Calculator}
2019 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
2023 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
2027 paddingLeft: function()
2033 * @param {!WebInspector.HeapTrackingOverviewGrid} chart
2035 _updateBoundaries: function(chart)
2037 this._minimumBoundaries = 0;
2038 this._maximumBoundaries = chart._profileSamples.totalTime;
2039 this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
2043 * @param {number} time
2046 computePosition: function(time)
2048 return (time - this._minimumBoundaries) * this._xScaleFactor;
2052 * @param {number} value
2053 * @param {number=} precision
2056 formatTime: function(value, precision)
2058 return Number.secondsToString(value / 1000, !!precision);
2064 maximumBoundary: function()
2066 return this._maximumBoundaries;
2072 minimumBoundary: function()
2074 return this._minimumBoundaries;
2080 zeroTime: function()
2082 return this._minimumBoundaries;
2088 boundarySpan: function()
2090 return this._maximumBoundaries - this._minimumBoundaries;
2097 * @extends {WebInspector.VBox}
2099 WebInspector.HeapSnapshotStatisticsView = function()
2101 WebInspector.VBox.call(this);
2102 this.setMinimumSize(50, 25);
2103 this._pieChart = new WebInspector.PieChart(150, WebInspector.HeapSnapshotStatisticsView._valueFormatter, true);
2104 this.element.appendChild(this._pieChart.element);
2105 this._labels = this.element.createChild("div", "heap-snapshot-stats-legend");
2109 * @param {number} value
2112 WebInspector.HeapSnapshotStatisticsView._valueFormatter = function(value)
2114 return WebInspector.UIString("%s KB", Number.withThousandsSeparator(Math.round(value / 1024)));
2117 WebInspector.HeapSnapshotStatisticsView.prototype = {
2119 * @param {number} value
2121 setTotal: function(value)
2123 this._pieChart.setTotal(value);
2127 * @param {number} value
2128 * @param {string} name
2129 * @param {string=} color
2131 addRecord: function(value, name, color)
2134 this._pieChart.addSlice(value, color);
2136 var node = this._labels.createChild("div");
2137 var swatchDiv = node.createChild("div", "heap-snapshot-stats-swatch");
2138 var nameDiv = node.createChild("div", "heap-snapshot-stats-name");
2139 var sizeDiv = node.createChild("div", "heap-snapshot-stats-size");
2141 swatchDiv.style.backgroundColor = color;
2143 swatchDiv.classList.add("heap-snapshot-stats-empty-swatch");
2144 nameDiv.textContent = name;
2145 sizeDiv.textContent = WebInspector.HeapSnapshotStatisticsView._valueFormatter(value);
2148 __proto__: WebInspector.VBox.prototype
2153 * @extends {WebInspector.View}
2154 * @param {?WebInspector.Target} target
2156 WebInspector.HeapAllocationStackView = function(target)
2158 WebInspector.View.call(this);
2159 this._target = target;;
2160 this._linkifier = new WebInspector.Linkifier();
2163 WebInspector.HeapAllocationStackView.prototype = {
2165 * @param {!WebInspector.HeapSnapshotProxy} snapshot
2166 * @param {number} snapshotNodeIndex
2168 setAllocatedObject: function(snapshot, snapshotNodeIndex)
2171 snapshot.allocationStack(snapshotNodeIndex, this._didReceiveAllocationStack.bind(this));
2176 this.element.removeChildren();
2177 this._linkifier.reset();
2181 * @param {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} frames
2183 _didReceiveAllocationStack: function(frames)
2186 var stackDiv = this.element.createChild("div", "no-heap-allocation-stack");
2187 stackDiv.createTextChild(WebInspector.UIString("Stack was not recorded for this object because it had been allocated before this profile recording started."));
2191 var stackDiv = this.element.createChild("div", "heap-allocation-stack");
2192 for (var i = 0; i < frames.length; i++) {
2193 var frame = frames[i];
2194 var frameDiv = stackDiv.createChild("div", "stack-frame");
2195 var name = frameDiv.createChild("div");
2196 name.textContent = frame.functionName;
2197 if (frame.scriptId) {
2198 var urlElement = this._linkifier.linkifyScriptLocation(this._target, String(frame.scriptId), frame.scriptName, frame.line - 1, frame.column - 1);
2199 frameDiv.appendChild(urlElement);
2204 __proto__: WebInspector.View.prototype