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 * @implements {WebInspector.Searchable}
35 * @extends {WebInspector.VBox}
36 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
37 * @param {!WebInspector.HeapProfileHeader} profile
39 WebInspector
.HeapSnapshotView = function(dataDisplayDelegate
, profile
)
41 WebInspector
.VBox
.call(this);
43 this.element
.classList
.add("heap-snapshot-view");
45 profile
.profileType().addEventListener(WebInspector
.HeapSnapshotProfileType
.SnapshotReceived
, this._onReceiveSnapshot
, this);
46 profile
.profileType().addEventListener(WebInspector
.ProfileType
.Events
.RemoveProfileHeader
, this._onProfileHeaderRemoved
, this);
48 var isHeapTimeline
= profile
.profileType().id
=== WebInspector
.TrackingHeapSnapshotProfileType
.TypeId
;
50 this._trackingOverviewGrid
= new WebInspector
.HeapTrackingOverviewGrid(profile
);
51 this._trackingOverviewGrid
.addEventListener(WebInspector
.HeapTrackingOverviewGrid
.IdsRangeChanged
, this._onIdsRangeChanged
.bind(this));
54 this._parentDataDisplayDelegate
= dataDisplayDelegate
;
56 this._searchableView
= new WebInspector
.SearchableView(this);
57 this._searchableView
.show(this.element
);
59 this._splitWidget
= new WebInspector
.SplitWidget(false, true, "heapSnapshotSplitViewState", 200, 200);
60 this._splitWidget
.show(this._searchableView
.element
);
62 this._containmentWidget
= new WebInspector
.VBox();
63 this._containmentWidget
.setMinimumSize(50, 25);
64 this._containmentDataGrid
= new WebInspector
.HeapSnapshotContainmentDataGrid(this);
65 this._containmentDataGrid
.show(this._containmentWidget
.element
);
66 this._containmentDataGrid
.addEventListener(WebInspector
.DataGrid
.Events
.SelectedNode
, this._selectionChanged
, this);
68 this._statisticsView
= new WebInspector
.HeapSnapshotStatisticsView();
70 this._constructorsWidget
= new WebInspector
.VBox();
71 this._constructorsWidget
.setMinimumSize(50, 25);
73 this._constructorsDataGrid
= new WebInspector
.HeapSnapshotConstructorsDataGrid(this);
74 this._constructorsDataGrid
.show(this._constructorsWidget
.element
);
75 this._constructorsDataGrid
.addEventListener(WebInspector
.DataGrid
.Events
.SelectedNode
, this._selectionChanged
, this);
77 this._diffWidget
= new WebInspector
.VBox();
78 this._diffWidget
.setMinimumSize(50, 25);
80 this._diffDataGrid
= new WebInspector
.HeapSnapshotDiffDataGrid(this);
81 this._diffDataGrid
.show(this._diffWidget
.element
);
82 this._diffDataGrid
.addEventListener(WebInspector
.DataGrid
.Events
.SelectedNode
, this._selectionChanged
, this);
84 if (isHeapTimeline
&& WebInspector
.moduleSetting("recordAllocationStacks").get()) {
85 this._allocationWidget
= new WebInspector
.VBox();
86 this._allocationWidget
.setMinimumSize(50, 25);
87 this._allocationDataGrid
= new WebInspector
.AllocationDataGrid(profile
.target() , this);
88 this._allocationDataGrid
.addEventListener(WebInspector
.DataGrid
.Events
.SelectedNode
, this._onSelectAllocationNode
, this);
89 this._allocationDataGrid
.show(this._allocationWidget
.element
);
91 this._allocationStackView
= new WebInspector
.HeapAllocationStackView(profile
.target());
92 this._allocationStackView
.setMinimumSize(50, 25);
94 this._tabbedPane
= new WebInspector
.TabbedPane();
95 this._tabbedPane
.headerElement().classList
.add("heap-object-details-header");
98 this._retainmentWidget
= new WebInspector
.VBox();
99 this._retainmentWidget
.setMinimumSize(50, 21);
100 this._retainmentWidget
.element
.classList
.add("retaining-paths-view");
102 var splitWidgetResizer
;
103 if (this._allocationStackView
) {
104 this._tabbedPane
= new WebInspector
.TabbedPane();
105 this._tabbedPane
.headerElement().classList
.add("heap-object-details-header");
107 this._tabbedPane
.appendTab("retainers", WebInspector
.UIString("Retainers"), this._retainmentWidget
);
108 this._tabbedPane
.appendTab("allocation-stack", WebInspector
.UIString("Allocation stack"), this._allocationStackView
);
110 splitWidgetResizer
= this._tabbedPane
.headerElement();
111 this._objectDetailsView
= this._tabbedPane
;
113 var retainmentViewHeader
= createElementWithClass("div", "heap-snapshot-view-resizer");
114 var retainingPathsTitleDiv
= retainmentViewHeader
.createChild("div", "title");
115 var retainingPathsTitle
= retainingPathsTitleDiv
.createChild("span");
116 retainingPathsTitle
.textContent
= WebInspector
.UIString("Retainers");
117 this._retainmentWidget
.element
.appendChild(retainmentViewHeader
);
119 splitWidgetResizer
= retainmentViewHeader
;
120 this._objectDetailsView
= this._retainmentWidget
;
122 this._splitWidget
.hideDefaultResizer();
123 this._splitWidget
.installResizer(splitWidgetResizer
);
125 this._retainmentDataGrid
= new WebInspector
.HeapSnapshotRetainmentDataGrid(this);
126 this._retainmentDataGrid
.show(this._retainmentWidget
.element
);
127 this._retainmentDataGrid
.addEventListener(WebInspector
.DataGrid
.Events
.SelectedNode
, this._inspectedObjectChanged
, this);
128 this._retainmentDataGrid
.reset();
130 this._perspectives
= [];
131 this._perspectives
.push(new WebInspector
.HeapSnapshotView
.SummaryPerspective());
132 if (profile
.profileType() !== WebInspector
.ProfileTypeRegistry
.instance
.trackingHeapSnapshotProfileType
)
133 this._perspectives
.push(new WebInspector
.HeapSnapshotView
.ComparisonPerspective());
134 this._perspectives
.push(new WebInspector
.HeapSnapshotView
.ContainmentPerspective());
135 if (this._allocationWidget
)
136 this._perspectives
.push(new WebInspector
.HeapSnapshotView
.AllocationPerspective());
137 this._perspectives
.push(new WebInspector
.HeapSnapshotView
.StatisticsPerspective());
139 this._perspectiveSelect
= new WebInspector
.ToolbarComboBox(this._onSelectedPerspectiveChanged
.bind(this));
140 for (var i
= 0; i
< this._perspectives
.length
; ++i
)
141 this._perspectiveSelect
.createOption(this._perspectives
[i
].title());
143 this._profile
= profile
;
145 this._baseSelect
= new WebInspector
.ToolbarComboBox(this._changeBase
.bind(this));
146 this._baseSelect
.setVisible(false);
147 this._updateBaseOptions();
149 this._filterSelect
= new WebInspector
.ToolbarComboBox(this._changeFilter
.bind(this));
150 this._filterSelect
.setVisible(false);
151 this._updateFilterOptions();
153 this._classNameFilter
= new WebInspector
.ToolbarInput("Class filter");
154 this._classNameFilter
.setVisible(false);
155 this._constructorsDataGrid
.setNameFilter(this._classNameFilter
);
156 this._diffDataGrid
.setNameFilter(this._classNameFilter
);
158 this._selectedSizeText
= new WebInspector
.ToolbarText("");
160 this._popoverHelper
= new WebInspector
.ObjectPopoverHelper(this.element
, this._getHoverAnchor
.bind(this), this._resolveObjectForPopover
.bind(this), undefined, true);
162 this._currentPerspectiveIndex
= 0;
163 this._currentPerspective
= this._perspectives
[0];
164 this._currentPerspective
.activate(this);
165 this._dataGrid
= this._currentPerspective
.masterGrid(this);
168 this._searchThrottler
= new WebInspector
.Throttler(0);
173 * @param {string} title
175 WebInspector
.HeapSnapshotView
.Perspective = function(title
)
180 WebInspector
.HeapSnapshotView
.Perspective
.prototype = {
182 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
184 activate: function(heapSnapshotView
) { },
187 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
189 deactivate: function(heapSnapshotView
)
191 heapSnapshotView
._baseSelect
.setVisible(false);
192 heapSnapshotView
._filterSelect
.setVisible(false);
193 heapSnapshotView
._classNameFilter
.setVisible(false);
194 if (heapSnapshotView
._trackingOverviewGrid
)
195 heapSnapshotView
._trackingOverviewGrid
.detach();
196 if (heapSnapshotView
._allocationWidget
)
197 heapSnapshotView
._allocationWidget
.detach();
198 if (heapSnapshotView
._statisticsView
)
199 heapSnapshotView
._statisticsView
.detach();
201 heapSnapshotView
._splitWidget
.detach();
202 heapSnapshotView
._splitWidget
.detachChildWidgets();
206 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
207 * @return {?WebInspector.DataGrid}
209 masterGrid: function(heapSnapshotView
)
225 supportsSearch: function()
233 * @extends {WebInspector.HeapSnapshotView.Perspective}
235 WebInspector
.HeapSnapshotView
.SummaryPerspective = function()
237 WebInspector
.HeapSnapshotView
.Perspective
.call(this, WebInspector
.UIString("Summary"));
240 WebInspector
.HeapSnapshotView
.SummaryPerspective
.prototype = {
243 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
245 activate: function(heapSnapshotView
)
247 heapSnapshotView
._splitWidget
.setMainWidget(heapSnapshotView
._constructorsWidget
);
248 heapSnapshotView
._splitWidget
.setSidebarWidget(heapSnapshotView
._objectDetailsView
);
249 heapSnapshotView
._splitWidget
.show(heapSnapshotView
._searchableView
.element
);
250 heapSnapshotView
._filterSelect
.setVisible(true);
251 heapSnapshotView
._classNameFilter
.setVisible(true);
252 if (heapSnapshotView
._trackingOverviewGrid
) {
253 heapSnapshotView
._trackingOverviewGrid
.show(heapSnapshotView
._searchableView
.element
, heapSnapshotView
._splitWidget
.element
);
254 heapSnapshotView
._trackingOverviewGrid
.update();
255 heapSnapshotView
._trackingOverviewGrid
._updateGrid();
261 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
262 * @return {?WebInspector.DataGrid}
264 masterGrid: function(heapSnapshotView
)
266 return heapSnapshotView
._constructorsDataGrid
;
273 supportsSearch: function()
278 __proto__
: WebInspector
.HeapSnapshotView
.Perspective
.prototype
283 * @extends {WebInspector.HeapSnapshotView.Perspective}
285 WebInspector
.HeapSnapshotView
.ComparisonPerspective = function()
287 WebInspector
.HeapSnapshotView
.Perspective
.call(this, WebInspector
.UIString("Comparison"));
290 WebInspector
.HeapSnapshotView
.ComparisonPerspective
.prototype = {
293 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
295 activate: function(heapSnapshotView
)
297 heapSnapshotView
._splitWidget
.setMainWidget(heapSnapshotView
._diffWidget
);
298 heapSnapshotView
._splitWidget
.setSidebarWidget(heapSnapshotView
._objectDetailsView
);
299 heapSnapshotView
._splitWidget
.show(heapSnapshotView
._searchableView
.element
);
300 heapSnapshotView
._baseSelect
.setVisible(true);
301 heapSnapshotView
._classNameFilter
.setVisible(true);
306 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
307 * @return {?WebInspector.DataGrid}
309 masterGrid: function(heapSnapshotView
)
311 return heapSnapshotView
._diffDataGrid
;
318 supportsSearch: function()
323 __proto__
: WebInspector
.HeapSnapshotView
.Perspective
.prototype
328 * @extends {WebInspector.HeapSnapshotView.Perspective}
330 WebInspector
.HeapSnapshotView
.ContainmentPerspective = function()
332 WebInspector
.HeapSnapshotView
.Perspective
.call(this, WebInspector
.UIString("Containment"));
335 WebInspector
.HeapSnapshotView
.ContainmentPerspective
.prototype = {
338 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
340 activate: function(heapSnapshotView
)
342 heapSnapshotView
._splitWidget
.setMainWidget(heapSnapshotView
._containmentWidget
);
343 heapSnapshotView
._splitWidget
.setSidebarWidget(heapSnapshotView
._objectDetailsView
);
344 heapSnapshotView
._splitWidget
.show(heapSnapshotView
._searchableView
.element
);
349 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
350 * @return {?WebInspector.DataGrid}
352 masterGrid: function(heapSnapshotView
)
354 return heapSnapshotView
._containmentDataGrid
;
356 __proto__
: WebInspector
.HeapSnapshotView
.Perspective
.prototype
361 * @extends {WebInspector.HeapSnapshotView.Perspective}
363 WebInspector
.HeapSnapshotView
.AllocationPerspective = function()
365 WebInspector
.HeapSnapshotView
.Perspective
.call(this, WebInspector
.UIString("Allocation"));
366 this._allocationSplitWidget
= new WebInspector
.SplitWidget(false, true, "heapSnapshotAllocationSplitViewState", 200, 200);
367 this._allocationSplitWidget
.setSidebarWidget(new WebInspector
.VBox());
371 WebInspector
.HeapSnapshotView
.AllocationPerspective
.prototype = {
374 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
376 activate: function(heapSnapshotView
)
378 this._allocationSplitWidget
.setMainWidget(heapSnapshotView
._allocationWidget
);
379 heapSnapshotView
._splitWidget
.setMainWidget(heapSnapshotView
._constructorsWidget
);
380 heapSnapshotView
._splitWidget
.setSidebarWidget(heapSnapshotView
._objectDetailsView
);
382 var allocatedObjectsView
= new WebInspector
.VBox();
383 var resizer
= createElementWithClass("div", "heap-snapshot-view-resizer");
384 var title
= resizer
.createChild("div", "title").createChild("span");
385 title
.textContent
= WebInspector
.UIString("Live objects");
386 this._allocationSplitWidget
.hideDefaultResizer();
387 this._allocationSplitWidget
.installResizer(resizer
);
388 allocatedObjectsView
.element
.appendChild(resizer
);
389 heapSnapshotView
._splitWidget
.show(allocatedObjectsView
.element
);
390 this._allocationSplitWidget
.setSidebarWidget(allocatedObjectsView
);
392 this._allocationSplitWidget
.show(heapSnapshotView
._searchableView
.element
);
394 heapSnapshotView
._constructorsDataGrid
.clear();
395 var selectedNode
= heapSnapshotView
._allocationDataGrid
.selectedNode
;
397 heapSnapshotView
._constructorsDataGrid
.setAllocationNodeId(selectedNode
.allocationNodeId());
402 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
404 deactivate: function(heapSnapshotView
)
406 this._allocationSplitWidget
.detach();
407 WebInspector
.HeapSnapshotView
.Perspective
.prototype.deactivate
.call(this, heapSnapshotView
);
412 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
413 * @return {?WebInspector.DataGrid}
415 masterGrid: function(heapSnapshotView
)
417 return heapSnapshotView
._allocationDataGrid
;
420 __proto__
: WebInspector
.HeapSnapshotView
.Perspective
.prototype
425 * @extends {WebInspector.HeapSnapshotView.Perspective}
427 WebInspector
.HeapSnapshotView
.StatisticsPerspective = function()
429 WebInspector
.HeapSnapshotView
.Perspective
.call(this, WebInspector
.UIString("Statistics"));
432 WebInspector
.HeapSnapshotView
.StatisticsPerspective
.prototype = {
435 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
437 activate: function(heapSnapshotView
)
439 heapSnapshotView
._statisticsView
.show(heapSnapshotView
._searchableView
.element
);
444 * @param {!WebInspector.HeapSnapshotView} heapSnapshotView
445 * @return {?WebInspector.DataGrid}
447 masterGrid: function(heapSnapshotView
)
452 __proto__
: WebInspector
.HeapSnapshotView
.Perspective
.prototype
456 WebInspector
.HeapSnapshotView
.prototype = {
458 * @return {!WebInspector.SearchableView}
460 searchableView: function()
462 return this._searchableView
;
467 * @param {?WebInspector.ProfileHeader} profile
468 * @return {?WebInspector.Widget}
470 showProfile: function(profile
)
472 return this._parentDataDisplayDelegate
.showProfile(profile
);
477 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
478 * @param {string} perspectiveName
480 showObject: function(snapshotObjectId
, perspectiveName
)
482 if (snapshotObjectId
<= this._profile
.maxJSObjectId
)
483 this.selectLiveObject(perspectiveName
, snapshotObjectId
);
485 this._parentDataDisplayDelegate
.showObject(snapshotObjectId
, perspectiveName
);
488 _refreshView: function()
490 this._profile
._loadPromise
.then(profileCallback
.bind(this));
494 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
495 * @this {WebInspector.HeapSnapshotView}
497 function profileCallback(heapSnapshotProxy
)
499 heapSnapshotProxy
.getStatistics().then(this._gotStatistics
.bind(this));
500 if (this._profile
.profileType().id
=== WebInspector
.TrackingHeapSnapshotProfileType
.TypeId
&& this._profile
.fromFile())
501 heapSnapshotProxy
.getSamples().then(didGetSamples
.bind(this, heapSnapshotProxy
));
503 setSnapshotProxy
.call(this, heapSnapshotProxy
);
507 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
508 * @param {?WebInspector.HeapSnapshotCommon.Samples} samples
509 * @this {WebInspector.HeapSnapshotView}
511 function didGetSamples(heapSnapshotProxy
, samples
)
513 setSnapshotProxy
.call(this, heapSnapshotProxy
);
514 this._trackingOverviewGrid
._setSamples(samples
);
518 * @param {!WebInspector.HeapSnapshotProxy} heapSnapshotProxy
519 * @this {WebInspector.HeapSnapshotView}
521 function setSnapshotProxy(heapSnapshotProxy
)
523 var list
= this._profiles();
524 var profileIndex
= list
.indexOf(this._profile
);
525 this._baseSelect
.setSelectedIndex(Math
.max(0, profileIndex
- 1));
526 this._dataGrid
.setDataSource(heapSnapshotProxy
);
532 * @param {!WebInspector.HeapSnapshotCommon.Statistics} statistics
534 _gotStatistics: function(statistics
)
536 this._statisticsView
.setTotal(statistics
.total
);
537 this._statisticsView
.addRecord(statistics
.code
, WebInspector
.UIString("Code"), "#f77");
538 this._statisticsView
.addRecord(statistics
.strings
, WebInspector
.UIString("Strings"), "#5e5");
539 this._statisticsView
.addRecord(statistics
.jsArrays
, WebInspector
.UIString("JS Arrays"), "#7af");
540 this._statisticsView
.addRecord(statistics
.native, WebInspector
.UIString("Typed Arrays"), "#fc5");
541 this._statisticsView
.addRecord(statistics
.system
, WebInspector
.UIString("System Objects"), "#98f");
542 this._statisticsView
.addRecord(statistics
.total
, WebInspector
.UIString("Total"));
545 _onIdsRangeChanged: function(event
)
547 var minId
= event
.data
.minId
;
548 var maxId
= event
.data
.maxId
;
549 this._selectedSizeText
.setText(WebInspector
.UIString("Selected size: %s", Number
.bytesToString(event
.data
.size
)));
550 if (this._constructorsDataGrid
.snapshot
)
551 this._constructorsDataGrid
.setSelectionRange(minId
, maxId
);
555 * @return {!Array.<!WebInspector.ToolbarItem>}
557 toolbarItems: function()
559 var result
= [this._perspectiveSelect
, this._classNameFilter
];
560 if (this._profile
.profileType() !== WebInspector
.ProfileTypeRegistry
.instance
.trackingHeapSnapshotProfileType
)
561 result
.push(this._baseSelect
, this._filterSelect
);
562 result
.push(this._selectedSizeText
);
568 this._profile
._loadPromise
.then(this._profile
._wasShown
.bind(this._profile
));
573 this._currentSearchResultIndex
= -1;
574 this._popoverHelper
.hidePopover();
575 if (this.helpPopover
&& this.helpPopover
.isShowing())
576 this.helpPopover
.hide();
583 supportsCaseSensitiveSearch: function()
592 supportsRegexSearch: function()
600 searchCanceled: function()
602 this._currentSearchResultIndex
= -1;
603 this._searchResults
= [];
607 * @param {?WebInspector.HeapSnapshotGridNode} node
609 _selectRevealedNode: function(node
)
617 * @param {!WebInspector.SearchableView.SearchConfig} searchConfig
618 * @param {boolean} shouldJump
619 * @param {boolean=} jumpBackwards
621 performSearch: function(searchConfig
, shouldJump
, jumpBackwards
)
623 var nextQuery
= new WebInspector
.HeapSnapshotCommon
.SearchConfig(
624 searchConfig
.query
.trim(),
625 searchConfig
.caseSensitive
,
626 searchConfig
.isRegex
,
628 jumpBackwards
|| false
631 this._searchThrottler
.schedule(this._performSearch
.bind(this, nextQuery
));
635 * @param {!WebInspector.HeapSnapshotCommon.SearchConfig} nextQuery
636 * @return {!Promise<?>}
638 _performSearch: function(nextQuery
)
640 // Call searchCanceled since it will reset everything we need before doing a new search.
641 this.searchCanceled();
643 if (!this._currentPerspective
.supportsSearch())
644 return Promise
.resolve();
646 this.currentQuery
= nextQuery
;
647 var query
= nextQuery
.query
.trim();
650 return Promise
.resolve();
652 if (query
.charAt(0) === "@") {
653 var snapshotNodeId
= parseInt(query
.substring(1), 10);
654 if (isNaN(snapshotNodeId
))
655 return Promise
.resolve();
656 return this._dataGrid
.revealObjectByHeapSnapshotId(String(snapshotNodeId
)).then(this._selectRevealedNode
.bind(this));
660 * @param {!Array<number>} entryIds
661 * @return {!Promise<?>}
662 * @this {WebInspector.HeapSnapshotView}
664 function didSearch(entryIds
)
666 this._searchResults
= entryIds
;
667 this._searchableView
.updateSearchMatchesCount(this._searchResults
.length
);
668 if (this._searchResults
.length
)
669 this._currentSearchResultIndex
= nextQuery
.jumpBackwards
? this._searchResults
.length
- 1 : 0;
670 return this._jumpToSearchResult(this._currentSearchResultIndex
);
673 return this._profile
._snapshotProxy
.search(this.currentQuery
, this._dataGrid
.nodeFilter()).then(didSearch
.bind(this));
679 jumpToNextSearchResult: function()
681 if (!this._searchResults
.length
)
683 this._currentSearchResultIndex
= (this._currentSearchResultIndex
+ 1) % this._searchResults
.length
;
684 this._searchThrottler
.schedule(this._jumpToSearchResult
.bind(this, this._currentSearchResultIndex
));
690 jumpToPreviousSearchResult: function()
692 if (!this._searchResults
.length
)
694 this._currentSearchResultIndex
= (this._currentSearchResultIndex
+ this._searchResults
.length
- 1) % this._searchResults
.length
;
695 this._searchThrottler
.schedule(this._jumpToSearchResult
.bind(this, this._currentSearchResultIndex
));
699 * @param {number} searchResultIndex
700 * @return {!Promise<undefined>}
702 _jumpToSearchResult: function(searchResultIndex
)
704 this._searchableView
.updateCurrentMatchIndex(searchResultIndex
);
705 return this._dataGrid
.revealObjectByHeapSnapshotId(String(this._searchResults
[searchResultIndex
])).then(this._selectRevealedNode
.bind(this));
708 refreshVisibleData: function()
712 var child
= this._dataGrid
.rootNode().children
[0];
715 child
= child
.traverseNextNode(false, null, true);
719 _changeBase: function()
721 if (this._baseProfile
=== this._profiles()[this._baseSelect
.selectedIndex()])
724 this._baseProfile
= this._profiles()[this._baseSelect
.selectedIndex()];
725 var dataGrid
= /** @type {!WebInspector.HeapSnapshotDiffDataGrid} */ (this._dataGrid
);
726 // Change set base data source only if main data source is already set.
727 if (dataGrid
.snapshot
)
728 this._baseProfile
._loadPromise
.then(dataGrid
.setBaseDataSource
.bind(dataGrid
));
730 if (!this.currentQuery
|| !this._searchResults
)
733 // The current search needs to be performed again. First negate out previous match
734 // count by calling the search finished callback with a negative number of matches.
735 // Then perform the search again with the same query and callback.
736 this.performSearch(this.currentQuery
, false);
739 _changeFilter: function()
741 var profileIndex
= this._filterSelect
.selectedIndex() - 1;
742 this._dataGrid
.filterSelectIndexChanged(this._profiles(), profileIndex
);
744 if (!this.currentQuery
|| !this._searchResults
)
747 // The current search needs to be performed again. First negate out previous match
748 // count by calling the search finished callback with a negative number of matches.
749 // Then perform the search again with the same query and callback.
750 this.performSearch(this.currentQuery
, false);
754 * @return {!Array.<!WebInspector.ProfileHeader>}
756 _profiles: function()
758 return this._profile
.profileType().getProfiles();
762 * @param {!WebInspector.ContextMenu} contextMenu
763 * @param {!Event} event
765 populateContextMenu: function(contextMenu
, event
)
768 this._dataGrid
.populateContextMenu(contextMenu
, event
);
771 _selectionChanged: function(event
)
773 var selectedNode
= event
.target
.selectedNode
;
774 this._setSelectedNodeForDetailsView(selectedNode
);
775 this._inspectedObjectChanged(event
);
778 _onSelectAllocationNode: function(event
)
780 var selectedNode
= event
.target
.selectedNode
;
781 this._constructorsDataGrid
.setAllocationNodeId(selectedNode
.allocationNodeId());
782 this._setSelectedNodeForDetailsView(null);
785 _inspectedObjectChanged: function(event
)
787 var selectedNode
= event
.target
.selectedNode
;
788 var target
= this._profile
.target();
789 if (target
&& selectedNode
instanceof WebInspector
.HeapSnapshotGenericObjectNode
)
790 target
.heapProfilerAgent().addInspectedHeapObject(String(selectedNode
.snapshotNodeId
));
794 * @param {?WebInspector.HeapSnapshotGridNode} nodeItem
796 _setSelectedNodeForDetailsView: function(nodeItem
)
798 var dataSource
= nodeItem
&& nodeItem
.retainersDataSource();
800 this._retainmentDataGrid
.setDataSource(dataSource
.snapshot
, dataSource
.snapshotNodeIndex
);
801 if (this._allocationStackView
)
802 this._allocationStackView
.setAllocatedObject(dataSource
.snapshot
, dataSource
.snapshotNodeIndex
);
804 if (this._allocationStackView
)
805 this._allocationStackView
.clear();
806 this._retainmentDataGrid
.reset();
811 * @param {string} perspectiveTitle
812 * @param {function()} callback
814 _changePerspectiveAndWait: function(perspectiveTitle
, callback
)
816 var perspectiveIndex
= null;
817 for (var i
= 0; i
< this._perspectives
.length
; ++i
) {
818 if (this._perspectives
[i
].title() === perspectiveTitle
) {
819 perspectiveIndex
= i
;
823 if (this._currentPerspectiveIndex
=== perspectiveIndex
|| perspectiveIndex
=== null) {
824 setTimeout(callback
, 0);
829 * @this {WebInspector.HeapSnapshotView}
831 function dataGridContentShown(event
)
833 var dataGrid
= event
.data
;
834 dataGrid
.removeEventListener(WebInspector
.HeapSnapshotSortableDataGrid
.Events
.ContentShown
, dataGridContentShown
, this);
835 if (dataGrid
=== this._dataGrid
)
838 this._perspectives
[perspectiveIndex
].masterGrid(this).addEventListener(WebInspector
.HeapSnapshotSortableDataGrid
.Events
.ContentShown
, dataGridContentShown
, this);
840 this._perspectiveSelect
.setSelectedIndex(perspectiveIndex
);
841 this._changePerspective(perspectiveIndex
);
844 _updateDataSourceAndView: function()
846 var dataGrid
= this._dataGrid
;
847 if (!dataGrid
|| dataGrid
.snapshot
)
850 this._profile
._loadPromise
.then(didLoadSnapshot
.bind(this));
853 * @this {WebInspector.HeapSnapshotView}
855 function didLoadSnapshot(snapshotProxy
)
857 if (this._dataGrid
!== dataGrid
)
859 if (dataGrid
.snapshot
!== snapshotProxy
)
860 dataGrid
.setDataSource(snapshotProxy
);
861 if (dataGrid
=== this._diffDataGrid
) {
862 if (!this._baseProfile
)
863 this._baseProfile
= this._profiles()[this._baseSelect
.selectedIndex()];
864 this._baseProfile
._loadPromise
.then(didLoadBaseSnaphot
.bind(this));
869 * @this {WebInspector.HeapSnapshotView}
871 function didLoadBaseSnaphot(baseSnapshotProxy
)
873 if (this._diffDataGrid
.baseSnapshot
!== baseSnapshotProxy
)
874 this._diffDataGrid
.setBaseDataSource(baseSnapshotProxy
);
878 _onSelectedPerspectiveChanged: function(event
)
880 this._changePerspective(event
.target
.selectedIndex
);
884 * @param {number} selectedIndex
886 _changePerspective: function(selectedIndex
)
888 if (selectedIndex
=== this._currentPerspectiveIndex
)
891 this._currentPerspectiveIndex
= selectedIndex
;
893 this._currentPerspective
.deactivate(this);
894 var perspective
= this._perspectives
[selectedIndex
];
895 this._currentPerspective
= perspective
;
896 this._dataGrid
= perspective
.masterGrid(this);
897 perspective
.activate(this);
899 this.refreshVisibleData();
901 this._dataGrid
.updateWidths();
903 this._updateDataSourceAndView();
905 if (!this.currentQuery
|| !this._searchResults
)
908 // The current search needs to be performed again. First negate out previous match
909 // count by calling the search finished callback with a negative number of matches.
910 // Then perform the search again the with same query and callback.
911 this.performSearch(this.currentQuery
, false);
915 * @param {string} perspectiveName
916 * @param {!HeapProfilerAgent.HeapSnapshotObjectId} snapshotObjectId
918 selectLiveObject: function(perspectiveName
, snapshotObjectId
)
920 this._changePerspectiveAndWait(perspectiveName
, didChangePerspective
.bind(this));
923 * @this {WebInspector.HeapSnapshotView}
925 function didChangePerspective()
927 this._dataGrid
.revealObjectByHeapSnapshotId(snapshotObjectId
, didRevealObject
);
931 * @param {?WebInspector.HeapSnapshotGridNode} node
933 function didRevealObject(node
)
938 WebInspector
.console
.error("Cannot find corresponding heap snapshot node");
942 _getHoverAnchor: function(target
)
944 var span
= target
.enclosingNodeOrSelfWithNodeName("span");
947 var row
= target
.enclosingNodeOrSelfWithNodeName("tr");
950 span
.node
= row
._dataGridNode
;
954 _resolveObjectForPopover: function(element
, showCallback
, objectGroupName
)
956 if (!this._profile
.target())
960 element
.node
.queryObjectContent(this._profile
.target(), showCallback
, objectGroupName
);
963 _updateBaseOptions: function()
965 var list
= this._profiles();
966 // We're assuming that snapshots can only be added.
967 if (this._baseSelect
.size() === list
.length
)
970 for (var i
= this._baseSelect
.size(), n
= list
.length
; i
< n
; ++i
) {
971 var title
= list
[i
].title
;
972 this._baseSelect
.createOption(title
);
976 _updateFilterOptions: function()
978 var list
= this._profiles();
979 // We're assuming that snapshots can only be added.
980 if (this._filterSelect
.size() - 1 === list
.length
)
983 if (!this._filterSelect
.size())
984 this._filterSelect
.createOption(WebInspector
.UIString("All objects"));
986 for (var i
= this._filterSelect
.size() - 1, n
= list
.length
; i
< n
; ++i
) {
987 var title
= list
[i
].title
;
989 title
= WebInspector
.UIString("Objects allocated before %s", title
);
991 title
= WebInspector
.UIString("Objects allocated between %s and %s", list
[i
- 1].title
, title
);
992 this._filterSelect
.createOption(title
);
996 _updateControls: function()
998 this._updateBaseOptions();
999 this._updateFilterOptions();
1003 * @param {!WebInspector.Event} event
1005 _onReceiveSnapshot: function(event
)
1007 this._updateControls();
1011 * @param {!WebInspector.Event} event
1013 _onProfileHeaderRemoved: function(event
)
1015 var profile
= event
.data
;
1016 if (this._profile
=== profile
) {
1018 this._profile
.profileType().removeEventListener(WebInspector
.HeapSnapshotProfileType
.SnapshotReceived
, this._onReceiveSnapshot
, this);
1019 this._profile
.profileType().removeEventListener(WebInspector
.ProfileType
.Events
.RemoveProfileHeader
, this._onProfileHeaderRemoved
, this);
1022 this._updateControls();
1028 if (this._allocationStackView
) {
1029 this._allocationStackView
.clear();
1030 this._allocationDataGrid
.dispose();
1032 if (this._trackingOverviewGrid
)
1033 this._trackingOverviewGrid
.dispose();
1036 __proto__
: WebInspector
.VBox
.prototype
1041 * @extends {WebInspector.ProfileType}
1042 * @implements {WebInspector.TargetManager.Observer}
1043 * @param {string=} id
1044 * @param {string=} title
1046 WebInspector
.HeapSnapshotProfileType = function(id
, title
)
1048 WebInspector
.ProfileType
.call(this, id
|| WebInspector
.HeapSnapshotProfileType
.TypeId
, title
|| WebInspector
.UIString("Take Heap Snapshot"));
1049 WebInspector
.targetManager
.observeTargets(this);
1050 WebInspector
.targetManager
.addModelListener(WebInspector
.HeapProfilerModel
, WebInspector
.HeapProfilerModel
.Events
.ResetProfiles
, this._resetProfiles
, this);
1051 WebInspector
.targetManager
.addModelListener(WebInspector
.HeapProfilerModel
, WebInspector
.HeapProfilerModel
.Events
.AddHeapSnapshotChunk
, this._addHeapSnapshotChunk
, this);
1052 WebInspector
.targetManager
.addModelListener(WebInspector
.HeapProfilerModel
, WebInspector
.HeapProfilerModel
.Events
.ReportHeapSnapshotProgress
, this._reportHeapSnapshotProgress
, this);
1055 WebInspector
.HeapSnapshotProfileType
.TypeId
= "HEAP";
1056 WebInspector
.HeapSnapshotProfileType
.SnapshotReceived
= "SnapshotReceived";
1058 WebInspector
.HeapSnapshotProfileType
.prototype = {
1061 * @param {!WebInspector.Target} target
1063 targetAdded: function(target
)
1065 target
.heapProfilerModel
.enable();
1070 * @param {!WebInspector.Target} target
1072 targetRemoved: function(target
)
1080 fileExtension: function()
1082 return ".heapsnapshot";
1087 return WebInspector
.UIString("Take heap snapshot.");
1094 isInstantProfile: function()
1103 buttonClicked: function()
1105 this._takeHeapSnapshot(function() {});
1106 WebInspector
.userMetrics
.ProfilesHeapProfileTaken
.record();
1112 return WebInspector
.UIString("HEAP SNAPSHOTS");
1117 return WebInspector
.UIString("Heap snapshot profiles show memory distribution among your page's JavaScript objects and related DOM nodes.");
1122 * @param {string} title
1123 * @return {!WebInspector.ProfileHeader}
1125 createProfileLoadedFromFile: function(title
)
1127 return new WebInspector
.HeapProfileHeader(null, this, title
);
1130 _takeHeapSnapshot: function(callback
)
1132 if (this.profileBeingRecorded())
1134 var target
= /** @type {!WebInspector.Target} */ (WebInspector
.context
.flavor(WebInspector
.Target
));
1135 var profile
= new WebInspector
.HeapProfileHeader(target
, this);
1136 this.setProfileBeingRecorded(profile
);
1137 this.addProfile(profile
);
1138 profile
.updateStatus(WebInspector
.UIString("Snapshotting\u2026"));
1141 * @param {?string} error
1142 * @this {WebInspector.HeapSnapshotProfileType}
1144 function didTakeHeapSnapshot(error
)
1146 var profile
= this._profileBeingRecorded
;
1147 profile
.title
= WebInspector
.UIString("Snapshot %d", profile
.uid
);
1148 profile
._finishLoad();
1149 this.setProfileBeingRecorded(null);
1150 this.dispatchEventToListeners(WebInspector
.ProfileType
.Events
.ProfileComplete
, profile
);
1153 target
.heapProfilerAgent().takeHeapSnapshot(true, didTakeHeapSnapshot
.bind(this));
1157 * @param {!WebInspector.Event} event
1159 _addHeapSnapshotChunk: function(event
)
1161 if (!this.profileBeingRecorded())
1163 var chunk
= /** @type {string} */(event
.data
);
1164 this.profileBeingRecorded().transferChunk(chunk
);
1168 * @param {!WebInspector.Event} event
1170 _reportHeapSnapshotProgress: function(event
)
1172 var profile
= this.profileBeingRecorded();
1175 var data
= /** @type {{done: number, total: number, finished: boolean}} */ (event
.data
);
1176 profile
.updateStatus(WebInspector
.UIString("%.0f%%", (data
.done
/ data
.total
) * 100), true);
1178 profile
._prepareToLoad();
1181 _resetProfiles: function()
1186 _snapshotReceived: function(profile
)
1188 if (this._profileBeingRecorded
=== profile
)
1189 this.setProfileBeingRecorded(null);
1190 this.dispatchEventToListeners(WebInspector
.HeapSnapshotProfileType
.SnapshotReceived
, profile
);
1193 __proto__
: WebInspector
.ProfileType
.prototype
1199 * @extends {WebInspector.HeapSnapshotProfileType}
1201 WebInspector
.TrackingHeapSnapshotProfileType = function()
1203 WebInspector
.HeapSnapshotProfileType
.call(this, WebInspector
.TrackingHeapSnapshotProfileType
.TypeId
, WebInspector
.UIString("Record Heap Allocations"));
1206 WebInspector
.TrackingHeapSnapshotProfileType
.TypeId
= "HEAP-RECORD";
1208 WebInspector
.TrackingHeapSnapshotProfileType
.HeapStatsUpdate
= "HeapStatsUpdate";
1209 WebInspector
.TrackingHeapSnapshotProfileType
.TrackingStarted
= "TrackingStarted";
1210 WebInspector
.TrackingHeapSnapshotProfileType
.TrackingStopped
= "TrackingStopped";
1216 WebInspector
.TrackingHeapSnapshotProfileType
.Samples = function()
1218 /** @type {!Array.<number>} */
1220 /** @type {!Array.<number>} */
1222 /** @type {!Array.<number>} */
1223 this.timestamps
= [];
1224 /** @type {!Array.<number>} */
1226 /** @type {number} */
1227 this.totalTime
= 30000;
1230 WebInspector
.TrackingHeapSnapshotProfileType
.prototype = {
1234 * @param {!WebInspector.Target} target
1236 targetAdded: function(target
)
1238 WebInspector
.HeapSnapshotProfileType
.prototype.targetAdded
.call(this, target
);
1239 target
.heapProfilerModel
.addEventListener(WebInspector
.HeapProfilerModel
.Events
.HeapStatsUpdate
, this._heapStatsUpdate
, this);
1240 target
.heapProfilerModel
.addEventListener(WebInspector
.HeapProfilerModel
.Events
.LastSeenObjectId
, this._lastSeenObjectId
, this);
1245 * @param {!WebInspector.Target} target
1247 targetRemoved: function(target
)
1249 WebInspector
.HeapSnapshotProfileType
.prototype.targetRemoved
.call(this, target
);
1250 target
.heapProfilerModel
.removeEventListener(WebInspector
.HeapProfilerModel
.Events
.HeapStatsUpdate
, this._heapStatsUpdate
, this);
1251 target
.heapProfilerModel
.removeEventListener(WebInspector
.HeapProfilerModel
.Events
.LastSeenObjectId
, this._lastSeenObjectId
, this);
1255 * @param {!WebInspector.Event} event
1257 _heapStatsUpdate: function(event
)
1259 if (!this._profileSamples
)
1261 var samples
= /** @type {!Array.<number>} */ (event
.data
);
1263 for (var i
= 0; i
< samples
.length
; i
+= 3) {
1265 var size
= samples
[i
+2];
1266 this._profileSamples
.sizes
[index
] = size
;
1267 if (!this._profileSamples
.max
[index
])
1268 this._profileSamples
.max
[index
] = size
;
1273 * @param {!WebInspector.Event} event
1275 _lastSeenObjectId: function(event
)
1277 var profileSamples
= this._profileSamples
;
1278 if (!profileSamples
)
1280 var data
= /** @type {{lastSeenObjectId: number, timestamp: number}} */ (event
.data
);
1281 var currentIndex
= Math
.max(profileSamples
.ids
.length
, profileSamples
.max
.length
- 1);
1282 profileSamples
.ids
[currentIndex
] = data
.lastSeenObjectId
;
1283 if (!profileSamples
.max
[currentIndex
]) {
1284 profileSamples
.max
[currentIndex
] = 0;
1285 profileSamples
.sizes
[currentIndex
] = 0;
1287 profileSamples
.timestamps
[currentIndex
] = data
.timestamp
;
1288 if (profileSamples
.totalTime
< data
.timestamp
- profileSamples
.timestamps
[0])
1289 profileSamples
.totalTime
*= 2;
1290 this.dispatchEventToListeners(WebInspector
.TrackingHeapSnapshotProfileType
.HeapStatsUpdate
, this._profileSamples
);
1291 this._profileBeingRecorded
.updateStatus(null, true);
1298 hasTemporaryView: function()
1305 return this._recording
? WebInspector
.UIString("Stop recording heap profile.") : WebInspector
.UIString("Start recording heap profile.");
1312 isInstantProfile: function()
1321 buttonClicked: function()
1323 return this._toggleRecording();
1326 _startRecordingProfile: function()
1328 if (this.profileBeingRecorded())
1330 this._addNewProfile();
1331 var recordAllocationStacks
= WebInspector
.moduleSetting("recordAllocationStacks").get();
1332 this.profileBeingRecorded().target().heapProfilerAgent().startTrackingHeapObjects(recordAllocationStacks
);
1335 _addNewProfile: function()
1337 var target
= WebInspector
.context
.flavor(WebInspector
.Target
);
1338 this.setProfileBeingRecorded(new WebInspector
.HeapProfileHeader(target
, this, undefined));
1339 this._profileSamples
= new WebInspector
.TrackingHeapSnapshotProfileType
.Samples();
1340 this._profileBeingRecorded
._profileSamples
= this._profileSamples
;
1341 this._recording
= true;
1342 this.addProfile(this._profileBeingRecorded
);
1343 this._profileBeingRecorded
.updateStatus(WebInspector
.UIString("Recording\u2026"));
1344 this.dispatchEventToListeners(WebInspector
.TrackingHeapSnapshotProfileType
.TrackingStarted
);
1347 _stopRecordingProfile: function()
1349 this._profileBeingRecorded
.updateStatus(WebInspector
.UIString("Snapshotting\u2026"));
1351 * @param {?string} error
1352 * @this {WebInspector.HeapSnapshotProfileType}
1354 function didTakeHeapSnapshot(error
)
1356 var profile
= this.profileBeingRecorded();
1359 profile
._finishLoad();
1360 this._profileSamples
= null;
1361 this.setProfileBeingRecorded(null);
1362 this.dispatchEventToListeners(WebInspector
.ProfileType
.Events
.ProfileComplete
, profile
);
1365 this._profileBeingRecorded
.target().heapProfilerAgent().stopTrackingHeapObjects(true, didTakeHeapSnapshot
.bind(this));
1366 this._recording
= false;
1367 this.dispatchEventToListeners(WebInspector
.TrackingHeapSnapshotProfileType
.TrackingStopped
);
1370 _toggleRecording: function()
1372 if (this._recording
)
1373 this._stopRecordingProfile();
1375 this._startRecordingProfile();
1376 return this._recording
;
1383 fileExtension: function()
1385 return ".heaptimeline";
1390 return WebInspector
.UIString("HEAP TIMELINES");
1395 return WebInspector
.UIString("Record JavaScript object allocations over time. Use this profile type to isolate memory leaks.");
1401 _resetProfiles: function()
1403 var wasRecording
= this._recording
;
1404 // Clear current profile to avoid stopping backend.
1405 this.setProfileBeingRecorded(null);
1406 WebInspector
.HeapSnapshotProfileType
.prototype._resetProfiles
.call(this);
1407 this._profileSamples
= null;
1409 this._addNewProfile();
1415 profileBeingRecordedRemoved: function()
1417 this._stopRecordingProfile();
1418 this._profileSamples
= null;
1421 __proto__
: WebInspector
.HeapSnapshotProfileType
.prototype
1426 * @extends {WebInspector.ProfileHeader}
1427 * @param {?WebInspector.Target} target
1428 * @param {!WebInspector.HeapSnapshotProfileType} type
1429 * @param {string=} title
1431 WebInspector
.HeapProfileHeader = function(target
, type
, title
)
1433 WebInspector
.ProfileHeader
.call(this, target
, type
, title
|| WebInspector
.UIString("Snapshot %d", type
.nextProfileUid()));
1434 this.maxJSObjectId
= -1;
1436 * @type {?WebInspector.HeapSnapshotWorkerProxy}
1438 this._workerProxy
= null;
1440 * @type {?WebInspector.OutputStream}
1442 this._receiver
= null;
1444 * @type {?WebInspector.HeapSnapshotProxy}
1446 this._snapshotProxy
= null;
1448 * @type {!Promise.<!WebInspector.HeapSnapshotProxy>}
1450 this._loadPromise
= new Promise(loadResolver
.bind(this));
1451 this._totalNumberOfChunks
= 0;
1452 this._bufferedWriter
= null;
1455 * @param {function(!WebInspector.HeapSnapshotProxy)} fulfill
1456 * @this {WebInspector.HeapProfileHeader}
1458 function loadResolver(fulfill
)
1460 this._fulfillLoad
= fulfill
;
1464 WebInspector
.HeapProfileHeader
.prototype = {
1467 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1468 * @return {!WebInspector.ProfileSidebarTreeElement}
1470 createSidebarTreeElement: function(dataDisplayDelegate
)
1472 return new WebInspector
.ProfileSidebarTreeElement(dataDisplayDelegate
, this, "heap-snapshot-sidebar-tree-item");
1477 * @param {!WebInspector.ProfileType.DataDisplayDelegate} dataDisplayDelegate
1478 * @return {!WebInspector.HeapSnapshotView}
1480 createView: function(dataDisplayDelegate
)
1482 return new WebInspector
.HeapSnapshotView(dataDisplayDelegate
, this);
1485 _prepareToLoad: function()
1487 console
.assert(!this._receiver
, "Already loading");
1488 this._setupWorker();
1489 this.updateStatus(WebInspector
.UIString("Loading\u2026"), true);
1492 _finishLoad: function()
1494 if (!this._wasDisposed
)
1495 this._receiver
.close();
1496 if (this._bufferedWriter
) {
1497 this._bufferedWriter
.finishWriting(this._didWriteToTempFile
.bind(this));
1498 this._bufferedWriter
= null;
1502 _didWriteToTempFile: function(tempFile
)
1504 if (this._wasDisposed
) {
1509 this._tempFile
= tempFile
;
1511 this._failedToCreateTempFile
= true;
1512 if (this._onTempFileReady
) {
1513 this._onTempFileReady();
1514 this._onTempFileReady
= null;
1518 _setupWorker: function()
1521 * @this {WebInspector.HeapProfileHeader}
1523 function setProfileWait(event
)
1525 this.updateStatus(null, event
.data
);
1527 console
.assert(!this._workerProxy
, "HeapSnapshotWorkerProxy already exists");
1528 this._workerProxy
= new WebInspector
.HeapSnapshotWorkerProxy(this._handleWorkerEvent
.bind(this));
1529 this._workerProxy
.addEventListener("wait", setProfileWait
, this);
1530 this._receiver
= this._workerProxy
.createLoader(this.uid
, this._snapshotReceived
.bind(this));
1534 * @param {string} eventName
1537 _handleWorkerEvent: function(eventName
, data
)
1539 if (WebInspector
.HeapSnapshotProgressEvent
.BrokenSnapshot
=== eventName
) {
1540 var error
= /** @type {string} */ (data
);
1541 WebInspector
.console
.error(error
);
1545 if (WebInspector
.HeapSnapshotProgressEvent
.Update
!== eventName
)
1547 var subtitle
= /** @type {string} */ (data
);
1548 this.updateStatus(subtitle
);
1556 if (this._workerProxy
)
1557 this._workerProxy
.dispose();
1558 this.removeTempFile();
1559 this._wasDisposed
= true;
1562 _didCompleteSnapshotTransfer: function()
1564 if (!this._snapshotProxy
)
1566 this.updateStatus(Number
.bytesToString(this._snapshotProxy
.totalSize
), false);
1570 * @param {string} chunk
1572 transferChunk: function(chunk
)
1574 if (!this._bufferedWriter
)
1575 this._bufferedWriter
= new WebInspector
.DeferredTempFile("heap-profiler", String(this.uid
));
1576 this._bufferedWriter
.write([chunk
]);
1578 ++this._totalNumberOfChunks
;
1579 this._receiver
.write(chunk
);
1582 _snapshotReceived: function(snapshotProxy
)
1584 if (this._wasDisposed
)
1586 this._receiver
= null;
1587 this._snapshotProxy
= snapshotProxy
;
1588 this.maxJSObjectId
= snapshotProxy
.maxJSObjectId();
1589 this._didCompleteSnapshotTransfer();
1590 this._workerProxy
.startCheckingForLongRunningCalls();
1591 this.notifySnapshotReceived();
1594 notifySnapshotReceived: function()
1596 this._fulfillLoad(this._snapshotProxy
);
1597 this._profileType
._snapshotReceived(this);
1598 if (this.canSaveToFile())
1599 this.dispatchEventToListeners(WebInspector
.ProfileHeader
.Events
.ProfileReceived
);
1602 // Hook point for tests.
1603 _wasShown: function()
1611 canSaveToFile: function()
1613 return !this.fromFile() && !!this._snapshotProxy
;
1619 saveToFile: function()
1621 var fileOutputStream
= new WebInspector
.FileOutputStream();
1624 * @param {boolean} accepted
1625 * @this {WebInspector.HeapProfileHeader}
1627 function onOpen(accepted
)
1631 if (this._failedToCreateTempFile
) {
1632 WebInspector
.console
.error("Failed to open temp file with heap snapshot");
1633 fileOutputStream
.close();
1634 } else if (this._tempFile
) {
1635 var delegate
= new WebInspector
.SaveSnapshotOutputStreamDelegate(this);
1636 this._tempFile
.writeToOutputSteam(fileOutputStream
, delegate
);
1638 this._onTempFileReady
= onOpen
.bind(this, accepted
);
1639 this._updateSaveProgress(0, 1);
1642 this._fileName
= this._fileName
|| "Heap-" + new Date().toISO8601Compact() + this._profileType
.fileExtension();
1643 fileOutputStream
.open(this._fileName
, onOpen
.bind(this));
1646 _updateSaveProgress: function(value
, total
)
1648 var percentValue
= ((total
? (value
/ total
) : 0) * 100).toFixed(0);
1649 this.updateStatus(WebInspector
.UIString("Saving\u2026 %d%%", percentValue
));
1654 * @param {!File} file
1656 loadFromFile: function(file
)
1658 this.updateStatus(WebInspector
.UIString("Loading\u2026"), true);
1659 this._setupWorker();
1660 var delegate
= new WebInspector
.HeapSnapshotLoadFromFileDelegate(this);
1661 var fileReader
= this._createFileReader(file
, delegate
);
1662 fileReader
.start(this._receiver
);
1665 _createFileReader: function(file
, delegate
)
1667 return new WebInspector
.ChunkedFileReader(file
, 10000000, delegate
);
1670 __proto__
: WebInspector
.ProfileHeader
.prototype
1675 * @implements {WebInspector.OutputStreamDelegate}
1677 WebInspector
.HeapSnapshotLoadFromFileDelegate = function(snapshotHeader
)
1679 this._snapshotHeader
= snapshotHeader
;
1682 WebInspector
.HeapSnapshotLoadFromFileDelegate
.prototype = {
1686 onTransferStarted: function()
1692 * @param {!WebInspector.ChunkedReader} reader
1694 onChunkTransferred: function(reader
)
1701 onTransferFinished: function()
1707 * @param {!WebInspector.ChunkedReader} reader
1710 onError: function(reader
, e
)
1713 switch(e
.target
.error
.code
) {
1714 case e
.target
.error
.NOT_FOUND_ERR
:
1715 subtitle
= WebInspector
.UIString("'%s' not found.", reader
.fileName());
1717 case e
.target
.error
.NOT_READABLE_ERR
:
1718 subtitle
= WebInspector
.UIString("'%s' is not readable", reader
.fileName());
1720 case e
.target
.error
.ABORT_ERR
:
1723 subtitle
= WebInspector
.UIString("'%s' error %d", reader
.fileName(), e
.target
.error
.code
);
1725 this._snapshotHeader
.updateStatus(subtitle
);
1731 * @implements {WebInspector.OutputStreamDelegate}
1732 * @param {!WebInspector.HeapProfileHeader} profileHeader
1734 WebInspector
.SaveSnapshotOutputStreamDelegate = function(profileHeader
)
1736 this._profileHeader
= profileHeader
;
1739 WebInspector
.SaveSnapshotOutputStreamDelegate
.prototype = {
1743 onTransferStarted: function()
1745 this._profileHeader
._updateSaveProgress(0, 1);
1751 onTransferFinished: function()
1753 this._profileHeader
._didCompleteSnapshotTransfer();
1758 * @param {!WebInspector.ChunkedReader} reader
1760 onChunkTransferred: function(reader
)
1762 this._profileHeader
._updateSaveProgress(reader
.loadedSize(), reader
.fileSize());
1767 * @param {!WebInspector.ChunkedReader} reader
1768 * @param {!Event} event
1770 onError: function(reader
, event
)
1772 WebInspector
.console
.error("Failed to read heap snapshot from temp file: " + /** @type {!ErrorEvent} */ (event
).message
);
1773 this.onTransferFinished();
1779 * @extends {WebInspector.VBox}
1780 * @param {!WebInspector.HeapProfileHeader} heapProfileHeader
1782 WebInspector
.HeapTrackingOverviewGrid = function(heapProfileHeader
)
1784 WebInspector
.VBox
.call(this);
1785 this.element
.id
= "heap-recording-view";
1786 this.element
.classList
.add("heap-tracking-overview");
1788 this._overviewContainer
= this.element
.createChild("div", "heap-overview-container");
1789 this._overviewGrid
= new WebInspector
.OverviewGrid("heap-recording");
1790 this._overviewGrid
.element
.classList
.add("fill");
1792 this._overviewCanvas
= this._overviewContainer
.createChild("canvas", "heap-recording-overview-canvas");
1793 this._overviewContainer
.appendChild(this._overviewGrid
.element
);
1794 this._overviewCalculator
= new WebInspector
.HeapTrackingOverviewGrid
.OverviewCalculator();
1795 this._overviewGrid
.addEventListener(WebInspector
.OverviewGrid
.Events
.WindowChanged
, this._onWindowChanged
, this);
1797 this._profileSamples
= heapProfileHeader
.fromFile() ? new WebInspector
.TrackingHeapSnapshotProfileType
.Samples() : heapProfileHeader
._profileSamples
;
1798 this._profileType
= heapProfileHeader
.profileType();
1799 if (!heapProfileHeader
.fromFile() && heapProfileHeader
.profileType().profileBeingRecorded() === heapProfileHeader
) {
1800 this._profileType
.addEventListener(WebInspector
.TrackingHeapSnapshotProfileType
.HeapStatsUpdate
, this._onHeapStatsUpdate
, this);
1801 this._profileType
.addEventListener(WebInspector
.TrackingHeapSnapshotProfileType
.TrackingStopped
, this._onStopTracking
, this);
1803 this._windowLeft
= 0.0;
1804 this._windowRight
= 1.0;
1805 this._overviewGrid
.setWindow(this._windowLeft
, this._windowRight
);
1806 this._yScale
= new WebInspector
.HeapTrackingOverviewGrid
.SmoothScale();
1807 this._xScale
= new WebInspector
.HeapTrackingOverviewGrid
.SmoothScale();
1810 WebInspector
.HeapTrackingOverviewGrid
.IdsRangeChanged
= "IdsRangeChanged";
1812 WebInspector
.HeapTrackingOverviewGrid
.prototype = {
1815 this._onStopTracking();
1818 _onStopTracking: function()
1820 this._profileType
.removeEventListener(WebInspector
.TrackingHeapSnapshotProfileType
.HeapStatsUpdate
, this._onHeapStatsUpdate
, this);
1821 this._profileType
.removeEventListener(WebInspector
.TrackingHeapSnapshotProfileType
.TrackingStopped
, this._onStopTracking
, this);
1824 _onHeapStatsUpdate: function(event
)
1826 this._profileSamples
= event
.data
;
1827 this._scheduleUpdate();
1831 * @param {?WebInspector.HeapSnapshotCommon.Samples} samples
1833 _setSamples: function(samples
)
1837 console
.assert(!this._profileSamples
.timestamps
.length
, "Should only call this method when loading from file.");
1838 console
.assert(samples
.timestamps
.length
);
1839 this._profileSamples
= new WebInspector
.TrackingHeapSnapshotProfileType
.Samples();
1840 this._profileSamples
.sizes
= samples
.sizes
;
1841 this._profileSamples
.ids
= samples
.lastAssignedIds
;
1842 this._profileSamples
.timestamps
= samples
.timestamps
;
1843 this._profileSamples
.max
= samples
.sizes
;
1844 this._profileSamples
.totalTime
= /** @type{number} */(samples
.timestamps
.peekLast());
1849 * @param {number} width
1850 * @param {number} height
1852 _drawOverviewCanvas: function(width
, height
)
1854 if (!this._profileSamples
)
1856 var profileSamples
= this._profileSamples
;
1857 var sizes
= profileSamples
.sizes
;
1858 var topSizes
= profileSamples
.max
;
1859 var timestamps
= profileSamples
.timestamps
;
1860 var startTime
= timestamps
[0];
1861 var endTime
= timestamps
[timestamps
.length
- 1];
1863 var scaleFactor
= this._xScale
.nextScale(width
/ profileSamples
.totalTime
);
1866 * @param {!Array.<number>} sizes
1867 * @param {function(number, number):void} callback
1869 function aggregateAndCall(sizes
, callback
)
1873 for (var i
= 1; i
< timestamps
.length
; ++i
) {
1874 var x
= Math
.floor((timestamps
[i
] - startTime
) * scaleFactor
);
1875 if (x
!== currentX
) {
1877 callback(currentX
, size
);
1883 callback(currentX
, size
);
1888 * @param {number} size
1890 function maxSizeCallback(x
, size
)
1892 maxSize
= Math
.max(maxSize
, size
);
1895 aggregateAndCall(sizes
, maxSizeCallback
);
1897 var yScaleFactor
= this._yScale
.nextScale(maxSize
? height
/ (maxSize
* 1.1) : 0.0);
1899 this._overviewCanvas
.width
= width
* window
.devicePixelRatio
;
1900 this._overviewCanvas
.height
= height
* window
.devicePixelRatio
;
1901 this._overviewCanvas
.style
.width
= width
+ "px";
1902 this._overviewCanvas
.style
.height
= height
+ "px";
1904 var context
= this._overviewCanvas
.getContext("2d");
1905 context
.scale(window
.devicePixelRatio
, window
.devicePixelRatio
);
1907 context
.beginPath();
1908 context
.lineWidth
= 2;
1909 context
.strokeStyle
= "rgba(192, 192, 192, 0.6)";
1910 var currentX
= (endTime
- startTime
) * scaleFactor
;
1911 context
.moveTo(currentX
, height
- 1);
1912 context
.lineTo(currentX
, 0);
1914 context
.closePath();
1918 var gridLabelHeight
= 14;
1920 const maxGridValue
= (height
- gridLabelHeight
) / yScaleFactor
;
1921 // The round value calculation is a bit tricky, because
1922 // it has a form k*10^n*1024^m, where k=[1,5], n=[0..3], m is an integer,
1923 // e.g. a round value 10KB is 10240 bytes.
1924 gridValue
= Math
.pow(1024, Math
.floor(Math
.log(maxGridValue
) / Math
.log(1024)));
1925 gridValue
*= Math
.pow(10, Math
.floor(Math
.log(maxGridValue
/ gridValue
) / Math
.LN10
));
1926 if (gridValue
* 5 <= maxGridValue
)
1928 gridY
= Math
.round(height
- gridValue
* yScaleFactor
- 0.5) + 0.5;
1929 context
.beginPath();
1930 context
.lineWidth
= 1;
1931 context
.strokeStyle
= "rgba(0, 0, 0, 0.2)";
1932 context
.moveTo(0, gridY
);
1933 context
.lineTo(width
, gridY
);
1935 context
.closePath();
1940 * @param {number} size
1942 function drawBarCallback(x
, size
)
1944 context
.moveTo(x
, height
- 1);
1945 context
.lineTo(x
, Math
.round(height
- size
* yScaleFactor
- 1));
1948 context
.beginPath();
1949 context
.lineWidth
= 2;
1950 context
.strokeStyle
= "rgba(192, 192, 192, 0.6)";
1951 aggregateAndCall(topSizes
, drawBarCallback
);
1953 context
.closePath();
1955 context
.beginPath();
1956 context
.lineWidth
= 2;
1957 context
.strokeStyle
= "rgba(0, 0, 192, 0.8)";
1958 aggregateAndCall(sizes
, drawBarCallback
);
1960 context
.closePath();
1963 var label
= Number
.bytesToString(gridValue
);
1964 var labelPadding
= 4;
1966 var labelY
= gridY
- 0.5;
1967 var labelWidth
= 2 * labelPadding
+ context
.measureText(label
).width
;
1968 context
.beginPath();
1969 context
.textBaseline
= "bottom";
1970 context
.font
= "10px " + window
.getComputedStyle(this.element
, null).getPropertyValue("font-family");
1971 context
.fillStyle
= "rgba(255, 255, 255, 0.75)";
1972 context
.fillRect(labelX
, labelY
- gridLabelHeight
, labelWidth
, gridLabelHeight
);
1973 context
.fillStyle
= "rgb(64, 64, 64)";
1974 context
.fillText(label
, labelX
+ labelPadding
, labelY
);
1976 context
.closePath();
1980 onResize: function()
1982 this._updateOverviewCanvas
= true;
1983 this._scheduleUpdate();
1986 _onWindowChanged: function()
1988 if (!this._updateGridTimerId
)
1989 this._updateGridTimerId
= setTimeout(this._updateGrid
.bind(this), 10);
1992 _scheduleUpdate: function()
1994 if (this._updateTimerId
)
1996 this._updateTimerId
= setTimeout(this.update
.bind(this), 10);
1999 _updateBoundaries: function()
2001 this._windowLeft
= this._overviewGrid
.windowLeft();
2002 this._windowRight
= this._overviewGrid
.windowRight();
2003 this._windowWidth
= this._windowRight
- this._windowLeft
;
2008 this._updateTimerId
= null;
2009 if (!this.isShowing())
2011 this._updateBoundaries();
2012 this._overviewCalculator
._updateBoundaries(this);
2013 this._overviewGrid
.updateDividers(this._overviewCalculator
);
2014 this._drawOverviewCanvas(this._overviewContainer
.clientWidth
, this._overviewContainer
.clientHeight
- 20);
2017 _updateGrid: function()
2019 this._updateGridTimerId
= 0;
2020 this._updateBoundaries();
2021 var ids
= this._profileSamples
.ids
;
2022 var timestamps
= this._profileSamples
.timestamps
;
2023 var sizes
= this._profileSamples
.sizes
;
2024 var startTime
= timestamps
[0];
2025 var totalTime
= this._profileSamples
.totalTime
;
2026 var timeLeft
= startTime
+ totalTime
* this._windowLeft
;
2027 var timeRight
= startTime
+ totalTime
* this._windowRight
;
2029 var maxId
= ids
[ids
.length
- 1] + 1;
2031 for (var i
= 0; i
< timestamps
.length
; ++i
) {
2034 if (timestamps
[i
] > timeRight
)
2037 if (timestamps
[i
] < timeLeft
) {
2044 this.dispatchEventToListeners(WebInspector
.HeapTrackingOverviewGrid
.IdsRangeChanged
, {minId
: minId
, maxId
: maxId
, size
: size
});
2047 __proto__
: WebInspector
.VBox
.prototype
2054 WebInspector
.HeapTrackingOverviewGrid
.SmoothScale = function()
2056 this._lastUpdate
= 0;
2057 this._currentScale
= 0.0;
2060 WebInspector
.HeapTrackingOverviewGrid
.SmoothScale
.prototype = {
2062 * @param {number} target
2065 nextScale: function(target
) {
2066 target
= target
|| this._currentScale
;
2067 if (this._currentScale
) {
2068 var now
= Date
.now();
2069 var timeDeltaMs
= now
- this._lastUpdate
;
2070 this._lastUpdate
= now
;
2071 var maxChangePerSec
= 20;
2072 var maxChangePerDelta
= Math
.pow(maxChangePerSec
, timeDeltaMs
/ 1000);
2073 var scaleChange
= target
/ this._currentScale
;
2074 this._currentScale
*= Number
.constrain(scaleChange
, 1 / maxChangePerDelta
, maxChangePerDelta
);
2076 this._currentScale
= target
;
2078 return this._currentScale
;
2085 * @implements {WebInspector.TimelineGrid.Calculator}
2087 WebInspector
.HeapTrackingOverviewGrid
.OverviewCalculator = function()
2091 WebInspector
.HeapTrackingOverviewGrid
.OverviewCalculator
.prototype = {
2096 paddingLeft: function()
2102 * @param {!WebInspector.HeapTrackingOverviewGrid} chart
2104 _updateBoundaries: function(chart
)
2106 this._minimumBoundaries
= 0;
2107 this._maximumBoundaries
= chart
._profileSamples
.totalTime
;
2108 this._xScaleFactor
= chart
._overviewContainer
.clientWidth
/ this._maximumBoundaries
;
2113 * @param {number} time
2116 computePosition: function(time
)
2118 return (time
- this._minimumBoundaries
) * this._xScaleFactor
;
2123 * @param {number} value
2124 * @param {number=} precision
2127 formatTime: function(value
, precision
)
2129 return Number
.secondsToString(value
/ 1000, !!precision
);
2136 maximumBoundary: function()
2138 return this._maximumBoundaries
;
2145 minimumBoundary: function()
2147 return this._minimumBoundaries
;
2154 zeroTime: function()
2156 return this._minimumBoundaries
;
2163 boundarySpan: function()
2165 return this._maximumBoundaries
- this._minimumBoundaries
;
2172 * @extends {WebInspector.VBox}
2174 WebInspector
.HeapSnapshotStatisticsView = function()
2176 WebInspector
.VBox
.call(this);
2177 this.setMinimumSize(50, 25);
2178 this._pieChart
= new WebInspector
.PieChart(150, WebInspector
.HeapSnapshotStatisticsView
._valueFormatter
, true);
2179 this._pieChart
.element
.classList
.add("heap-snapshot-stats-pie-chart");
2180 this.element
.appendChild(this._pieChart
.element
);
2181 this._labels
= this.element
.createChild("div", "heap-snapshot-stats-legend");
2185 * @param {number} value
2188 WebInspector
.HeapSnapshotStatisticsView
._valueFormatter = function(value
)
2190 return WebInspector
.UIString("%s KB", Number
.withThousandsSeparator(Math
.round(value
/ 1024)));
2193 WebInspector
.HeapSnapshotStatisticsView
.prototype = {
2195 * @param {number} value
2197 setTotal: function(value
)
2199 this._pieChart
.setTotal(value
);
2203 * @param {number} value
2204 * @param {string} name
2205 * @param {string=} color
2207 addRecord: function(value
, name
, color
)
2210 this._pieChart
.addSlice(value
, color
);
2212 var node
= this._labels
.createChild("div");
2213 var swatchDiv
= node
.createChild("div", "heap-snapshot-stats-swatch");
2214 var nameDiv
= node
.createChild("div", "heap-snapshot-stats-name");
2215 var sizeDiv
= node
.createChild("div", "heap-snapshot-stats-size");
2217 swatchDiv
.style
.backgroundColor
= color
;
2219 swatchDiv
.classList
.add("heap-snapshot-stats-empty-swatch");
2220 nameDiv
.textContent
= name
;
2221 sizeDiv
.textContent
= WebInspector
.HeapSnapshotStatisticsView
._valueFormatter(value
);
2224 __proto__
: WebInspector
.VBox
.prototype
2229 * @extends {WebInspector.Widget}
2230 * @param {?WebInspector.Target} target
2232 WebInspector
.HeapAllocationStackView = function(target
)
2234 WebInspector
.Widget
.call(this);
2235 this._target
= target
;;
2236 this._linkifier
= new WebInspector
.Linkifier();
2239 WebInspector
.HeapAllocationStackView
.prototype = {
2241 * @param {!WebInspector.HeapSnapshotProxy} snapshot
2242 * @param {number} snapshotNodeIndex
2244 setAllocatedObject: function(snapshot
, snapshotNodeIndex
)
2247 snapshot
.allocationStack(snapshotNodeIndex
, this._didReceiveAllocationStack
.bind(this));
2252 this.element
.removeChildren();
2253 this._linkifier
.reset();
2257 * @param {?Array.<!WebInspector.HeapSnapshotCommon.AllocationStackFrame>} frames
2259 _didReceiveAllocationStack: function(frames
)
2262 var stackDiv
= this.element
.createChild("div", "no-heap-allocation-stack");
2263 stackDiv
.createTextChild(WebInspector
.UIString("Stack was not recorded for this object because it had been allocated before this profile recording started."));
2267 var stackDiv
= this.element
.createChild("div", "heap-allocation-stack");
2268 for (var i
= 0; i
< frames
.length
; i
++) {
2269 var frame
= frames
[i
];
2270 var frameDiv
= stackDiv
.createChild("div", "stack-frame");
2271 var name
= frameDiv
.createChild("div");
2272 name
.textContent
= WebInspector
.beautifyFunctionName(frame
.functionName
);
2273 if (frame
.scriptId
) {
2274 var urlElement
= this._linkifier
.linkifyScriptLocation(this._target
, String(frame
.scriptId
), frame
.scriptName
, frame
.line
- 1, frame
.column
- 1);
2275 frameDiv
.appendChild(urlElement
);
2280 __proto__
: WebInspector
.Widget
.prototype