chrome-devtools-frontend
Version:
Chrome DevTools UI
1,486 lines (1,327 loc) • 71 kB
JavaScript
/*
* Copyright (C) 2011 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import * as Bindings from '../bindings/bindings.js';
import * as Common from '../common/common.js';
import * as Components from '../components/components.js';
import * as DataGrid from '../data_grid/data_grid.js';
import * as HeapSnapshotModel from '../heap_snapshot_model/heap_snapshot_model.js';
import * as Host from '../host/host.js';
import * as i18n from '../i18n/i18n.js';
import * as ObjectUI from '../object_ui/object_ui.js';
import * as PerfUI from '../perf_ui/perf_ui.js';
import * as Platform from '../platform/platform.js';
import * as Root from '../root/root.js';
import * as SDK from '../sdk/sdk.js';
import * as UI from '../ui/ui.js';
import {AllocationDataGrid, HeapSnapshotConstructorsDataGrid, HeapSnapshotContainmentDataGrid, HeapSnapshotDiffDataGrid, HeapSnapshotRetainmentDataGrid, HeapSnapshotSortableDataGrid, HeapSnapshotSortableDataGridEvents,} from './HeapSnapshotDataGrids.js'; // eslint-disable-line no-unused-vars
import {AllocationGridNode, HeapSnapshotGenericObjectNode, HeapSnapshotGridNode} from './HeapSnapshotGridNodes.js'; // eslint-disable-line no-unused-vars
import {HeapSnapshotProxy, HeapSnapshotWorkerProxy} from './HeapSnapshotProxy.js'; // eslint-disable-line no-unused-vars
import {HeapTimelineOverview, IdsRangeChanged, Samples} from './HeapTimelineOverview.js';
import * as ModuleUIStrings from './ModuleUIStrings.js';
import {DataDisplayDelegate, Events as ProfileHeaderEvents, ProfileEvents as ProfileTypeEvents, ProfileHeader, ProfileType} from './ProfileHeader.js'; // eslint-disable-line no-unused-vars
import {ProfileSidebarTreeElement} from './ProfileSidebarTreeElement.js';
import {instance} from './ProfileTypeRegistry.js';
export const UIStrings = {
/**
*@description Text to find an item
*/
find: 'Find',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
containment: 'Containment',
/**
*@description Retaining paths title text content in Heap Snapshot View of a profiler tool
*/
retainers: 'Retainers',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
allocationStack: 'Allocation stack',
/**
*@description Screen reader label for a select box that chooses the perspective in the Memory panel when vieweing a Heap Snapshot
*/
perspective: 'Perspective',
/**
*@description Screen reader label for a select box that chooses the snapshot to use as a base in the Memory panel when vieweing a Heap Snapshot
*/
baseSnapshot: 'Base snapshot',
/**
*@description Text to filter result items
*/
filter: 'Filter',
/**
*@description Filter label text in the Memory tool to filter class names for a heap snapshot
*/
classFilter: 'Class filter',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
code: 'Code',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
strings: 'Strings',
/**
*@description Label on a pie chart in the statistics view for the Heap Snapshot tool
*/
jsArrays: 'JS arrays',
/**
*@description Label on a pie chart in the statistics view for the Heap Snapshot tool
*/
typedArrays: 'Typed arrays',
/**
*@description Label on a pie chart in the statistics view for the Heap Snapshot tool
*/
systemObjects: 'System objects',
/**
*@description The reported total size used in the selected time frame of the allocation sampling profile
*@example {3 MB} PH1
*/
selectedSizeS: 'Selected size: {PH1}',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
allObjects: 'All objects',
/**
*@description Title in Heap Snapshot View of a profiler tool
*@example {Profile 2} PH1
*/
objectsAllocatedBeforeS: 'Objects allocated before {PH1}',
/**
*@description Title in Heap Snapshot View of a profiler tool
*@example {Profile 1} PH1
*@example {Profile 2} PH2
*/
objectsAllocatedBetweenSAndS: 'Objects allocated between {PH1} and {PH2}',
/**
*@description Text for the summary view
*/
summary: 'Summary',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
comparison: 'Comparison',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
allocation: 'Allocation',
/**
*@description Title text content in Heap Snapshot View of a profiler tool
*/
liveObjects: 'Live objects',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
statistics: 'Statistics',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
heapSnapshot: 'Heap snapshot',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
takeHeapSnapshot: 'Take heap snapshot',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
heapSnapshots: 'HEAP SNAPSHOTS',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
heapSnapshotProfilesShowMemory:
'Heap snapshot profiles show memory distribution among your page\'s JavaScript objects and related DOM nodes.',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
treatGlobalObjectsAsRoots:
'Treat global objects as roots (recommended, unchecking this exposes internal nodes and introduces excessive detail, but might help debugging cycles in retaining paths)',
/**
*@description Progress update that the profiler is capturing a snapshot of the heap
*/
snapshotting: 'Snapshotting…',
/**
*@description Profile title in Heap Snapshot View of a profiler tool
*@example {1} PH1
*/
snapshotD: 'Snapshot {PH1}',
/**
*@description Text for a percentage value
*@example {13.0} PH1
*/
percentagePlaceholder: '{PH1}%',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
allocationInstrumentationOn: 'Allocation instrumentation on timeline',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
stopRecordingHeapProfile: 'Stop recording heap profile',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
startRecordingHeapProfile: 'Start recording heap profile',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
recordAllocationStacksExtra: 'Record allocation stacks (extra performance overhead)',
/**
*@description Text in CPUProfile View of a profiler tool
*/
recording: 'Recording…',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
allocationTimelines: 'ALLOCATION TIMELINES',
/**
*@description Description(part 1) in Heap Snapshot View of a profiler tool
*/
AllocationTimelinesShowInstrumented:
'Allocation timelines show instrumented JavaScript memory allocations over time.',
/**
*@description Description(part 2) in Heap Snapshot View of a profiler tool
*/
OnceProfileIsRecorded: 'Once profile is recorded you can select a time interval to see objects that',
/**
*@description Description(part 3) in Heap Snapshot View of a profiler tool
*/
WereAllocatedWithinIt: 'were allocated within it and still alive by the end of recording.',
/**
*@description Description(part 4) in Heap Snapshot View of a profiler tool
*/
UseThisProfileTypeToIsolate: 'Use this profile type to isolate memory leaks.',
/**
*@description Text when something is loading
*/
loading: 'Loading…',
/**
*@description Text in Heap Snapshot View of a profiler tool
*@example {30} PH1
*/
savingD: 'Saving… {PH1}%',
/**
*@description Text in Heap Snapshot View of a profiler tool
*@example {1,021} PH1
*/
sKb: '{PH1} kB',
/**
*@description Text in Heap Snapshot View of a profiler tool
*/
heapMemoryUsage: 'Heap memory usage',
/**
*@description Text of a DOM element in Heap Snapshot View of a profiler tool
*/
stackWasNotRecordedForThisObject:
'Stack was not recorded for this object because it had been allocated before this profile recording started.',
};
const str_ = i18n.i18n.registerUIStrings('profiler/HeapSnapshotView.js', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
// The way this is handled is to workaround the strings inside the heap_snapshot_worker
// If strings are removed from inside the worker strings can be declared in this module
// as any other.
const moduleUIstr_ = i18n.i18n.registerUIStrings('profiler/ModuleUIStrings.js', ModuleUIStrings.UIStrings);
const moduleI18nString = i18n.i18n.getLocalizedString.bind(undefined, moduleUIstr_);
/**
* @implements {DataDisplayDelegate}
* @implements {UI.SearchableView.Searchable}
*/
export class HeapSnapshotView extends UI.View.SimpleView {
/**
* @param {!DataDisplayDelegate} dataDisplayDelegate
* @param {!HeapProfileHeader} profile
*/
constructor(dataDisplayDelegate, profile) {
super(i18nString(UIStrings.heapSnapshot));
/** @type {!Array<number>} */
this._searchResults = [];
this.element.classList.add('heap-snapshot-view');
this._profile = profile;
this._linkifier = new Components.Linkifier.Linkifier();
const profileType = profile.profileType();
profileType.addEventListener(HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
profileType.addEventListener(ProfileTypeEvents.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
const isHeapTimeline = profileType.id === TrackingHeapSnapshotProfileType.TypeId;
if (isHeapTimeline) {
this._createOverview();
}
this._parentDataDisplayDelegate = dataDisplayDelegate;
this._searchableView = new UI.SearchableView.SearchableView(this, null);
this._searchableView.setPlaceholder(i18nString(UIStrings.find), i18nString(UIStrings.find));
this._searchableView.show(this.element);
this._splitWidget = new UI.SplitWidget.SplitWidget(false, true, 'heapSnapshotSplitViewState', 200, 200);
this._splitWidget.show(this._searchableView.element);
const heapProfilerModel = profile.heapProfilerModel();
this._containmentDataGrid = new HeapSnapshotContainmentDataGrid(
heapProfilerModel, this, /* displayName */ i18nString(UIStrings.containment));
this._containmentDataGrid.addEventListener(DataGrid.DataGrid.Events.SelectedNode, this._selectionChanged, this);
this._containmentWidget = this._containmentDataGrid.asWidget();
this._containmentWidget.setMinimumSize(50, 25);
this._statisticsView = new HeapSnapshotStatisticsView();
this._constructorsDataGrid = new HeapSnapshotConstructorsDataGrid(heapProfilerModel, this);
this._constructorsDataGrid.addEventListener(DataGrid.DataGrid.Events.SelectedNode, this._selectionChanged, this);
this._constructorsWidget = this._constructorsDataGrid.asWidget();
this._constructorsWidget.setMinimumSize(50, 25);
this._diffDataGrid = new HeapSnapshotDiffDataGrid(heapProfilerModel, this);
this._diffDataGrid.addEventListener(DataGrid.DataGrid.Events.SelectedNode, this._selectionChanged, this);
this._diffWidget = this._diffDataGrid.asWidget();
this._diffWidget.setMinimumSize(50, 25);
/** @type {?AllocationDataGrid} */
this._allocationDataGrid = null;
if (isHeapTimeline) {
this._allocationDataGrid = new AllocationDataGrid(heapProfilerModel, this);
this._allocationDataGrid.addEventListener(
DataGrid.DataGrid.Events.SelectedNode, this._onSelectAllocationNode, this);
this._allocationWidget = this._allocationDataGrid.asWidget();
this._allocationWidget.setMinimumSize(50, 25);
this._allocationStackView = new HeapAllocationStackView(heapProfilerModel);
this._allocationStackView.setMinimumSize(50, 25);
this._tabbedPane = new UI.TabbedPane.TabbedPane();
}
this._retainmentDataGrid = new HeapSnapshotRetainmentDataGrid(heapProfilerModel, this);
this._retainmentWidget = this._retainmentDataGrid.asWidget();
this._retainmentWidget.setMinimumSize(50, 21);
this._retainmentWidget.element.classList.add('retaining-paths-view');
let splitWidgetResizer;
if (this._allocationStackView) {
this._tabbedPane = new UI.TabbedPane.TabbedPane();
this._tabbedPane.appendTab('retainers', i18nString(UIStrings.retainers), this._retainmentWidget);
this._tabbedPane.appendTab('allocation-stack', i18nString(UIStrings.allocationStack), this._allocationStackView);
splitWidgetResizer = this._tabbedPane.headerElement();
this._objectDetailsView = this._tabbedPane;
} else {
const retainmentViewHeader = document.createElement('div');
retainmentViewHeader.classList.add('heap-snapshot-view-resizer');
const retainingPathsTitleDiv = retainmentViewHeader.createChild('div', 'title');
const retainingPathsTitle = retainingPathsTitleDiv.createChild('span');
retainingPathsTitle.textContent = i18nString(UIStrings.retainers);
splitWidgetResizer = retainmentViewHeader;
this._objectDetailsView = new UI.Widget.VBox();
this._objectDetailsView.element.appendChild(retainmentViewHeader);
this._retainmentWidget.show(this._objectDetailsView.element);
}
this._splitWidget.hideDefaultResizer();
this._splitWidget.installResizer(splitWidgetResizer);
this._retainmentDataGrid.addEventListener(
DataGrid.DataGrid.Events.SelectedNode, this._inspectedObjectChanged, this);
this._retainmentDataGrid.reset();
this._perspectives = [];
this._comparisonPerspective = new ComparisonPerspective();
this._perspectives.push(new SummaryPerspective());
if (profile.profileType() !== instance.trackingHeapSnapshotProfileType) {
this._perspectives.push(this._comparisonPerspective);
}
this._perspectives.push(new ContainmentPerspective());
if (this._allocationWidget) {
this._perspectives.push(new AllocationPerspective());
}
this._perspectives.push(new StatisticsPerspective());
this._perspectiveSelect = new UI.Toolbar.ToolbarComboBox(
this._onSelectedPerspectiveChanged.bind(this), i18nString(UIStrings.perspective));
this._updatePerspectiveOptions();
this._baseSelect = new UI.Toolbar.ToolbarComboBox(this._changeBase.bind(this), i18nString(UIStrings.baseSnapshot));
this._baseSelect.setVisible(false);
this._updateBaseOptions();
this._filterSelect = new UI.Toolbar.ToolbarComboBox(this._changeFilter.bind(this), i18nString(UIStrings.filter));
this._filterSelect.setVisible(false);
this._updateFilterOptions();
this._classNameFilter = new UI.Toolbar.ToolbarInput(i18nString(UIStrings.classFilter));
this._classNameFilter.setVisible(false);
this._constructorsDataGrid.setNameFilter(this._classNameFilter);
this._diffDataGrid.setNameFilter(this._classNameFilter);
this._selectedSizeText = new UI.Toolbar.ToolbarText();
this._popoverHelper = new UI.PopoverHelper.PopoverHelper(this.element, this._getPopoverRequest.bind(this));
this._popoverHelper.setDisableOnClick(true);
this._popoverHelper.setHasPadding(true);
this.element.addEventListener('scroll', this._popoverHelper.hidePopover.bind(this._popoverHelper), true);
this._currentPerspectiveIndex = 0;
this._currentPerspective = this._perspectives[0];
this._currentPerspective.activate(this);
this._dataGrid = /** @type {?HeapSnapshotSortableDataGrid} */ (this._currentPerspective.masterGrid(this));
this._populate();
this._searchThrottler = new Common.Throttler.Throttler(0);
for (const existingProfile of this._profiles()) {
existingProfile.addEventListener(ProfileHeaderEvents.ProfileTitleChanged, this._updateControls, this);
}
/** @type {?HeapProfileHeader} */
this._baseProfile;
}
_createOverview() {
const profileType = this._profile.profileType();
this._trackingOverviewGrid = new HeapTimelineOverview();
this._trackingOverviewGrid.addEventListener(IdsRangeChanged, this._onIdsRangeChanged.bind(this));
if (!this._profile.fromFile() && profileType.profileBeingRecorded() === this._profile) {
profileType.addEventListener(TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
profileType.addEventListener(TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
this._trackingOverviewGrid.start();
}
}
_onStopTracking() {
this._profile.profileType().removeEventListener(
TrackingHeapSnapshotProfileType.HeapStatsUpdate, this._onHeapStatsUpdate, this);
this._profile.profileType().removeEventListener(
TrackingHeapSnapshotProfileType.TrackingStopped, this._onStopTracking, this);
if (this._trackingOverviewGrid) {
this._trackingOverviewGrid.stop();
}
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_onHeapStatsUpdate(event) {
const samples = event.data;
if (samples && this._trackingOverviewGrid) {
this._trackingOverviewGrid.setSamples(event.data);
}
}
/**
* @return {!UI.SearchableView.SearchableView}
*/
searchableView() {
return this._searchableView;
}
/**
* @override
* @param {?ProfileHeader} profile
* @return {?UI.Widget.Widget}
*/
showProfile(profile) {
return this._parentDataDisplayDelegate.showProfile(profile);
}
/**
* @override
* @param {!Protocol.HeapProfiler.HeapSnapshotObjectId} snapshotObjectId
* @param {string} perspectiveName
*/
showObject(snapshotObjectId, perspectiveName) {
if (Number(snapshotObjectId) <= this._profile.maxJSObjectId) {
this.selectLiveObject(perspectiveName, snapshotObjectId);
} else {
this._parentDataDisplayDelegate.showObject(snapshotObjectId, perspectiveName);
}
}
/**
* @override
* @param {number} nodeIndex
* @return {!Promise<?Element>}
*/
async linkifyObject(nodeIndex) {
const heapProfilerModel = this._profile.heapProfilerModel();
// heapProfilerModel is null if snapshot was loaded from file
if (!heapProfilerModel) {
return null;
}
const location = await this._profile.getLocation(nodeIndex);
if (!location) {
return null;
}
const debuggerModel = heapProfilerModel.runtimeModel().debuggerModel();
const rawLocation = debuggerModel.createRawLocationByScriptId(
String(location.scriptId), location.lineNumber, location.columnNumber);
if (!rawLocation) {
return null;
}
const script = rawLocation.script();
const sourceURL = script && script.sourceURL;
return sourceURL && this._linkifier ? this._linkifier.linkifyRawLocation(rawLocation, sourceURL) : null;
}
async _populate() {
const heapSnapshotProxy = await this._profile._loadPromise;
this._retrieveStatistics(heapSnapshotProxy);
if (this._dataGrid) {
this._dataGrid.setDataSource(heapSnapshotProxy, 0);
}
if (this._profile.profileType().id === TrackingHeapSnapshotProfileType.TypeId && this._profile.fromFile()) {
const samples = await heapSnapshotProxy.getSamples();
if (samples) {
console.assert(Boolean(samples.timestamps.length));
const profileSamples = new Samples();
profileSamples.sizes = samples.sizes;
profileSamples.ids = samples.lastAssignedIds;
profileSamples.timestamps = samples.timestamps;
profileSamples.max = samples.sizes;
profileSamples.totalTime = Math.max(samples.timestamps[samples.timestamps.length - 1] || 0, 10000);
if (this._trackingOverviewGrid) {
this._trackingOverviewGrid.setSamples(profileSamples);
}
}
}
const list = this._profiles();
const profileIndex = list.indexOf(this._profile);
this._baseSelect.setSelectedIndex(Math.max(0, profileIndex - 1));
if (this._trackingOverviewGrid) {
this._trackingOverviewGrid.updateGrid();
}
}
/**
* @param {!HeapSnapshotProxy} heapSnapshotProxy
* @return {!Promise<!HeapSnapshotModel.HeapSnapshotModel.Statistics>}
*/
async _retrieveStatistics(heapSnapshotProxy) {
const statistics = await heapSnapshotProxy.getStatistics();
const records = [
{value: statistics.code, color: '#f77', title: i18nString(UIStrings.code)},
{value: statistics.strings, color: '#5e5', title: i18nString(UIStrings.strings)},
{value: statistics.jsArrays, color: '#7af', title: i18nString(UIStrings.jsArrays)},
{value: statistics.native, color: '#fc5', title: i18nString(UIStrings.typedArrays)},
{value: statistics.system, color: '#98f', title: i18nString(UIStrings.systemObjects)},
];
this._statisticsView.setTotalAndRecords(statistics.total, records);
return statistics;
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_onIdsRangeChanged(event) {
const minId = event.data.minId;
const maxId = event.data.maxId;
this._selectedSizeText.setText(
i18nString(UIStrings.selectedSizeS, {PH1: Platform.NumberUtilities.bytesToString(event.data.size)}));
if (this._constructorsDataGrid.snapshot) {
this._constructorsDataGrid.setSelectionRange(minId, maxId);
}
}
/**
* @override
* @return {!Promise<!Array<!UI.Toolbar.ToolbarItem>>}
*/
async toolbarItems() {
/** @type {!Array<!UI.Toolbar.ToolbarItem>} */
const result = [this._perspectiveSelect, this._classNameFilter];
if (this._profile.profileType() !== instance.trackingHeapSnapshotProfileType) {
result.push(this._baseSelect, this._filterSelect);
}
result.push(this._selectedSizeText);
return result;
}
/**
* @override
*/
willHide() {
this._currentSearchResultIndex = -1;
this._popoverHelper.hidePopover();
}
/**
* @override
* @return {boolean}
*/
supportsCaseSensitiveSearch() {
return true;
}
/**
* @override
* @return {boolean}
*/
supportsRegexSearch() {
return false;
}
/**
* @override
*/
searchCanceled() {
this._currentSearchResultIndex = -1;
/** @type {!Array<number>} */
this._searchResults = [];
}
/**
* @param {?HeapSnapshotGridNode} node
*/
_selectRevealedNode(node) {
if (node) {
node.select();
}
}
/**
* @override
* @param {!UI.SearchableView.SearchConfig} searchConfig
* @param {boolean} shouldJump
* @param {boolean=} jumpBackwards
*/
performSearch(searchConfig, shouldJump, jumpBackwards) {
const nextQuery = new HeapSnapshotModel.HeapSnapshotModel.SearchConfig(
searchConfig.query.trim(), searchConfig.caseSensitive, searchConfig.isRegex, shouldJump,
jumpBackwards || false);
this._searchThrottler.schedule(this._performSearch.bind(this, nextQuery));
}
/**
* @param {!HeapSnapshotModel.HeapSnapshotModel.SearchConfig} nextQuery
* @return {!Promise<void>}
*/
async _performSearch(nextQuery) {
// Call searchCanceled since it will reset everything we need before doing a new search.
this.searchCanceled();
if (!this._currentPerspective.supportsSearch()) {
return;
}
this.currentQuery = nextQuery;
const query = nextQuery.query.trim();
if (!query) {
return;
}
if (query.charAt(0) === '@') {
const snapshotNodeId = parseInt(query.substring(1), 10);
if (isNaN(snapshotNodeId)) {
return;
}
if (!this._dataGrid) {
return;
}
const node = await this._dataGrid.revealObjectByHeapSnapshotId(String(snapshotNodeId));
this._selectRevealedNode(node);
return;
}
if (!this._profile._snapshotProxy || !this._dataGrid) {
return;
}
const filter = this._dataGrid.nodeFilter();
this._searchResults = filter ? await this._profile._snapshotProxy.search(this.currentQuery, filter) : [];
this._searchableView.updateSearchMatchesCount(this._searchResults.length);
if (this._searchResults.length) {
this._currentSearchResultIndex = nextQuery.jumpBackward ? this._searchResults.length - 1 : 0;
}
await this._jumpToSearchResult(this._currentSearchResultIndex);
}
/**
* @override
*/
jumpToNextSearchResult() {
if (!this._searchResults.length) {
return;
}
this._currentSearchResultIndex = (this._currentSearchResultIndex + 1) % this._searchResults.length;
this._searchThrottler.schedule(this._jumpToSearchResult.bind(this, this._currentSearchResultIndex));
}
/**
* @override
*/
jumpToPreviousSearchResult() {
if (!this._searchResults.length) {
return;
}
this._currentSearchResultIndex =
(this._currentSearchResultIndex + this._searchResults.length - 1) % this._searchResults.length;
this._searchThrottler.schedule(this._jumpToSearchResult.bind(this, this._currentSearchResultIndex));
}
/**
* @param {number} searchResultIndex
* @return {!Promise<void>}
*/
async _jumpToSearchResult(searchResultIndex) {
this._searchableView.updateCurrentMatchIndex(searchResultIndex);
if (searchResultIndex === -1) {
return;
}
if (!this._dataGrid) {
return;
}
const node = await this._dataGrid.revealObjectByHeapSnapshotId(String(this._searchResults[searchResultIndex]));
this._selectRevealedNode(node);
}
refreshVisibleData() {
if (!this._dataGrid) {
return;
}
let child = /** @type {?HeapSnapshotGridNode} */ (this._dataGrid.rootNode().children[0]);
while (child) {
child.refresh();
child = /** @type {?HeapSnapshotGridNode} */ (child.traverseNextNode(false, null, true));
}
}
_changeBase() {
if (this._baseProfile === this._profiles()[this._baseSelect.selectedIndex()]) {
return;
}
this._baseProfile = /** @type {!HeapProfileHeader} */ (this._profiles()[this._baseSelect.selectedIndex()]);
const dataGrid = /** @type {!HeapSnapshotDiffDataGrid} */ (this._dataGrid);
// Change set base data source only if main data source is already set.
if (dataGrid.snapshot) {
this._baseProfile._loadPromise.then(dataGrid.setBaseDataSource.bind(dataGrid));
}
if (!this.currentQuery || !this._searchResults) {
return;
}
// The current search needs to be performed again. First negate out previous match
// count by calling the search finished callback with a negative number of matches.
// Then perform the search again with the same query and callback.
this.performSearch(this.currentQuery, false);
}
_changeFilter() {
const profileIndex = this._filterSelect.selectedIndex() - 1;
if (!this._dataGrid) {
return;
}
/** @type {!HeapSnapshotConstructorsDataGrid} */ (this._dataGrid)
.filterSelectIndexChanged(/** @type {!Array<!HeapProfileHeader>} */ (this._profiles()), profileIndex);
if (!this.currentQuery || !this._searchResults) {
return;
}
// The current search needs to be performed again. First negate out previous match
// count by calling the search finished callback with a negative number of matches.
// Then perform the search again with the same query and callback.
this.performSearch(this.currentQuery, false);
}
/**
* @return {!Array.<!ProfileHeader>}
*/
_profiles() {
return this._profile.profileType().getProfiles();
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_selectionChanged(event) {
const selectedNode = /** @type {!HeapSnapshotGridNode} */ (event.data);
this._setSelectedNodeForDetailsView(selectedNode);
this._inspectedObjectChanged(event);
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_onSelectAllocationNode(event) {
const selectedNode = /** @type {!AllocationGridNode} */ (event.data);
this._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
this._setSelectedNodeForDetailsView(null);
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_inspectedObjectChanged(event) {
const selectedNode = /** @type {!HeapSnapshotGridNode} */ (event.data);
const heapProfilerModel = this._profile.heapProfilerModel();
if (heapProfilerModel && selectedNode instanceof HeapSnapshotGenericObjectNode) {
heapProfilerModel.addInspectedHeapObject(String(selectedNode.snapshotNodeId));
}
}
/**
* @param {?HeapSnapshotGridNode} nodeItem
*/
_setSelectedNodeForDetailsView(nodeItem) {
const dataSource = nodeItem && nodeItem.retainersDataSource();
if (dataSource) {
this._retainmentDataGrid.setDataSource(dataSource.snapshot, dataSource.snapshotNodeIndex);
if (this._allocationStackView) {
this._allocationStackView.setAllocatedObject(dataSource.snapshot, dataSource.snapshotNodeIndex);
}
} else {
if (this._allocationStackView) {
this._allocationStackView.clear();
}
this._retainmentDataGrid.reset();
}
}
/**
* @param {string} perspectiveTitle
* @return {!Promise<void>}
*/
async _changePerspectiveAndWait(perspectiveTitle) {
const perspectiveIndex = this._perspectives.findIndex(perspective => perspective.title() === perspectiveTitle);
if (perspectiveIndex === -1 || this._currentPerspectiveIndex === perspectiveIndex) {
return;
}
const dataGrid = this._perspectives[perspectiveIndex].masterGrid(this);
if (!dataGrid) {
return;
}
const promise = dataGrid.once(HeapSnapshotSortableDataGridEvents.ContentShown);
const option = this._perspectiveSelect.options().find(option => option.value === String(perspectiveIndex));
this._perspectiveSelect.select(/** @type {!Element} */ (option));
this._changePerspective(perspectiveIndex);
return promise;
}
async _updateDataSourceAndView() {
const dataGrid = this._dataGrid;
if (!dataGrid || dataGrid.snapshot) {
return;
}
const snapshotProxy = await this._profile._loadPromise;
if (this._dataGrid !== dataGrid) {
return;
}
if (dataGrid.snapshot !== snapshotProxy) {
dataGrid.setDataSource(snapshotProxy, 0);
}
if (dataGrid !== this._diffDataGrid) {
return;
}
if (!this._baseProfile) {
this._baseProfile = /** @type {!HeapProfileHeader} */ (this._profiles()[this._baseSelect.selectedIndex()]);
}
const baseSnapshotProxy = await this._baseProfile._loadPromise;
if (this._diffDataGrid.baseSnapshot !== baseSnapshotProxy) {
this._diffDataGrid.setBaseDataSource(baseSnapshotProxy);
}
}
/**
* @param {!Event} event
*/
_onSelectedPerspectiveChanged(event) {
this._changePerspective(Number(/** @type {!HTMLSelectElement} */ (event.target).selectedOptions[0].value));
}
/**
* @param {number} selectedIndex
*/
_changePerspective(selectedIndex) {
if (selectedIndex === this._currentPerspectiveIndex) {
return;
}
this._currentPerspectiveIndex = selectedIndex;
this._currentPerspective.deactivate(this);
const perspective = this._perspectives[selectedIndex];
this._currentPerspective = perspective;
this._dataGrid = /** @type {!HeapSnapshotSortableDataGrid} */ (perspective.masterGrid(this));
perspective.activate(this);
this.refreshVisibleData();
if (this._dataGrid) {
this._dataGrid.updateWidths();
}
this._updateDataSourceAndView();
if (!this.currentQuery || !this._searchResults) {
return;
}
// The current search needs to be performed again. First negate out previous match
// count by calling the search finished callback with a negative number of matches.
// Then perform the search again the with same query and callback.
this.performSearch(this.currentQuery, false);
}
/**
* @param {string} perspectiveName
* @param {!Protocol.HeapProfiler.HeapSnapshotObjectId} snapshotObjectId
*/
async selectLiveObject(perspectiveName, snapshotObjectId) {
await this._changePerspectiveAndWait(perspectiveName);
if (!this._dataGrid) {
return;
}
const node = await this._dataGrid.revealObjectByHeapSnapshotId(snapshotObjectId);
if (node) {
node.select();
} else {
Common.Console.Console.instance().error('Cannot find corresponding heap snapshot node');
}
}
/**
* @param {!Event} event
* @return {?UI.PopoverHelper.PopoverRequest}
*/
_getPopoverRequest(event) {
const span = /** @type {?HTMLElement} */ (
UI.UIUtils.enclosingNodeOrSelfWithNodeName(/** @type {!Node} */ (event.target), 'span'));
const row = UI.UIUtils.enclosingNodeOrSelfWithNodeName(/** @type {!Node} */ (event.target), 'row');
if (!row) {
return null;
}
if (!this._dataGrid) {
return null;
}
const node = this._dataGrid.dataGridNodeFromNode(row) || this._containmentDataGrid.dataGridNodeFromNode(row) ||
this._constructorsDataGrid.dataGridNodeFromNode(row) || this._diffDataGrid.dataGridNodeFromNode(row) ||
(this._allocationDataGrid && this._allocationDataGrid.dataGridNodeFromNode(row)) ||
this._retainmentDataGrid.dataGridNodeFromNode(row);
const heapProfilerModel = this._profile.heapProfilerModel();
if (!node || !span || !heapProfilerModel) {
return null;
}
/** @type {?ObjectUI.ObjectPopoverHelper.ObjectPopoverHelper} */
let objectPopoverHelper;
return {
box: span.boxInWindow(),
show: async popover => {
if (!heapProfilerModel) {
return false;
}
const remoteObject =
await /** @type {!HeapSnapshotGridNode} */ (node).queryObjectContent(heapProfilerModel, 'popover');
if (!remoteObject) {
return false;
}
objectPopoverHelper =
await ObjectUI.ObjectPopoverHelper.ObjectPopoverHelper.buildObjectPopover(remoteObject, popover);
if (!objectPopoverHelper) {
heapProfilerModel.runtimeModel().releaseObjectGroup('popover');
return false;
}
return true;
},
hide: () => {
heapProfilerModel.runtimeModel().releaseObjectGroup('popover');
if (objectPopoverHelper) {
objectPopoverHelper.dispose();
}
}
};
}
_updatePerspectiveOptions() {
const multipleSnapshots = this._profiles().length > 1;
this._perspectiveSelect.removeOptions();
this._perspectives.forEach((perspective, index) => {
if (multipleSnapshots || perspective !== this._comparisonPerspective) {
this._perspectiveSelect.createOption(perspective.title(), String(index));
}
});
}
_updateBaseOptions() {
const list = this._profiles();
const selectedIndex = this._baseSelect.selectedIndex();
this._baseSelect.removeOptions();
for (const item of list) {
this._baseSelect.createOption(item.title);
}
if (selectedIndex > -1) {
this._baseSelect.setSelectedIndex(selectedIndex);
}
}
_updateFilterOptions() {
const list = this._profiles();
const selectedIndex = this._filterSelect.selectedIndex();
this._filterSelect.removeOptions();
this._filterSelect.createOption(i18nString(UIStrings.allObjects));
for (let i = 0; i < list.length; ++i) {
let title;
if (!i) {
title = i18nString(UIStrings.objectsAllocatedBeforeS, {PH1: list[i].title});
} else {
title = i18nString(UIStrings.objectsAllocatedBetweenSAndS, {PH1: list[i - 1].title, PH2: list[i].title});
}
this._filterSelect.createOption(title);
}
if (selectedIndex > -1) {
this._filterSelect.setSelectedIndex(selectedIndex);
}
}
_updateControls() {
this._updatePerspectiveOptions();
this._updateBaseOptions();
this._updateFilterOptions();
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_onReceiveSnapshot(event) {
this._updateControls();
const profile = event.data;
profile.addEventListener(ProfileHeaderEvents.ProfileTitleChanged, this._updateControls, this);
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_onProfileHeaderRemoved(event) {
const profile = event.data;
profile.removeEventListener(ProfileHeaderEvents.ProfileTitleChanged, this._updateControls, this);
if (this._profile === profile) {
this.detach();
this._profile.profileType().removeEventListener(
HeapSnapshotProfileType.SnapshotReceived, this._onReceiveSnapshot, this);
this._profile.profileType().removeEventListener(
ProfileTypeEvents.RemoveProfileHeader, this._onProfileHeaderRemoved, this);
this.dispose();
} else {
this._updateControls();
}
}
dispose() {
this._linkifier.dispose();
this._popoverHelper.dispose();
if (this._allocationStackView) {
this._allocationStackView.clear();
if (this._allocationDataGrid) {
this._allocationDataGrid.dispose();
}
}
this._onStopTracking();
if (this._trackingOverviewGrid) {
this._trackingOverviewGrid.removeEventListener(IdsRangeChanged, this._onIdsRangeChanged.bind(this));
}
}
}
export class Perspective {
/**
* @param {string} title
*/
constructor(title) {
this._title = title;
}
/**
* @param {!HeapSnapshotView} heapSnapshotView
*/
activate(heapSnapshotView) {
}
/**
* @param {!HeapSnapshotView} heapSnapshotView
*/
deactivate(heapSnapshotView) {
heapSnapshotView._baseSelect.setVisible(false);
heapSnapshotView._filterSelect.setVisible(false);
heapSnapshotView._classNameFilter.setVisible(false);
if (heapSnapshotView._trackingOverviewGrid) {
heapSnapshotView._trackingOverviewGrid.detach();
}
if (heapSnapshotView._allocationWidget) {
heapSnapshotView._allocationWidget.detach();
}
if (heapSnapshotView._statisticsView) {
heapSnapshotView._statisticsView.detach();
}
heapSnapshotView._splitWidget.detach();
heapSnapshotView._splitWidget.detachChildWidgets();
}
/**
* @param {!HeapSnapshotView} heapSnapshotView
* @return {?DataGrid.DataGrid.DataGridImpl<!HeapSnapshotGridNode>}
*/
masterGrid(heapSnapshotView) {
return null;
}
/**
* @return {string}
*/
title() {
return this._title;
}
/**
* @return {boolean}
*/
supportsSearch() {
return false;
}
}
export class SummaryPerspective extends Perspective {
constructor() {
super(i18nString(UIStrings.summary));
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
*/
activate(heapSnapshotView) {
heapSnapshotView._splitWidget.setMainWidget(heapSnapshotView._constructorsWidget);
heapSnapshotView._splitWidget.setSidebarWidget(heapSnapshotView._objectDetailsView);
heapSnapshotView._splitWidget.show(heapSnapshotView._searchableView.element);
heapSnapshotView._filterSelect.setVisible(true);
heapSnapshotView._classNameFilter.setVisible(true);
if (!heapSnapshotView._trackingOverviewGrid) {
return;
}
heapSnapshotView._trackingOverviewGrid.show(
heapSnapshotView._searchableView.element, heapSnapshotView._splitWidget.element);
heapSnapshotView._trackingOverviewGrid.update();
heapSnapshotView._trackingOverviewGrid.updateGrid();
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
* @return {?DataGrid.DataGrid.DataGridImpl<!HeapSnapshotGridNode>}
*/
masterGrid(heapSnapshotView) {
return heapSnapshotView._constructorsDataGrid;
}
/**
* @override
* @return {boolean}
*/
supportsSearch() {
return true;
}
}
export class ComparisonPerspective extends Perspective {
constructor() {
super(i18nString(UIStrings.comparison));
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
*/
activate(heapSnapshotView) {
heapSnapshotView._splitWidget.setMainWidget(heapSnapshotView._diffWidget);
heapSnapshotView._splitWidget.setSidebarWidget(heapSnapshotView._objectDetailsView);
heapSnapshotView._splitWidget.show(heapSnapshotView._searchableView.element);
heapSnapshotView._baseSelect.setVisible(true);
heapSnapshotView._classNameFilter.setVisible(true);
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
* @return {?DataGrid.DataGrid.DataGridImpl<!HeapSnapshotGridNode>}
*/
masterGrid(heapSnapshotView) {
return heapSnapshotView._diffDataGrid;
}
/**
* @override
* @return {boolean}
*/
supportsSearch() {
return true;
}
}
export class ContainmentPerspective extends Perspective {
constructor() {
super(i18nString(UIStrings.containment));
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
*/
activate(heapSnapshotView) {
heapSnapshotView._splitWidget.setMainWidget(heapSnapshotView._containmentWidget);
heapSnapshotView._splitWidget.setSidebarWidget(heapSnapshotView._objectDetailsView);
heapSnapshotView._splitWidget.show(heapSnapshotView._searchableView.element);
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
* @return {?DataGrid.DataGrid.DataGridImpl<!HeapSnapshotGridNode>}
*/
masterGrid(heapSnapshotView) {
return heapSnapshotView._containmentDataGrid;
}
}
export class AllocationPerspective extends Perspective {
constructor() {
super(i18nString(UIStrings.allocation));
this._allocationSplitWidget =
new UI.SplitWidget.SplitWidget(false, true, 'heapSnapshotAllocationSplitViewState', 200, 200);
this._allocationSplitWidget.setSidebarWidget(new UI.Widget.VBox());
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
*/
activate(heapSnapshotView) {
if (heapSnapshotView._allocationWidget) {
this._allocationSplitWidget.setMainWidget(heapSnapshotView._allocationWidget);
}
heapSnapshotView._splitWidget.setMainWidget(heapSnapshotView._constructorsWidget);
heapSnapshotView._splitWidget.setSidebarWidget(heapSnapshotView._objectDetailsView);
const allocatedObjectsView = new UI.Widget.VBox();
const resizer = document.createElement('div');
resizer.classList.add('heap-snapshot-view-resizer');
const title = resizer.createChild('div', 'title').createChild('span');
title.textContent = i18nString(UIStrings.liveObjects);
this._allocationSplitWidget.hideDefaultResizer();
this._allocationSplitWidget.installResizer(resizer);
allocatedObjectsView.element.appendChild(resizer);
heapSnapshotView._splitWidget.show(allocatedObjectsView.element);
this._allocationSplitWidget.setSidebarWidget(allocatedObjectsView);
this._allocationSplitWidget.show(heapSnapshotView._searchableView.element);
heapSnapshotView._constructorsDataGrid.clear();
if (heapSnapshotView._allocationDataGrid) {
const selectedNode = /** @type {!AllocationGridNode} */ (heapSnapshotView._allocationDataGrid.selectedNode);
if (selectedNode) {
heapSnapshotView._constructorsDataGrid.setAllocationNodeId(selectedNode.allocationNodeId());
}
}
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
*/
deactivate(heapSnapshotView) {
this._allocationSplitWidget.detach();
super.deactivate(heapSnapshotView);
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
* @return {?DataGrid.DataGrid.DataGridImpl<!HeapSnapshotGridNode>}
*/
masterGrid(heapSnapshotView) {
return heapSnapshotView._allocationDataGrid;
}
}
export class StatisticsPerspective extends Perspective {
constructor() {
super(i18nString(UIStrings.statistics));
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
*/
activate(heapSnapshotView) {
heapSnapshotView._statisticsView.show(heapSnapshotView._searchableView.element);
}
/**
* @override
* @param {!HeapSnapshotView} heapSnapshotView
* @return {?DataGrid.DataGrid.DataGridImpl<!HeapSnapshotGridNode>}
*/
masterGrid(heapSnapshotView) {
return null;
}
}
/**
* @implements {SDK.SDKModel.SDKModelObserver<!SDK.HeapProfilerModel.HeapProfilerModel>}
*/
export class HeapSnapshotProfileType extends ProfileType {
/**
* @param {string=} id
* @param {string=} title
*/
constructor(id, title) {
super(id || HeapSnapshotProfileType.TypeId, title || i18nString(UIStrings.heapSnapshot));
SDK.SDKModel.TargetManager.instance().observeModels(SDK.HeapProfilerModel.HeapProfilerModel, this);
SDK.SDKModel.TargetManager.instance().addModelListener(
SDK.HeapProfilerModel.HeapProfilerModel, SDK.HeapProfilerModel.Events.ResetProfiles, this._resetProfiles, this);
SDK.SDKModel.TargetManager.instance().addModelListener(
SDK.HeapProfilerModel.HeapProfilerModel, SDK.HeapProfilerModel.Events.AddHeapSnapshotChunk,
this._addHeapSnapshotChunk, this);
SDK.SDKModel.TargetManager.instance().addModelListener(
SDK.HeapProfilerModel.HeapProfilerModel, SDK.HeapProfilerModel.Events.ReportHeapSnapshotProgress,
this._reportHeapSnapshotProgress, this);
this._treatGlobalObjectsAsRoots =
Common.Settings.Settings.instance().createSetting('treatGlobalObjectsAsRoots', true);
/** @type {?UI.UIUtils.CheckboxLabel} */
this._customContent = null;
}
/**
* @override
* @param {!SDK.HeapProfilerModel.HeapProfilerModel} heapProfilerModel
*/
modelAdded(heapProfilerModel) {
heapProfilerModel.enable();
}
/**
* @override
* @param {!SDK.HeapProfilerModel.HeapProfilerModel} heapProfilerModel
*/
modelRemoved(heapProfilerModel) {
}
/**
* @override
* @return {!Array<!HeapProfileHeader>}
*/
getProfiles() {
return /** @type {!Array<!HeapProfileHeader>} */ (super.getProfiles());
}
/**
* @override
* @return {string}
*/
fileExtension() {
return '.heapsnapshot';
}
/**
* @override
*/
get buttonTooltip() {
return i18nString(UIStrings.takeHeapSnapshot);
}
/**
* @override
* @return {boolean}
*/
isInstantProfile() {
return true;
}
/**
* @override
* @return {boolean}
*/
buttonClicked() {
this._takeHeapSnapshot();
Host.userMetrics.actionTaken(Host.UserMetrics.Action.ProfilesHeapProfileTaken);
return false;
}
/**
* @override
*/
get treeItemTitle() {
return i18nString(UIStrings.heapSnapshots);
}
/**
* @override
*/
get description() {
return i18nString(UIStrings.heapSnapshotProfilesShowMemory);
}
/**
* @override
* @return {?Element}
*/
customContent() {
const checkboxSetting = UI.SettingsUI.createSettingCheckbox(
i18nString(UIStrings.treatGlobalObjectsAsRoots), this._treatGlobalObjectsAsRoots, true);
this._customContent = /** @type {!UI.UIUtils.CheckboxLabel} */ (checkboxSetting);
const showOptionToNotTreatGlobalObjectsAsRoots =
Root.Runtime.experiments.isEnabled('showOptionToNotTreatGlobalObjectsAsRoots');
return showOptionToNotTreatGlobalObjectsAsRoots ? checkboxSetting : null;
}
/**
* @override
* @param {boolean} enable
*/
setCustomContentEnabled(enable) {
if (this._customContent) {
this._customContent.checkboxElement.disabled = !enable;
}
}
/**
* @override
* @param {string} title
* @return {!ProfileHeader}
*/
createProfileLoadedFromFile(title) {
return new HeapProfileHeader(null, this, title);
}
async _takeHeapSnapshot() {
if (this.profileBeingRecorded()) {
return;
}
const heapProfilerModel = UI.Context.Context.instance().flavor(SDK.HeapProfilerModel.HeapProfilerModel);
if (!heapProfilerModel) {
return;
}
let profile = new HeapProfileHeader(heapProfilerModel, this);
this.setProfileBeingRecorded(profile);
this.addProfile(profile);
profile.updateStatus(i18nString(UIStrings.snapshotting));
await heapProfilerModel.takeHeapSnapshot(true, this._treatGlobalObjectsAsRoots.get());
// ------------ ASYNC ------------
profile = /** @type {!HeapProfileHeader} */ (this.profileBeingRecorded());
profile.title = i18nString(UIStrings.snapshotD, {PH1: profile.uid});
profile._finishLoad();
this.setProfileBeingRecorded(null);
this.dispatchEventToListeners(ProfileTypeEvents.ProfileComplete, profile);
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_addHeapSnapshotChunk(event) {
const profile = /** @type {?HeapProfileHeader} */ (this.profileBeingRecorded());
if (!profile) {
return;
}
const chunk = /** @type {string} */ (event.data);
profile.transferChunk(chunk);
}
/**
* @param {!Common.EventTarget.EventTargetEvent} event
*/
_reportHeapSnapshotProgress(event) {
const