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.HeapProfileHeader} profile
36 WebInspector.HeapSnapshotView = function(profile)
38 WebInspector.VBox.call(this);
40 this.element.classList.add("heap-snapshot-view");
42 profile.profileType().addEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
43 profile.profileType().addEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
45 if (profile._profileType.id === WebInspector.TrackingHeapSnapshotProfileType.TypeId) {
46 this._trackingOverviewGrid = new WebInspector.HeapTrackingOverviewGrid(profile);
47 this._trackingOverviewGrid.addEventListener(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, this._onIdsRangeChanged.bind(this));
50 this._splitView = new WebInspector.SplitView(false, true, "heapSnapshotSplitViewState", 200, 200);
51 this._splitView.show(this.element);
53 this._containmentView = new WebInspector.VBox();
54 this._containmentView.setMinimumSize(50, 25);
55 this._containmentDataGrid = new WebInspector.HeapSnapshotContainmentDataGrid();
56 this._containmentDataGrid.show(this._containmentView.element);
57 this._containmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
59 this._statisticsView = new WebInspector.HeapSnapshotStatisticsView();
61 this._constructorsView = new WebInspector.VBox();
62 this._constructorsView.setMinimumSize(50, 25);
64 this._constructorsDataGrid = new WebInspector.HeapSnapshotConstructorsDataGrid();
65 this._constructorsDataGrid.show(this._constructorsView.element);
66 this._constructorsDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
68 this._diffView = new WebInspector.VBox();
69 this._diffView.setMinimumSize(50, 25);
71 this._diffDataGrid = new WebInspector.HeapSnapshotDiffDataGrid();
72 this._diffDataGrid.show(this._diffView.element);
73 this._diffDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
75 this._dominatorView = new WebInspector.VBox();
76 this._dominatorView.setMinimumSize(50, 25);
77 this._dominatorDataGrid = new WebInspector.HeapSnapshotDominatorsDataGrid();
78 this._dominatorDataGrid.show(this._dominatorView.element);
79 this._dominatorDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._selectionChanged, this);
81 if (WebInspector.experimentsSettings.allocationProfiler.isEnabled() && profile.profileType() === WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType) {
82 this._allocationView = new WebInspector.VBox();
83 this._allocationView.setMinimumSize(50, 25);
84 this._allocationDataGrid = new WebInspector.AllocationDataGrid();
85 this._allocationDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
86 this._allocationDataGrid.show(this._allocationView.element);
89 this._retainmentViewHeader = document.createElementWithClass("div", "retainers-view-header");
90 var retainingPathsTitleDiv = this._retainmentViewHeader.createChild("div", "title");
91 var retainingPathsTitle = retainingPathsTitleDiv.createChild("span");
92 retainingPathsTitle.textContent = WebInspector.UIString("Object's retaining tree");
93 this._splitView.hideDefaultResizer();
94 this._splitView.installResizer(this._retainmentViewHeader);
96 this._retainmentView = new WebInspector.VBox();
97 this._retainmentView.setMinimumSize(50, 21);
98 this._retainmentView.element.classList.add("retaining-paths-view");
99 this._retainmentView.element.appendChild(this._retainmentViewHeader);
100 this._retainmentDataGrid = new WebInspector.HeapSnapshotRetainmentDataGrid();
101 this._retainmentDataGrid.show(this._retainmentView.element);
102 this._retainmentDataGrid.addEventListener(WebInspector.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
103 this._retainmentDataGrid.reset();
105 this._perspectives = [];
106 this._perspectives.push(new WebInspector.HeapSnapshotView.SummaryPerspective());
107 if (profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
108 this._perspectives.push(new WebInspector.HeapSnapshotView.ComparisonPerspective());
109 this._perspectives.push(new WebInspector.HeapSnapshotView.ContainmentPerspective());
110 if (WebInspector.settings.showAdvancedHeapSnapshotProperties.get())
111 this._perspectives.push(new WebInspector.HeapSnapshotView.DominatorPerspective());
112 if (this._allocationView)
113 this._perspectives.push(new WebInspector.HeapSnapshotView.AllocationPerspective());
114 if (WebInspector.experimentsSettings.heapSnapshotStatistics.isEnabled())
115 this._perspectives.push(new WebInspector.HeapSnapshotView.StatisticsPerspective());
117 this._perspectiveSelect = new WebInspector.StatusBarComboBox(this._onSelectedPerspectiveChanged.bind(this));
118 for (var i = 0; i < this._perspectives.length; ++i)
119 this._perspectiveSelect.createOption(this._perspectives[i].title());
121 this._profile = profile;
123 this._baseSelect = new WebInspector.StatusBarComboBox(this._changeBase.bind(this));
124 this._baseSelect.visible = false;
125 this._updateBaseOptions();
127 this._filterSelect = new WebInspector.StatusBarComboBox(this._changeFilter.bind(this));
128 this._filterSelect.visible = false;
129 this._updateFilterOptions();
131 this._classNameFilter = new WebInspector.StatusBarInput("Class filter");
132 this._classNameFilter.visible = false;
133 this._classNameFilter.setOnChangeHandler(this._onClassFilterChanged.bind(this));
135 this._selectedSizeText = new WebInspector.StatusBarText("");
137 this._popoverHelper = new WebInspector.ObjectPopoverHelper(this.element, this._getHoverAnchor.bind(this), this._resolveObjectForPopover.bind(this), undefined, true);
139 this._currentPerspectiveIndex = 0;
140 this._currentPerspective = this._perspectives[0];
141 this._currentPerspective.activate(this);
142 this._dataGrid = this._currentPerspective.masterGrid(this);
143 this._dataGrid.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ResetFilter, this._onResetClassNameFilter, this);
150 * @param {string} title
152 WebInspector.HeapSnapshotView.Perspective = function(title)
157 WebInspector.HeapSnapshotView.Perspective.prototype = {
159 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
161 activate: function(heapSnapshotView) { },
164 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
166 deactivate: function(heapSnapshotView)
168 heapSnapshotView._baseSelect.visible = false;
169 heapSnapshotView._filterSelect.visible = false;
170 heapSnapshotView._classNameFilter.visible = false;
171 if (heapSnapshotView._trackingOverviewGrid)
172 heapSnapshotView._trackingOverviewGrid.detach();
173 if (heapSnapshotView._allocationView)
174 heapSnapshotView._allocationView.detach();
175 if (heapSnapshotView._statisticsView)
176 heapSnapshotView._statisticsView.detach();
178 heapSnapshotView._splitView.detach();
179 heapSnapshotView._splitView.detachChildViews();
183 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
184 * @return {?WebInspector.DataGrid}
186 masterGrid: function(heapSnapshotView)
202 supportsSearch: function()
210 * @extends {WebInspector.HeapSnapshotView.Perspective}
212 WebInspector.HeapSnapshotView.SummaryPerspective = function()
214 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Summary"));
217 WebInspector.HeapSnapshotView.SummaryPerspective.prototype = {
220 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
222 activate: function(heapSnapshotView)
224 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
225 heapSnapshotView._retainmentView.show(heapSnapshotView._splitView.sidebarElement());
226 heapSnapshotView._splitView.show(heapSnapshotView.element);
227 heapSnapshotView._filterSelect.visible = true;
228 heapSnapshotView._classNameFilter.visible = true;
229 if (heapSnapshotView._trackingOverviewGrid) {
230 heapSnapshotView._trackingOverviewGrid.show(heapSnapshotView.element, heapSnapshotView._splitView.element);
231 heapSnapshotView._trackingOverviewGrid.update();
232 heapSnapshotView._trackingOverviewGrid._updateGrid();
238 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
239 * @return {?WebInspector.DataGrid}
241 masterGrid: function(heapSnapshotView)
243 return heapSnapshotView._constructorsDataGrid;
250 supportsSearch: function()
255 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
260 * @extends {WebInspector.HeapSnapshotView.Perspective}
262 WebInspector.HeapSnapshotView.ComparisonPerspective = function()
264 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Comparison"));
267 WebInspector.HeapSnapshotView.ComparisonPerspective.prototype = {
270 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
272 activate: function(heapSnapshotView)
274 heapSnapshotView._diffView.show(heapSnapshotView._splitView.mainElement());
275 heapSnapshotView._retainmentView.show(heapSnapshotView._splitView.sidebarElement());
276 heapSnapshotView._splitView.show(heapSnapshotView.element);
277 heapSnapshotView._baseSelect.visible = true;
278 heapSnapshotView._classNameFilter.visible = true;
283 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
284 * @return {?WebInspector.DataGrid}
286 masterGrid: function(heapSnapshotView)
288 return heapSnapshotView._diffDataGrid;
295 supportsSearch: function()
300 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
305 * @extends {WebInspector.HeapSnapshotView.Perspective}
307 WebInspector.HeapSnapshotView.ContainmentPerspective = function()
309 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Containment"));
312 WebInspector.HeapSnapshotView.ContainmentPerspective.prototype = {
315 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
317 activate: function(heapSnapshotView)
319 heapSnapshotView._containmentView.show(heapSnapshotView._splitView.mainElement());
320 heapSnapshotView._retainmentView.show(heapSnapshotView._splitView.sidebarElement());
321 heapSnapshotView._splitView.show(heapSnapshotView.element);
326 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
327 * @return {?WebInspector.DataGrid}
329 masterGrid: function(heapSnapshotView)
331 return heapSnapshotView._containmentDataGrid;
333 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
338 * @extends {WebInspector.HeapSnapshotView.Perspective}
340 WebInspector.HeapSnapshotView.DominatorPerspective = function()
342 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Dominators"));
345 WebInspector.HeapSnapshotView.DominatorPerspective.prototype = {
348 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
350 activate: function(heapSnapshotView)
352 heapSnapshotView._dominatorView.show(heapSnapshotView._splitView.mainElement());
353 heapSnapshotView._retainmentView.show(heapSnapshotView._splitView.sidebarElement());
354 heapSnapshotView._splitView.show(heapSnapshotView.element);
359 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
360 * @return {?WebInspector.DataGrid}
362 masterGrid: function(heapSnapshotView)
364 return heapSnapshotView._dominatorDataGrid;
367 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
372 * @extends {WebInspector.HeapSnapshotView.Perspective}
374 WebInspector.HeapSnapshotView.AllocationPerspective = function()
376 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Allocation"));
377 this._allocationSplitView = new WebInspector.SplitView(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
380 WebInspector.HeapSnapshotView.AllocationPerspective.prototype = {
383 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
385 activate: function(heapSnapshotView)
387 heapSnapshotView._allocationView.show(this._allocationSplitView.mainElement());
388 heapSnapshotView._constructorsView.show(heapSnapshotView._splitView.mainElement());
389 heapSnapshotView._retainmentView.show(heapSnapshotView._splitView.sidebarElement());
390 heapSnapshotView._splitView.show(this._allocationSplitView.sidebarElement());
391 this._allocationSplitView.show(heapSnapshotView.element);
393 heapSnapshotView._constructorsDataGrid.clear();
394 var selectedNode = heapSnapshotView._allocationDataGrid.selectedNode;
396 heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
401 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
403 deactivate: function(heapSnapshotView)
405 this._allocationSplitView.detach();
406 WebInspector.HeapSnapshotView.Perspective.prototype.deactivate.call(this, heapSnapshotView);
411 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
412 * @return {?WebInspector.DataGrid}
414 masterGrid: function(heapSnapshotView)
416 return heapSnapshotView._allocationDataGrid;
419 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
424 * @extends {WebInspector.HeapSnapshotView.Perspective}
426 WebInspector.HeapSnapshotView.StatisticsPerspective = function()
428 WebInspector.HeapSnapshotView.Perspective.call(this, WebInspector.UIString("Statistics"));
431 WebInspector.HeapSnapshotView.StatisticsPerspective.prototype = {
434 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
436 activate: function(heapSnapshotView)
438 heapSnapshotView._statisticsView.show(heapSnapshotView.element);
443 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
444 * @return {?WebInspector.DataGrid}
446 masterGrid: function(heapSnapshotView)
451 __proto__: WebInspector.HeapSnapshotView.Perspective.prototype
455 WebInspector.HeapSnapshotView.prototype = {
456 _refreshView: function()
458 this._profile.load(profileCallback.bind(this));
461 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
462 * @this {WebInspector.HeapSnapshotView}
464 function profileCallback(heapSnapshotProxy)
466 heapSnapshotProxy.getStatistics(this._gotStatistics.bind(this));
467 var list = this._profiles();
468 var profileIndex = list.indexOf(this._profile);
469 this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
470 this._dataGrid.setDataSource(heapSnapshotProxy);
471 if (this._trackingOverviewGrid)
472 this._trackingOverviewGrid._updateGrid();
477 * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
479 _gotStatistics: function(statistics) {
480 this._statisticsView.setTotal(statistics.total);
481 this._statisticsView.addRecord(statistics.code, WebInspector.UIString("Code"), "#f77");
482 this._statisticsView.addRecord(statistics.strings, WebInspector.UIString("Strings"), "#5e5");
483 this._statisticsView.addRecord(statistics.jsArrays, WebInspector.UIString("JS Arrays"), "#7af");
484 this._statisticsView.addRecord(statistics.native, WebInspector.UIString("Typed Arrays"), "#fc5");
485 this._statisticsView.addRecord(statistics.total, WebInspector.UIString("Total"));
488 _onIdsRangeChanged: function(event)
490 var minId = event.data.minId;
491 var maxId = event.data.maxId;
492 this._selectedSizeText.setText(WebInspector.UIString("Selected size: %s", Number.bytesToString(event.data.size)));
493 if (this._constructorsDataGrid.snapshot)
494 this._constructorsDataGrid.setSelectionRange(minId, maxId);
499 var result = [this._perspectiveSelect.element, this._classNameFilter.element];
500 if (this._profile.profileType() !== WebInspector.ProfileTypeRegistry.instance.trackingHeapSnapshotProfileType)
501 result.push(this._baseSelect.element, this._filterSelect.element);
502 result.push(this._selectedSizeText.element);
508 // FIXME: load base and current snapshots in parallel
509 this._profile.load(profileCallback.bind(this));
512 * @this {WebInspector.HeapSnapshotView}
514 function profileCallback() {
515 this._profile._wasShown();
516 if (this._baseProfile)
517 this._baseProfile.load(function() { });
523 this._currentSearchResultIndex = -1;
524 this._popoverHelper.hidePopover();
525 if (this.helpPopover && this.helpPopover.isShowing())
526 this.helpPopover.hide();
529 searchCanceled: function()
531 if (this._searchResults) {
532 for (var i = 0; i < this._searchResults.length; ++i) {
533 var node = this._searchResults[i].node;
534 delete node._searchMatched;
539 delete this._searchFinishedCallback;
540 this._currentSearchResultIndex = -1;
541 this._searchResults = [];
545 * @param {string} query
546 * @param {function(!WebInspector.View, number)} finishedCallback
548 performSearch: function(query, finishedCallback)
550 // Call searchCanceled since it will reset everything we need before doing a new search.
551 this.searchCanceled();
553 query = query.trim();
557 if (!this._currentPerspective.supportsSearch())
561 * @param {boolean} found
562 * @this {WebInspector.HeapSnapshotView}
564 function didHighlight(found)
566 finishedCallback(this, found ? 1 : 0);
569 if (query.charAt(0) === "@") {
570 var snapshotNodeId = parseInt(query.substring(1), 10);
571 if (!isNaN(snapshotNodeId))
572 this._dataGrid.highlightObjectByHeapSnapshotId(String(snapshotNodeId), didHighlight.bind(this));
574 finishedCallback(this, 0);
578 this._searchFinishedCallback = finishedCallback;
579 var nameRegExp = createPlainTextSearchRegex(query, "i");
581 function matchesByName(gridNode) {
582 return ("_name" in gridNode) && nameRegExp.test(gridNode._name);
585 function matchesQuery(gridNode)
587 delete gridNode._searchMatched;
588 if (matchesByName(gridNode)) {
589 gridNode._searchMatched = true;
596 var current = this._dataGrid.rootNode().children[0];
600 // Restrict to type nodes and instances.
604 if (matchesQuery(current))
605 this._searchResults.push({ node: current });
606 current = current.traverseNextNode(false, null, (depth >= maxDepth), info);
607 depth += info.depthChange;
610 finishedCallback(this, this._searchResults.length);
613 jumpToFirstSearchResult: function()
615 if (!this._searchResults || !this._searchResults.length)
617 this._currentSearchResultIndex = 0;
618 this._jumpToSearchResult(this._currentSearchResultIndex);
621 jumpToLastSearchResult: function()
623 if (!this._searchResults || !this._searchResults.length)
625 this._currentSearchResultIndex = (this._searchResults.length - 1);
626 this._jumpToSearchResult(this._currentSearchResultIndex);
629 jumpToNextSearchResult: function()
631 if (!this._searchResults || !this._searchResults.length)
633 if (++this._currentSearchResultIndex >= this._searchResults.length)
634 this._currentSearchResultIndex = 0;
635 this._jumpToSearchResult(this._currentSearchResultIndex);
638 jumpToPreviousSearchResult: function()
640 if (!this._searchResults || !this._searchResults.length)
642 if (--this._currentSearchResultIndex < 0)
643 this._currentSearchResultIndex = (this._searchResults.length - 1);
644 this._jumpToSearchResult(this._currentSearchResultIndex);
650 showingFirstSearchResult: function()
652 return (this._currentSearchResultIndex === 0);
658 showingLastSearchResult: function()
660 return (this._searchResults && this._currentSearchResultIndex === (this._searchResults.length - 1));
666 currentSearchResultIndex: function() {
667 return this._currentSearchResultIndex;
670 _jumpToSearchResult: function(index)
672 var searchResult = this._searchResults[index];
676 var node = searchResult.node;
677 node.revealAndSelect();
680 refreshVisibleData: function()
684 var child = this._dataGrid.rootNode().children[0];
687 child = child.traverseNextNode(false, null, true);
691 _changeBase: function()
693 if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()])
696 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
697 var dataGrid = /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid);
698 // Change set base data source only if main data source is already set.
699 if (dataGrid.snapshot)
700 this._baseProfile.load(dataGrid.setBaseDataSource.bind(dataGrid));
702 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
705 // The current search needs to be performed again. First negate out previous match
706 // count by calling the search finished callback with a negative number of matches.
707 // Then perform the search again with the same query and callback.
708 this._searchFinishedCallback(this, -this._searchResults.length);
709 this.performSearch(this.currentQuery, this._searchFinishedCallback);
712 _changeFilter: function()
714 var profileIndex = this._filterSelect.selectedIndex() - 1;
715 this._dataGrid.filterSelectIndexChanged(this._profiles(), profileIndex);
717 WebInspector.notifications.dispatchEventToListeners(WebInspector.UserMetrics.UserAction, {
718 action: WebInspector.UserMetrics.UserActionNames.HeapSnapshotFilterChanged,
719 label: this._filterSelect.selectedOption().label
722 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
725 // The current search needs to be performed again. First negate out previous match
726 // count by calling the search finished callback with a negative number of matches.
727 // Then perform the search again with the same query and callback.
728 this._searchFinishedCallback(this, -this._searchResults.length);
729 this.performSearch(this.currentQuery, this._searchFinishedCallback);
733 * @param {string} value
735 _onClassFilterChanged: function(value)
737 this._dataGrid.changeNameFilter(value);
740 _onResetClassNameFilter: function()
742 this._classNameFilter.setValue("");
746 * @return {!Array.<!WebInspector.ProfileHeader>}
748 _profiles: function()
750 return this._profile.profileType().getProfiles();
754 * @param {!WebInspector.ContextMenu} contextMenu
755 * @param {?Event} event
757 populateContextMenu: function(contextMenu, event)
760 this._dataGrid.populateContextMenu(contextMenu, event);
763 _selectionChanged: function(event)
765 var selectedNode = event.target.selectedNode;
766 this._setRetainmentDataGridSource(selectedNode);
767 this._inspectedObjectChanged(event);
770 _onSelectAllocationNode: function(event)
772 var selectedNode = event.target.selectedNode;
773 this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
776 _inspectedObjectChanged: function(event)
778 var selectedNode = event.target.selectedNode;
779 if (!this._profile.fromFile() && selectedNode instanceof WebInspector.HeapSnapshotGenericObjectNode)
780 ConsoleAgent.addInspectedHeapObject(selectedNode.snapshotNodeId);
784 * @param {?WebInspector.HeapSnapshotGridNode} nodeItem
786 _setRetainmentDataGridSource: function(nodeItem)
788 var dataSource = nodeItem && nodeItem.retainersDataSource();
790 this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
792 this._retainmentDataGrid.reset();
796 * @param {string} perspectiveTitle
797 * @param {function()} callback
799 _changePerspectiveAndWait: function(perspectiveTitle, callback)
801 var perspectiveIndex = null;
802 for (var i = 0; i < this._perspectives.length; ++i) {
803 if (this._perspectives[i].title() === perspectiveTitle) {
804 perspectiveIndex = i;
808 if (this._currentPerspectiveIndex === perspectiveIndex || perspectiveIndex === null) {
809 setTimeout(callback, 0);
814 * @this {WebInspector.HeapSnapshotView}
816 function dataGridContentShown(event)
818 var dataGrid = event.data;
819 dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
820 if (dataGrid === this._dataGrid)
823 this._perspectives[perspectiveIndex].masterGrid(this).addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ContentShown, dataGridContentShown, this);
825 this._perspectiveSelect.setSelectedIndex(perspectiveIndex);
826 this._changePerspective(perspectiveIndex);
829 _updateDataSourceAndView: function()
831 var dataGrid = this._dataGrid;
832 if (!dataGrid || dataGrid.snapshot)
835 this._profile.load(didLoadSnapshot.bind(this));
838 * @this {WebInspector.HeapSnapshotView}
840 function didLoadSnapshot(snapshotProxy)
842 if (this._dataGrid !== dataGrid)
844 if (dataGrid.snapshot !== snapshotProxy)
845 dataGrid.setDataSource(snapshotProxy);
846 if (dataGrid === this._diffDataGrid) {
847 if (!this._baseProfile)
848 this._baseProfile = this._profiles()[this._baseSelect.selectedIndex()];
849 this._baseProfile.load(didLoadBaseSnaphot.bind(this));
854 * @this {WebInspector.HeapSnapshotView}
856 function didLoadBaseSnaphot(baseSnapshotProxy)
858 if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy)
859 this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
863 _onSelectedPerspectiveChanged: function(event)
865 this._changePerspective(event.target.selectedIndex);
866 // FIXME: This is needed by CodeSchool extension.
867 this._onSelectedViewChanged(event);
870 _onSelectedViewChanged: function(event)
874 _changePerspective: function(selectedIndex)
876 if (selectedIndex === this._currentPerspectiveIndex)
880 this._dataGrid.removeEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ResetFilter, this._onResetClassNameFilter, this);
882 this._currentPerspectiveIndex = selectedIndex;
884 this._currentPerspective.deactivate(this);
885 var perspective = this._perspectives[selectedIndex];
886 this._currentPerspective = perspective;
887 this._dataGrid = perspective.masterGrid(this);
888 perspective.activate(this);
890 this.refreshVisibleData();
891 if (this._dataGrid) {
892 this._dataGrid.addEventListener(WebInspector.HeapSnapshotSortableDataGrid.Events.ResetFilter, this._onResetClassNameFilter, this);
893 this._dataGrid.updateWidths();
896 this._updateDataSourceAndView();
898 if (!this.currentQuery || !this._searchFinishedCallback || !this._searchResults)
901 // The current search needs to be performed again. First negate out previous match
902 // count by calling the search finished callback with a negative number of matches.
903 // Then perform the search again the with same query and callback.
904 this._searchFinishedCallback(this, -this._searchResults.length);
905 this.performSearch(this.currentQuery, this._searchFinishedCallback);
909 * @param {string} perspectiveName
910 * @param {number} snapshotObjectId
912 highlightLiveObject: function(perspectiveName, snapshotObjectId)
914 this._changePerspectiveAndWait(perspectiveName, didChangePerspective.bind(this));
917 * @this {WebInspector.HeapSnapshotView}
919 function didChangePerspective()
921 this._dataGrid.highlightObjectByHeapSnapshotId(snapshotObjectId, didHighlightObject);
924 function didHighlightObject(found)
927 WebInspector.console.log("Cannot find corresponding heap snapshot node", WebInspector.ConsoleMessage.MessageLevel.Error, true);
931 _getHoverAnchor: function(target)
933 var span = target.enclosingNodeOrSelfWithNodeName("span");
936 var row = target.enclosingNodeOrSelfWithNodeName("tr");
939 span.node = row._dataGridNode;
943 _resolveObjectForPopover: function(element, showCallback, objectGroupName)
945 if (this._profile.fromFile())
947 element.node.queryObjectContent(showCallback, objectGroupName);
950 _updateBaseOptions: function()
952 var list = this._profiles();
953 // We're assuming that snapshots can only be added.
954 if (this._baseSelect.size() === list.length)
957 for (var i = this._baseSelect.size(), n = list.length; i < n; ++i) {
958 var title = list[i].title;
959 this._baseSelect.createOption(title);
963 _updateFilterOptions: function()
965 var list = this._profiles();
966 // We're assuming that snapshots can only be added.
967 if (this._filterSelect.size() - 1 === list.length)
970 if (!this._filterSelect.size())
971 this._filterSelect.createOption(WebInspector.UIString("All objects"));
973 for (var i = this._filterSelect.size() - 1, n = list.length; i < n; ++i) {
974 var title = list[i].title;
976 title = WebInspector.UIString("Objects allocated before %s", title);
978 title = WebInspector.UIString("Objects allocated between %s and %s", list[i - 1].title, title);
979 this._filterSelect.createOption(title);
983 _updateControls: function()
985 this._updateBaseOptions();
986 this._updateFilterOptions();
990 * @param {!WebInspector.Event} event
992 _onReceiveSnapshot: function(event)
994 this._updateControls();
998 * @param {!WebInspector.Event} event
1000 _onProfileHeaderRemoved: function(event)
1002 var profile = event.data;
1003 if (this._profile === profile) {
1005 this._profile.profileType().removeEventListener(WebInspector.HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
1006 this._profile.profileType().removeEventListener(WebInspector.ProfileType.Events.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
1008 this._updateControls();
1012 __proto__: WebInspector.VBox.prototype
1017 * @implements {HeapProfilerAgent.Dispatcher}
1019 WebInspector.HeapProfilerDispatcher = function()
1021 this._dispatchers = [];
1022 InspectorBackend.registerHeapProfilerDispatcher(this);
1025 WebInspector.HeapProfilerDispatcher.prototype = {
1027 * @param {!HeapProfilerAgent.Dispatcher} dispatcher
1029 register: function(dispatcher)
1031 this._dispatchers.push(dispatcher);
1034 _genericCaller: function(eventName)
1036 var args = Array.prototype.slice.call(arguments.callee.caller.arguments);
1037 for (var i = 0; i < this._dispatchers.length; ++i)
1038 this._dispatchers[i][eventName].apply(this._dispatchers[i], args);
1043 * @param {!Array.<number>} samples
1045 heapStatsUpdate: function(samples)
1047 this._genericCaller("heapStatsUpdate");
1052 * @param {number} lastSeenObjectId
1053 * @param {number} timestamp
1055 lastSeenObjectId: function(lastSeenObjectId, timestamp)
1057 this._genericCaller("lastSeenObjectId");
1062 * @param {string} chunk
1064 addHeapSnapshotChunk: function(chunk)
1066 this._genericCaller("addHeapSnapshotChunk");
1071 * @param {number} done
1072 * @param {number} total
1073 * @param {boolean=} finished
1075 reportHeapSnapshotProgress: function(done, total, finished)
1077 this._genericCaller("reportHeapSnapshotProgress");
1083 resetProfiles: function()
1085 this._genericCaller("resetProfiles");
1089 WebInspector.HeapProfilerDispatcher._dispatcher = new WebInspector.HeapProfilerDispatcher();
1093 * @extends {WebInspector.ProfileType}
1094 * @implements {HeapProfilerAgent.Dispatcher}
1095 * @param {string=} id
1096 * @param {string=} title
1098 WebInspector.HeapSnapshotProfileType = function(id, title)
1100 WebInspector.ProfileType.call(this, id || WebInspector.HeapSnapshotProfileType.TypeId, title || WebInspector.UIString("Take Heap Snapshot"));
1101 WebInspector.HeapProfilerDispatcher._dispatcher.register(this);
1104 WebInspector.HeapSnapshotProfileType.TypeId = "HEAP";
1105 WebInspector.HeapSnapshotProfileType.SnapshotReceived = "SnapshotReceived";
1107 WebInspector.HeapSnapshotProfileType.prototype = {
1112 fileExtension: function()
1114 return ".heapsnapshot";
1119 return WebInspector.UIString("Take heap snapshot.");
1126 isInstantProfile: function()
1135 buttonClicked: function()
1137 this._takeHeapSnapshot(function() {});
1138 WebInspector.userMetrics.ProfilesHeapProfileTaken.record();
1144 * @param {!Array.<number>} samples
1146 heapStatsUpdate: function(samples)
1152 * @param {number} lastSeenObjectId
1153 * @param {number} timestamp
1155 lastSeenObjectId: function(lastSeenObjectId, timestamp)
1161 return WebInspector.UIString("HEAP SNAPSHOTS");
1166 return WebInspector.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
1171 * @param {!string} title
1172 * @return {!WebInspector.ProfileHeader}
1174 createProfileLoadedFromFile: function(title)
1176 return new WebInspector.HeapProfileHeader(this, title);
1179 _takeHeapSnapshot: function(callback)
1181 if (this.profileBeingRecorded())
1183 this._profileBeingRecorded = new WebInspector.HeapProfileHeader(this);
1184 this.addProfile(this._profileBeingRecorded);
1185 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1188 * @param {?string} error
1189 * @this {WebInspector.HeapSnapshotProfileType}
1191 function didTakeHeapSnapshot(error)
1193 var profile = this._profileBeingRecorded;
1194 profile.title = WebInspector.UIString("Snapshot %d", profile.uid);
1195 profile._finishLoad();
1196 this._profileBeingRecorded = null;
1197 WebInspector.panels.profiles.showProfile(profile);
1200 HeapProfilerAgent.takeHeapSnapshot(true, didTakeHeapSnapshot.bind(this));
1205 * @param {string} chunk
1207 addHeapSnapshotChunk: function(chunk)
1209 if (!this.profileBeingRecorded())
1211 this.profileBeingRecorded().transferChunk(chunk);
1216 * @param {number} done
1217 * @param {number} total
1218 * @param {boolean=} finished
1220 reportHeapSnapshotProgress: function(done, total, finished)
1222 var profile = this.profileBeingRecorded();
1225 profile.updateStatus(WebInspector.UIString("%.0f%", (done / total) * 100), true);
1227 profile._prepareToLoad();
1233 resetProfiles: function()
1238 _snapshotReceived: function(profile)
1240 if (this._profileBeingRecorded === profile)
1241 this._profileBeingRecorded = null;
1242 this.dispatchEventToListeners(WebInspector.HeapSnapshotProfileType.SnapshotReceived, profile);
1245 __proto__: WebInspector.ProfileType.prototype
1251 * @extends {WebInspector.HeapSnapshotProfileType}
1253 WebInspector.TrackingHeapSnapshotProfileType = function()
1255 WebInspector.HeapSnapshotProfileType.call(this, WebInspector.TrackingHeapSnapshotProfileType.TypeId, WebInspector.UIString("Record Heap Allocations"));
1258 WebInspector.TrackingHeapSnapshotProfileType.TypeId = "HEAP-RECORD";
1260 WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate = "HeapStatsUpdate";
1261 WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted = "TrackingStarted";
1262 WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped = "TrackingStopped";
1264 WebInspector.TrackingHeapSnapshotProfileType.prototype = {
1268 * @param {!Array.<number>} samples
1270 heapStatsUpdate: function(samples)
1272 if (!this._profileSamples)
1275 for (var i = 0; i < samples.length; i += 3) {
1277 var count = samples[i+1];
1278 var size = samples[i+2];
1279 this._profileSamples.sizes[index] = size;
1280 if (!this._profileSamples.max[index])
1281 this._profileSamples.max[index] = size;
1287 * @param {number} lastSeenObjectId
1288 * @param {number} timestamp
1290 lastSeenObjectId: function(lastSeenObjectId, timestamp)
1292 var profileSamples = this._profileSamples;
1293 if (!profileSamples)
1295 var currentIndex = Math.max(profileSamples.ids.length, profileSamples.max.length - 1);
1296 profileSamples.ids[currentIndex] = lastSeenObjectId;
1297 if (!profileSamples.max[currentIndex]) {
1298 profileSamples.max[currentIndex] = 0;
1299 profileSamples.sizes[currentIndex] = 0;
1301 profileSamples.timestamps[currentIndex] = timestamp;
1302 if (profileSamples.totalTime < timestamp - profileSamples.timestamps[0])
1303 profileSamples.totalTime *= 2;
1304 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._profileSamples);
1305 this._profileBeingRecorded.updateStatus(null, true);
1312 hasTemporaryView: function()
1319 return this._recording ? WebInspector.UIString("Stop recording heap profile.") : WebInspector.UIString("Start recording heap profile.");
1326 isInstantProfile: function()
1335 buttonClicked: function()
1337 return this._toggleRecording();
1340 _startRecordingProfile: function()
1342 if (this.profileBeingRecorded())
1344 this._addNewProfile();
1345 HeapProfilerAgent.startTrackingHeapObjects(WebInspector.experimentsSettings.allocationProfiler.isEnabled());
1348 _addNewProfile: function()
1350 this._profileBeingRecorded = new WebInspector.HeapProfileHeader(this);
1351 this._lastSeenIndex = -1;
1352 this._profileSamples = {
1359 this._profileBeingRecorded._profileSamples = this._profileSamples;
1360 this._recording = true;
1361 this.addProfile(this._profileBeingRecorded);
1362 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Recording\u2026"));
1363 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStarted);
1366 _stopRecordingProfile: function()
1368 this._profileBeingRecorded.updateStatus(WebInspector.UIString("Snapshotting\u2026"));
1370 * @param {?string} error
1371 * @this {WebInspector.HeapSnapshotProfileType}
1373 function didTakeHeapSnapshot(error)
1375 var profile = this._profileBeingRecorded;
1378 profile._finishLoad();
1379 this._profileSamples = null;
1380 this._profileBeingRecorded = null;
1381 WebInspector.panels.profiles.showProfile(profile);
1384 HeapProfilerAgent.stopTrackingHeapObjects(true, didTakeHeapSnapshot.bind(this));
1385 this._recording = false;
1386 this.dispatchEventToListeners(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped);
1389 _toggleRecording: function()
1391 if (this._recording)
1392 this._stopRecordingProfile();
1394 this._startRecordingProfile();
1395 return this._recording;
1400 return WebInspector.UIString("HEAP TIMELINES");
1405 return WebInspector.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
1411 resetProfiles: function()
1413 var wasRecording = this._recording;
1414 // Clear current profile to avoid stopping backend.
1415 this._profileBeingRecorded = null;
1416 WebInspector.HeapSnapshotProfileType.prototype.resetProfiles.call(this);
1417 this._profileSamples = null;
1418 this._lastSeenIndex = -1;
1420 this._addNewProfile();
1426 profileBeingRecordedRemoved: function()
1428 this._stopRecordingProfile();
1429 this._profileSamples = null;
1432 __proto__: WebInspector.HeapSnapshotProfileType.prototype
1437 * @extends {WebInspector.ProfileHeader}
1438 * @param {!WebInspector.HeapSnapshotProfileType} type
1439 * @param {string=} title
1441 WebInspector.HeapProfileHeader = function(type, title)
1443 WebInspector.ProfileHeader.call(this, type, title || WebInspector.UIString("Snapshot %d", type._nextProfileUid));
1444 this.maxJSObjectId = -1;
1446 * @type {?WebInspector.HeapSnapshotWorkerProxy}
1448 this._workerProxy = null;
1450 * @type {?WebInspector.OutputStream}
1452 this._receiver = null;
1454 * @type {?WebInspector.HeapSnapshotProxy}
1456 this._snapshotProxy = null;
1458 * @type {?Array.<function(!WebInspector.HeapSnapshotProxy)>}
1460 this._loadCallbacks = [];
1461 this._totalNumberOfChunks = 0;
1462 this._bufferedWriter = null;
1465 WebInspector.HeapProfileHeader.prototype = {
1468 * @return {!WebInspector.ProfileSidebarTreeElement}
1470 createSidebarTreeElement: function()
1472 return new WebInspector.ProfileSidebarTreeElement(this, "heap-snapshot-sidebar-tree-item");
1477 * @return {!WebInspector.HeapSnapshotView}
1479 createView: function()
1481 return new WebInspector.HeapSnapshotView(this);
1486 * @param {function(!WebInspector.HeapSnapshotProxy):void} callback
1488 load: function(callback)
1490 if (this.uid === -1)
1492 if (this._snapshotProxy) {
1493 callback(this._snapshotProxy);
1496 this._loadCallbacks.push(callback);
1499 _prepareToLoad: function()
1501 console.assert(!this._receiver, "Already loading");
1502 this._setupWorker();
1503 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1506 _finishLoad: function()
1508 if (!this._wasDisposed)
1509 this._receiver.close(function() {});
1510 if (this._bufferedWriter) {
1511 this._bufferedWriter.close(this._didWriteToTempFile.bind(this));
1512 this._bufferedWriter = null;
1516 _didWriteToTempFile: function(tempFile)
1518 if (this._wasDisposed) {
1523 this._tempFile = tempFile;
1525 this._failedToCreateTempFile = true;
1526 if (this._onTempFileReady) {
1527 this._onTempFileReady();
1528 this._onTempFileReady = null;
1532 _setupWorker: function()
1535 * @this {WebInspector.HeapProfileHeader}
1537 function setProfileWait(event)
1539 this.updateStatus(null, event.data);
1541 console.assert(!this._workerProxy, "HeapSnapshotWorkerProxy already exists");
1542 this._workerProxy = new WebInspector.HeapSnapshotWorkerProxy(this._handleWorkerEvent.bind(this));
1543 this._workerProxy.addEventListener("wait", setProfileWait, this);
1544 this._receiver = this._workerProxy.createLoader(this.uid, this._snapshotReceived.bind(this));
1548 * @param {string} eventName
1551 _handleWorkerEvent: function(eventName, data)
1553 if (WebInspector.HeapSnapshotProgressEvent.Update !== eventName)
1555 var subtitle = /** @type {string} */ (data);
1556 this.updateStatus(subtitle);
1564 if (this._workerProxy)
1565 this._workerProxy.dispose();
1566 this.removeTempFile();
1567 this._wasDisposed = true;
1570 _didCompleteSnapshotTransfer: function()
1572 if (!this._snapshotProxy)
1574 this.updateStatus(Number.bytesToString(this._snapshotProxy.totalSize), false);
1578 * @param {string} chunk
1580 transferChunk: function(chunk)
1582 if (!this._bufferedWriter)
1583 this._bufferedWriter = new WebInspector.BufferedTempFileWriter("heap-profiler", this.uid);
1584 this._bufferedWriter.write(chunk);
1586 ++this._totalNumberOfChunks;
1587 this._receiver.write(chunk, function() {});
1590 _snapshotReceived: function(snapshotProxy)
1592 if (this._wasDisposed)
1594 this._receiver = null;
1595 this._snapshotProxy = snapshotProxy;
1596 this.maxJSObjectId = snapshotProxy.maxJSObjectId();
1597 this._didCompleteSnapshotTransfer();
1598 this._workerProxy.startCheckingForLongRunningCalls();
1599 this.notifySnapshotReceived();
1602 notifySnapshotReceived: function()
1604 for (var i = 0; i < this._loadCallbacks.length; i++)
1605 this._loadCallbacks[i](this._snapshotProxy);
1606 this._loadCallbacks = null;
1607 this._profileType._snapshotReceived(this);
1608 if (this.canSaveToFile())
1609 this.dispatchEventToListeners(WebInspector.ProfileHeader.Events.ProfileReceived);
1612 // Hook point for tests.
1613 _wasShown: function()
1621 canSaveToFile: function()
1623 return !this.fromFile() && this._snapshotProxy;
1629 saveToFile: function()
1631 var fileOutputStream = new WebInspector.FileOutputStream();
1634 * @param {boolean} accepted
1635 * @this {WebInspector.HeapProfileHeader}
1637 function onOpen(accepted)
1641 if (this._failedToCreateTempFile) {
1642 WebInspector.console.log("Failed to open temp file with heap snapshot",
1643 WebInspector.ConsoleMessage.MessageLevel.Error);
1644 fileOutputStream.close();
1645 } else if (this._tempFile) {
1646 var delegate = new WebInspector.SaveSnapshotOutputStreamDelegate(this);
1647 this._tempFile.writeToOutputSteam(fileOutputStream, delegate);
1649 this._onTempFileReady = onOpen.bind(this, accepted);
1650 this._updateSaveProgress(0, 1);
1653 this._fileName = this._fileName || "Heap-" + new Date().toISO8601Compact() + this._profileType.fileExtension();
1654 fileOutputStream.open(this._fileName, onOpen.bind(this));
1657 _updateSaveProgress: function(value, total)
1659 var percentValue = ((total ? (value / total) : 0) * 100).toFixed(0);
1660 this.updateStatus(WebInspector.UIString("Saving\u2026 %d\%", percentValue));
1665 * @param {!File} file
1667 loadFromFile: function(file)
1669 this.updateStatus(WebInspector.UIString("Loading\u2026"), true);
1670 this._setupWorker();
1671 var delegate = new WebInspector.HeapSnapshotLoadFromFileDelegate(this);
1672 var fileReader = this._createFileReader(file, delegate);
1673 fileReader.start(this._receiver);
1676 _createFileReader: function(file, delegate)
1678 return new WebInspector.ChunkedFileReader(file, 10000000, delegate);
1681 __proto__: WebInspector.ProfileHeader.prototype
1686 * @implements {WebInspector.OutputStreamDelegate}
1688 WebInspector.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader)
1690 this._snapshotHeader = snapshotHeader;
1693 WebInspector.HeapSnapshotLoadFromFileDelegate.prototype = {
1694 onTransferStarted: function()
1699 * @param {!WebInspector.ChunkedReader} reader
1701 onChunkTransferred: function(reader)
1705 onTransferFinished: function()
1710 * @param {!WebInspector.ChunkedReader} reader
1712 onError: function (reader, e)
1715 switch(e.target.error.code) {
1716 case e.target.error.NOT_FOUND_ERR:
1717 subtitle = WebInspector.UIString("'%s' not found.", reader.fileName());
1719 case e.target.error.NOT_READABLE_ERR:
1720 subtitle = WebInspector.UIString("'%s' is not readable", reader.fileName());
1722 case e.target.error.ABORT_ERR:
1725 subtitle = WebInspector.UIString("'%s' error %d", reader.fileName(), e.target.error.code);
1727 this._snapshotHeader.updateStatus(subtitle);
1733 * @implements {WebInspector.OutputStreamDelegate}
1734 * @param {!WebInspector.HeapProfileHeader} profileHeader
1736 WebInspector.SaveSnapshotOutputStreamDelegate = function(profileHeader)
1738 this._profileHeader = profileHeader;
1741 WebInspector.SaveSnapshotOutputStreamDelegate.prototype = {
1742 onTransferStarted: function()
1744 this._profileHeader._updateSaveProgress(0, 1);
1747 onTransferFinished: function()
1749 this._profileHeader._didCompleteSnapshotTransfer();
1753 * @param {!WebInspector.ChunkedReader} reader
1755 onChunkTransferred: function(reader)
1757 this._profileHeader._updateSaveProgress(reader.loadedSize(), reader.fileSize());
1761 * @param {!WebInspector.ChunkedReader} reader
1763 onError: function(reader, event)
1765 WebInspector.console.log("Failed to read heap snapshot from temp file: " + event.message,
1766 WebInspector.ConsoleMessage.MessageLevel.Error);
1767 this.onTransferFinished();
1773 * @extends {WebInspector.VBox}
1774 * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
1776 WebInspector.HeapTrackingOverviewGrid = function(heapProfileHeader)
1778 WebInspector.VBox.call(this);
1779 this.registerRequiredCSS("flameChart.css");
1780 this.element.id = "heap-recording-view";
1781 this.element.classList.add("heap-tracking-overview");
1783 this._overviewContainer = this.element.createChild("div", "overview-container");
1784 this._overviewGrid = new WebInspector.OverviewGrid("heap-recording");
1785 this._overviewGrid.element.classList.add("fill");
1787 this._overviewCanvas = this._overviewContainer.createChild("canvas", "heap-recording-overview-canvas");
1788 this._overviewContainer.appendChild(this._overviewGrid.element);
1789 this._overviewCalculator = new WebInspector.HeapTrackingOverviewGrid.OverviewCalculator();
1790 this._overviewGrid.addEventListener(WebInspector.OverviewGrid.Events.WindowChanged, this._onWindowChanged, this);
1792 this._profileSamples = heapProfileHeader._profileSamples;
1793 if (heapProfileHeader.profileType().profileBeingRecorded() === heapProfileHeader) {
1794 this._profileType = heapProfileHeader._profileType;
1795 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1796 this._profileType.addEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1798 var timestamps = this._profileSamples.timestamps;
1799 var totalTime = this._profileSamples.totalTime;
1800 this._windowLeft = 0.0;
1801 this._windowRight = totalTime && timestamps.length ? (timestamps[timestamps.length - 1] - timestamps[0]) / totalTime : 1.0;
1802 this._overviewGrid.setWindow(this._windowLeft, this._windowRight);
1803 this._yScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1804 this._xScale = new WebInspector.HeapTrackingOverviewGrid.SmoothScale();
1807 WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged = "IdsRangeChanged";
1809 WebInspector.HeapTrackingOverviewGrid.prototype = {
1810 _onStopTracking: function(event)
1812 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
1813 this._profileType.removeEventListener(WebInspector.TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
1816 _onHeapStatsUpdate: function(event)
1818 this._profileSamples = event.data;
1819 this._scheduleUpdate();
1823 * @param {number} width
1824 * @param {number} height
1826 _drawOverviewCanvas: function(width, height)
1828 if (!this._profileSamples)
1830 var profileSamples = this._profileSamples;
1831 var sizes = profileSamples.sizes;
1832 var topSizes = profileSamples.max;
1833 var timestamps = profileSamples.timestamps;
1834 var startTime = timestamps[0];
1835 var endTime = timestamps[timestamps.length - 1];
1837 var scaleFactor = this._xScale.nextScale(width / profileSamples.totalTime);
1840 * @param {!Array.<number>} sizes
1841 * @param {function(number, number):void} callback
1843 function aggregateAndCall(sizes, callback)
1847 for (var i = 1; i < timestamps.length; ++i) {
1848 var x = Math.floor((timestamps[i] - startTime) * scaleFactor);
1849 if (x !== currentX) {
1851 callback(currentX, size);
1857 callback(currentX, size);
1862 * @param {number} size
1864 function maxSizeCallback(x, size)
1866 maxSize = Math.max(maxSize, size);
1869 aggregateAndCall(sizes, maxSizeCallback);
1871 var yScaleFactor = this._yScale.nextScale(maxSize ? height / (maxSize * 1.1) : 0.0);
1873 this._overviewCanvas.width = width * window.devicePixelRatio;
1874 this._overviewCanvas.height = height * window.devicePixelRatio;
1875 this._overviewCanvas.style.width = width + "px";
1876 this._overviewCanvas.style.height = height + "px";
1878 var context = this._overviewCanvas.getContext("2d");
1879 context.scale(window.devicePixelRatio, window.devicePixelRatio);
1881 context.beginPath();
1882 context.lineWidth = 2;
1883 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1884 var currentX = (endTime - startTime) * scaleFactor;
1885 context.moveTo(currentX, height - 1);
1886 context.lineTo(currentX, 0);
1888 context.closePath();
1892 var gridLabelHeight = 14;
1894 const maxGridValue = (height - gridLabelHeight) / yScaleFactor;
1895 // The round value calculation is a bit tricky, because
1896 // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
1897 // e.g. a round value 10KB is 10240 bytes.
1898 gridValue = Math.pow(1024, Math.floor(Math.log(maxGridValue) / Math.log(1024)));
1899 gridValue *= Math.pow(10, Math.floor(Math.log(maxGridValue / gridValue) / Math.LN10));
1900 if (gridValue * 5 <= maxGridValue)
1902 gridY = Math.round(height - gridValue * yScaleFactor - 0.5) + 0.5;
1903 context.beginPath();
1904 context.lineWidth = 1;
1905 context.strokeStyle = "rgba(0, 0, 0, 0.2)";
1906 context.moveTo(0, gridY);
1907 context.lineTo(width, gridY);
1909 context.closePath();
1914 * @param {number} size
1916 function drawBarCallback(x, size)
1918 context.moveTo(x, height - 1);
1919 context.lineTo(x, Math.round(height - size * yScaleFactor - 1));
1922 context.beginPath();
1923 context.lineWidth = 2;
1924 context.strokeStyle = "rgba(192, 192, 192, 0.6)";
1925 aggregateAndCall(topSizes, drawBarCallback);
1927 context.closePath();
1929 context.beginPath();
1930 context.lineWidth = 2;
1931 context.strokeStyle = "rgba(0, 0, 192, 0.8)";
1932 aggregateAndCall(sizes, drawBarCallback);
1934 context.closePath();
1937 var label = Number.bytesToString(gridValue);
1938 var labelPadding = 4;
1940 var labelY = gridY - 0.5;
1941 var labelWidth = 2 * labelPadding + context.measureText(label).width;
1942 context.beginPath();
1943 context.textBaseline = "bottom";
1944 context.font = "10px " + window.getComputedStyle(this.element, null).getPropertyValue("font-family");
1945 context.fillStyle = "rgba(255, 255, 255, 0.75)";
1946 context.fillRect(labelX, labelY - gridLabelHeight, labelWidth, gridLabelHeight);
1947 context.fillStyle = "rgb(64, 64, 64)";
1948 context.fillText(label, labelX + labelPadding, labelY);
1950 context.closePath();
1954 onResize: function()
1956 this._updateOverviewCanvas = true;
1957 this._scheduleUpdate();
1960 _onWindowChanged: function()
1962 if (!this._updateGridTimerId)
1963 this._updateGridTimerId = setTimeout(this._updateGrid.bind(this), 10);
1966 _scheduleUpdate: function()
1968 if (this._updateTimerId)
1970 this._updateTimerId = setTimeout(this.update.bind(this), 10);
1973 _updateBoundaries: function()
1975 this._windowLeft = this._overviewGrid.windowLeft();
1976 this._windowRight = this._overviewGrid.windowRight();
1977 this._windowWidth = this._windowRight - this._windowLeft;
1982 this._updateTimerId = null;
1983 if (!this.isShowing())
1985 this._updateBoundaries();
1986 this._overviewCalculator._updateBoundaries(this);
1987 this._overviewGrid.updateDividers(this._overviewCalculator);
1988 this._drawOverviewCanvas(this._overviewContainer.clientWidth, this._overviewContainer.clientHeight - 20);
1991 _updateGrid: function()
1993 this._updateGridTimerId = 0;
1994 this._updateBoundaries();
1995 var ids = this._profileSamples.ids;
1996 var timestamps = this._profileSamples.timestamps;
1997 var sizes = this._profileSamples.sizes;
1998 var startTime = timestamps[0];
1999 var totalTime = this._profileSamples.totalTime;
2000 var timeLeft = startTime + totalTime * this._windowLeft;
2001 var timeRight = startTime + totalTime * this._windowRight;
2003 var maxId = ids[ids.length - 1] + 1;
2005 for (var i = 0; i < timestamps.length; ++i) {
2008 if (timestamps[i] > timeRight)
2011 if (timestamps[i] < timeLeft) {
2018 this.dispatchEventToListeners(WebInspector.HeapTrackingOverviewGrid.IdsRangeChanged, {minId: minId, maxId: maxId, size: size});
2021 __proto__: WebInspector.VBox.prototype
2028 WebInspector.HeapTrackingOverviewGrid.SmoothScale = function()
2030 this._lastUpdate = 0;
2031 this._currentScale = 0.0;
2034 WebInspector.HeapTrackingOverviewGrid.SmoothScale.prototype = {
2036 * @param {number} target
2039 nextScale: function(target) {
2040 target = target || this._currentScale;
2041 if (this._currentScale) {
2042 var now = Date.now();
2043 var timeDeltaMs = now - this._lastUpdate;
2044 this._lastUpdate = now;
2045 var maxChangePerSec = 20;
2046 var maxChangePerDelta = Math.pow(maxChangePerSec, timeDeltaMs / 1000);
2047 var scaleChange = target / this._currentScale;
2048 this._currentScale *= Number.constrain(scaleChange, 1 / maxChangePerDelta, maxChangePerDelta);
2050 this._currentScale = target;
2051 return this._currentScale;
2058 * @implements {WebInspector.TimelineGrid.Calculator}
2060 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator = function()
2064 WebInspector.HeapTrackingOverviewGrid.OverviewCalculator.prototype = {
2068 paddingLeft: function()
2074 * @param {!WebInspector.HeapTrackingOverviewGrid} chart
2076 _updateBoundaries: function(chart)
2078 this._minimumBoundaries = 0;
2079 this._maximumBoundaries = chart._profileSamples.totalTime;
2080 this._xScaleFactor = chart._overviewContainer.clientWidth / this._maximumBoundaries;
2084 * @param {number} time
2087 computePosition: function(time)
2089 return (time - this._minimumBoundaries) * this._xScaleFactor;
2093 * @param {number} value
2094 * @param {number=} precision
2097 formatTime: function(value, precision)
2099 return Number.secondsToString(value / 1000, !!precision);
2105 maximumBoundary: function()
2107 return this._maximumBoundaries;
2113 minimumBoundary: function()
2115 return this._minimumBoundaries;
2121 zeroTime: function()
2123 return this._minimumBoundaries;
2129 boundarySpan: function()
2131 return this._maximumBoundaries - this._minimumBoundaries;
2138 * @extends {WebInspector.VBox}
2140 WebInspector.HeapSnapshotStatisticsView = function()
2142 WebInspector.VBox.call(this);
2143 this.setMinimumSize(50, 25);
2144 this._pieChart = new WebInspector.PieChart();
2145 this._pieChart.setSize(150);
2146 this.element.appendChild(this._pieChart.element);
2147 this._labels = this.element.createChild("div", "heap-snapshot-stats-legend");
2150 WebInspector.HeapSnapshotStatisticsView.prototype = {
2152 * @param {number} value
2154 setTotal: function(value)
2156 this._pieChart.setTotal(value);
2160 * @param {number} value
2161 * @param {string} name
2162 * @param {string=} color
2164 addRecord: function(value, name, color)
2167 this._pieChart.addSlice(value, color);
2169 var node = this._labels.createChild("div");
2170 var swatchDiv = node.createChild("div", "heap-snapshot-stats-swatch");
2171 var nameDiv = node.createChild("div", "heap-snapshot-stats-name");
2172 var sizeDiv = node.createChild("div", "heap-snapshot-stats-size");
2174 swatchDiv.style.backgroundColor = color;
2176 swatchDiv.classList.add("heap-snapshot-stats-empty-swatch");
2177 nameDiv.textContent = name;
2178 sizeDiv.textContent = WebInspector.UIString("%s KB", Number.withThousandsSeparator(Math.round(value / 1024)));
2181 __proto__: WebInspector.VBox.prototype