blob: f79124d49e3f68a5f050e54e984843c3a44b6e7d [file] [log] [blame]
Alexei Filippov933306d2019-03-26 17:40:511// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Jan Scheffler77e3e682020-07-30 09:00:545// @ts-nocheck
6// TODO(crbug.com/1011811): Enable TypeScript compiler checks
7
Tim van der Lippee54f66e2020-02-03 18:20:328import * as Common from '../common/common.js';
9import * as DataGrid from '../data_grid/data_grid.js';
10import * as SDK from '../sdk/sdk.js';
11import * as UI from '../ui/ui.js';
Paul Lewis7cbdcae2020-03-19 10:48:1212import * as Workspace from '../workspace/workspace.js';
Tim van der Lippee54f66e2020-02-03 18:20:3213
Paul Lewis0d43b802020-01-14 13:34:2314import {SamplingHeapProfileNode} from './HeapProfileView.js'; // eslint-disable-line no-unused-vars
15
Alexei Filippov933306d2019-03-26 17:40:5116/**
Tim van der Lippee54f66e2020-02-03 18:20:3217 * @extends {UI.Widget.VBox}
Alexei Filippov933306d2019-03-26 17:40:5118 */
Tim van der Lippee54f66e2020-02-03 18:20:3219export class LiveHeapProfileView extends UI.Widget.VBox {
Alexei Filippov933306d2019-03-26 17:40:5120 constructor() {
21 super(true);
Tim van der Lippe6288cf32020-01-07 16:58:4022 /** @type {!Map<string, !GridNode>} */
Alexei Filippov933306d2019-03-26 17:40:5123 this._gridNodeByUrl = new Map();
Jack Franklin71519f82020-11-03 12:08:5924 this.registerRequiredCSS('profiler/liveHeapProfile.css', {enableLegacyPatching: true});
Alexei Filippovfc6ad272019-06-10 18:51:5525
Paul Lewis2d7d65c2020-03-16 17:26:3026 this._setting = Common.Settings.Settings.instance().moduleSetting('memoryLiveHeapProfile');
Tim van der Lippee54f66e2020-02-03 18:20:3227 const toolbar = new UI.Toolbar.Toolbar('live-heap-profile-toolbar', this.contentElement);
Alexei Filippovfc6ad272019-06-10 18:51:5528
29 this._toggleRecordAction =
Tim van der Lippe177c0b22020-08-19 14:56:0230 /** @type {!UI.Action.Action }*/ (
31 UI.ActionRegistry.ActionRegistry.instance().action('live-heap-profile.toggle-recording'));
Tim van der Lippee54f66e2020-02-03 18:20:3232 this._toggleRecordButton = UI.Toolbar.Toolbar.createActionButton(this._toggleRecordAction);
Alexei Filippovfc6ad272019-06-10 18:51:5533 this._toggleRecordButton.setToggled(this._setting.get());
34 toolbar.appendToolbarItem(this._toggleRecordButton);
35
Paul Lewisdaac1062020-03-05 14:37:1036 const mainTarget = SDK.SDKModel.TargetManager.instance().mainTarget();
Tim van der Lippee54f66e2020-02-03 18:20:3237 if (mainTarget && mainTarget.model(SDK.ResourceTreeModel.ResourceTreeModel)) {
Alexei Filippovfc6ad272019-06-10 18:51:5538 const startWithReloadAction =
Tim van der Lippe177c0b22020-08-19 14:56:0239 /** @type {!UI.Action.Action }*/ (
40 UI.ActionRegistry.ActionRegistry.instance().action('live-heap-profile.start-with-reload'));
Tim van der Lippee54f66e2020-02-03 18:20:3241 this._startWithReloadButton = UI.Toolbar.Toolbar.createActionButton(startWithReloadAction);
Alexei Filippovfc6ad272019-06-10 18:51:5542 toolbar.appendToolbarItem(this._startWithReloadButton);
43 }
44
45 this._dataGrid = this._createDataGrid();
46 this._dataGrid.asWidget().show(this.contentElement);
47
48 this._currentPollId = 0;
49 }
50
51 /**
Tim van der Lippee54f66e2020-02-03 18:20:3252 * @return {!DataGrid.SortableDataGrid.SortableDataGrid}
Alexei Filippovfc6ad272019-06-10 18:51:5553 */
54 _createDataGrid() {
Alexei Filippov933306d2019-03-26 17:40:5155 const columns = [
56 {
57 id: 'size',
58 title: ls`JS Heap`,
59 width: '72px',
60 fixedWidth: true,
61 sortable: true,
62 align: DataGrid.DataGrid.Align.Right,
Alexei Filippovfc6ad272019-06-10 18:51:5563 sort: DataGrid.DataGrid.Order.Descending,
64 tooltip: ls`Allocated JS heap size currently in use`,
Alexei Filippov933306d2019-03-26 17:40:5165 },
Alexei Filippovfc6ad272019-06-10 18:51:5566 {
67 id: 'isolates',
68 title: ls`VMs`,
69 width: '40px',
70 fixedWidth: true,
71 align: DataGrid.DataGrid.Align.Right,
72 tooltip: ls`Number of VMs sharing the same script source`
73 },
74 {id: 'url', title: ls`Script URL`, fixedWidth: false, sortable: true, tooltip: ls`URL of the script source`}
Alexei Filippov933306d2019-03-26 17:40:5175 ];
Tim van der Lippee54f66e2020-02-03 18:20:3276 const dataGrid = new DataGrid.SortableDataGrid.SortableDataGrid({displayName: ls`Heap Profile`, columns});
Alexei Filippovfc6ad272019-06-10 18:51:5577 dataGrid.setResizeMethod(DataGrid.DataGrid.ResizeMethod.Last);
78 dataGrid.element.classList.add('flex-auto');
79 dataGrid.element.addEventListener('keydown', this._onKeyDown.bind(this), false);
80 dataGrid.addEventListener(DataGrid.DataGrid.Events.OpenedNode, this._revealSourceForSelectedNode, this);
81 dataGrid.addEventListener(DataGrid.DataGrid.Events.SortingChanged, this._sortingChanged, this);
82 for (const info of columns) {
83 const headerCell = dataGrid.headerTableHeader(info.id);
Tim van der Lippe1d6e57a2019-09-30 11:55:3484 if (headerCell) {
Alexei Filippovfc6ad272019-06-10 18:51:5585 headerCell.setAttribute('title', info.tooltip);
Tim van der Lippe1d6e57a2019-09-30 11:55:3486 }
Alexei Filippovfc6ad272019-06-10 18:51:5587 }
88 return dataGrid;
Alexei Filippov933306d2019-03-26 17:40:5189 }
90
91 /**
92 * @override
93 */
94 wasShown() {
95 this._poll();
Alexei Filippovfc6ad272019-06-10 18:51:5596 this._setting.addChangeListener(this._settingChanged, this);
Alexei Filippov933306d2019-03-26 17:40:5197 }
98
99 /**
100 * @override
101 */
102 willHide() {
Alexei Filippov3f8ff4b2019-04-30 01:08:41103 ++this._currentPollId;
Alexei Filippovfc6ad272019-06-10 18:51:55104 this._setting.removeChangeListener(this._settingChanged, this);
105 }
106
107 /**
Tim van der Lippec02a97c2020-02-14 14:39:27108 * @param {!Common.EventTarget.EventTargetEvent} value
Alexei Filippovfc6ad272019-06-10 18:51:55109 */
110 _settingChanged(value) {
111 this._toggleRecordButton.setToggled(/** @type {boolean} */ (value.data));
Alexei Filippov933306d2019-03-26 17:40:51112 }
113
114 async _poll() {
Alexei Filippov3f8ff4b2019-04-30 01:08:41115 const pollId = this._currentPollId;
116 do {
Tim van der Lipped5de9a02020-09-03 12:55:49117 const isolates = Array.from(SDK.IsolateManager.IsolateManager.instance().isolates());
Alexei Filippov44a88362019-05-09 01:41:05118 const profiles = await Promise.all(
119 isolates.map(isolate => isolate.heapProfilerModel() && isolate.heapProfilerModel().getSamplingProfile()));
Tim van der Lippe1d6e57a2019-09-30 11:55:34120 if (this._currentPollId !== pollId) {
Alexei Filippov3f8ff4b2019-04-30 01:08:41121 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34122 }
Alexei Filippov44a88362019-05-09 01:41:05123 this._update(isolates, profiles);
Alexei Filippov3f8ff4b2019-04-30 01:08:41124 await new Promise(r => setTimeout(r, 3000));
125 } while (this._currentPollId === pollId);
Alexei Filippov933306d2019-03-26 17:40:51126 }
127
128 /**
Alexei Filippov44a88362019-05-09 01:41:05129 * @param {!Array<!SDK.IsolateManager.Isolate>} isolates
130 * @param {!Array<?Protocol.HeapProfiler.SamplingHeapProfile>} profiles
Alexei Filippov933306d2019-03-26 17:40:51131 */
Alexei Filippov44a88362019-05-09 01:41:05132 _update(isolates, profiles) {
133 /** @type {!Map<string, !{size: number, isolates: !Set<!SDK.IsolateManager.Isolate>}>} */
134 const dataByUrl = new Map();
135 profiles.forEach((profile, index) => {
Tim van der Lippe1d6e57a2019-09-30 11:55:34136 if (profile) {
Alexei Filippov44a88362019-05-09 01:41:05137 processNodeTree(isolates[index], '', profile.head);
Tim van der Lippe1d6e57a2019-09-30 11:55:34138 }
Alexei Filippov44a88362019-05-09 01:41:05139 });
Alexei Filippov933306d2019-03-26 17:40:51140
141 const rootNode = this._dataGrid.rootNode();
142 const exisitingNodes = new Set();
Alexei Filippov44a88362019-05-09 01:41:05143 for (const pair of dataByUrl) {
Alexei Filippov933306d2019-03-26 17:40:51144 const url = /** @type {string} */ (pair[0]);
Alexei Filippov44a88362019-05-09 01:41:05145 const size = /** @type {number} */ (pair[1].size);
146 const isolateCount = /** @type {number} */ (pair[1].isolates.size);
Alexei Filippov933306d2019-03-26 17:40:51147 if (!url) {
148 console.info(`Node with empty URL: ${size} bytes`); // eslint-disable-line no-console
149 continue;
150 }
151 let node = this._gridNodeByUrl.get(url);
152 if (node) {
Alexei Filippov44a88362019-05-09 01:41:05153 node.updateNode(size, isolateCount);
Alexei Filippov933306d2019-03-26 17:40:51154 } else {
Tim van der Lippe6288cf32020-01-07 16:58:40155 node = new GridNode(url, size, isolateCount);
Alexei Filippov933306d2019-03-26 17:40:51156 this._gridNodeByUrl.set(url, node);
157 rootNode.appendChild(node);
158 }
159 exisitingNodes.add(node);
160 }
161
162 for (const node of rootNode.children.slice()) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34163 if (!exisitingNodes.has(node)) {
Alexei Filippov933306d2019-03-26 17:40:51164 node.remove();
Tim van der Lippe1d6e57a2019-09-30 11:55:34165 }
Andres Olivares03d9c752020-10-01 15:08:11166 this._gridNodeByUrl.delete(node._url);
Alexei Filippov933306d2019-03-26 17:40:51167 }
168
169 this._sortingChanged();
170
171 /**
Alexei Filippov44a88362019-05-09 01:41:05172 * @param {!SDK.IsolateManager.Isolate} isolate
Alexei Filippov933306d2019-03-26 17:40:51173 * @param {string} parentUrl
174 * @param {!Protocol.HeapProfiler.SamplingHeapProfileNode} node
175 */
Alexei Filippov44a88362019-05-09 01:41:05176 function processNodeTree(isolate, parentUrl, node) {
Alexei Filippov933306d2019-03-26 17:40:51177 const url = node.callFrame.url || parentUrl || systemNodeName(node) || anonymousScriptName(node);
Alexei Filippov44a88362019-05-09 01:41:05178 node.children.forEach(processNodeTree.bind(null, isolate, url));
Tim van der Lippe1d6e57a2019-09-30 11:55:34179 if (!node.selfSize) {
Alexei Filippov44a88362019-05-09 01:41:05180 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34181 }
Alexei Filippov44a88362019-05-09 01:41:05182 let data = dataByUrl.get(url);
183 if (!data) {
184 data = {size: 0, isolates: new Set()};
185 dataByUrl.set(url, data);
186 }
187 data.size += node.selfSize;
188 data.isolates.add(isolate);
Alexei Filippov933306d2019-03-26 17:40:51189 }
190
191 /**
192 * @param {!Protocol.HeapProfiler.SamplingHeapProfileNode} node
193 * @return {string}
194 */
195 function systemNodeName(node) {
196 const name = node.callFrame.functionName;
197 return name.startsWith('(') && name !== '(root)' ? name : '';
198 }
199
200 /**
201 * @param {!Protocol.HeapProfiler.SamplingHeapProfileNode} node
202 * @return {string}
203 */
204 function anonymousScriptName(node) {
Tim van der Lippee54f66e2020-02-03 18:20:32205 return Number(node.callFrame.scriptId) ?
206 Common.UIString.UIString('(Anonymous Script %s)', node.callFrame.scriptId) :
207 '';
Alexei Filippov933306d2019-03-26 17:40:51208 }
209 }
210
211 /**
212 * @param {!Event} event
213 */
214 _onKeyDown(event) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34215 if (!isEnterKey(event)) {
Alexei Filippov933306d2019-03-26 17:40:51216 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34217 }
Alexei Filippov933306d2019-03-26 17:40:51218 event.consume(true);
219 this._revealSourceForSelectedNode();
220 }
221
222 _revealSourceForSelectedNode() {
223 const node = this._dataGrid.selectedNode;
Tim van der Lippe1d6e57a2019-09-30 11:55:34224 if (!node || !node._url) {
Alexei Filippov933306d2019-03-26 17:40:51225 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34226 }
Paul Lewis7cbdcae2020-03-19 10:48:12227 const sourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURL(node._url);
Tim van der Lippe1d6e57a2019-09-30 11:55:34228 if (sourceCode) {
Alexei Filippov933306d2019-03-26 17:40:51229 Common.Revealer.reveal(sourceCode);
Tim van der Lippe1d6e57a2019-09-30 11:55:34230 }
Alexei Filippov933306d2019-03-26 17:40:51231 }
232
233 _sortingChanged() {
234 const columnId = this._dataGrid.sortColumnId();
Tim van der Lippe1d6e57a2019-09-30 11:55:34235 if (!columnId) {
Alexei Filippov933306d2019-03-26 17:40:51236 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34237 }
Alexei Filippov933306d2019-03-26 17:40:51238 const sortByUrl = (a, b) => b._url.localeCompare(a._url);
239 const sortBySize = (a, b) => b._size - a._size;
240 const sortFunction = columnId === 'url' ? sortByUrl : sortBySize;
241 this._dataGrid.sortNodes(sortFunction, this._dataGrid.isSortOrderAscending());
242 }
Alexei Filippovfc6ad272019-06-10 18:51:55243
244 _toggleRecording() {
245 const enable = !this._setting.get();
Tim van der Lippe1d6e57a2019-09-30 11:55:34246 if (enable) {
Alexei Filippovfc6ad272019-06-10 18:51:55247 this._startRecording(false);
Tim van der Lippe1d6e57a2019-09-30 11:55:34248 } else {
Alexei Filippovfc6ad272019-06-10 18:51:55249 this._stopRecording();
Tim van der Lippe1d6e57a2019-09-30 11:55:34250 }
Alexei Filippovfc6ad272019-06-10 18:51:55251 }
252
253 /**
254 * @param {boolean=} reload
255 */
256 _startRecording(reload) {
257 this._setting.set(true);
Tim van der Lippe1d6e57a2019-09-30 11:55:34258 if (!reload) {
Alexei Filippovfc6ad272019-06-10 18:51:55259 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34260 }
Paul Lewisdaac1062020-03-05 14:37:10261 const mainTarget = SDK.SDKModel.TargetManager.instance().mainTarget();
Tim van der Lippe1d6e57a2019-09-30 11:55:34262 if (!mainTarget) {
Alexei Filippovfc6ad272019-06-10 18:51:55263 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34264 }
Tim van der Lippee54f66e2020-02-03 18:20:32265 const resourceTreeModel = /** @type {?SDK.ResourceTreeModel.ResourceTreeModel} */ (
266 mainTarget.model(SDK.ResourceTreeModel.ResourceTreeModel));
Tim van der Lippe1d6e57a2019-09-30 11:55:34267 if (resourceTreeModel) {
Alexei Filippovfc6ad272019-06-10 18:51:55268 resourceTreeModel.reloadPage();
Tim van der Lippe1d6e57a2019-09-30 11:55:34269 }
Alexei Filippovfc6ad272019-06-10 18:51:55270 }
271
272 async _stopRecording() {
273 this._setting.set(false);
274 }
Tim van der Lippe6288cf32020-01-07 16:58:40275}
Alexei Filippov933306d2019-03-26 17:40:51276
Tim van der Lippee54f66e2020-02-03 18:20:32277export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode {
Alexei Filippov933306d2019-03-26 17:40:51278 /**
279 * @param {string} url
280 * @param {number} size
Alexei Filippov44a88362019-05-09 01:41:05281 * @param {number} isolateCount
Alexei Filippov933306d2019-03-26 17:40:51282 */
Alexei Filippov44a88362019-05-09 01:41:05283 constructor(url, size, isolateCount) {
Alexei Filippov933306d2019-03-26 17:40:51284 super();
285 this._url = url;
286 this._size = size;
Alexei Filippov44a88362019-05-09 01:41:05287 this._isolateCount = isolateCount;
Alexei Filippov933306d2019-03-26 17:40:51288 }
289
290 /**
291 * @param {number} size
Alexei Filippov44a88362019-05-09 01:41:05292 * @param {number} isolateCount
Alexei Filippov933306d2019-03-26 17:40:51293 */
Alexei Filippov44a88362019-05-09 01:41:05294 updateNode(size, isolateCount) {
Tim van der Lippe1d6e57a2019-09-30 11:55:34295 if (this._size === size && this._isolateCount === isolateCount) {
Alexei Filippov933306d2019-03-26 17:40:51296 return;
Tim van der Lippe1d6e57a2019-09-30 11:55:34297 }
Alexei Filippov933306d2019-03-26 17:40:51298 this._size = size;
Alexei Filippov44a88362019-05-09 01:41:05299 this._isolateCount = isolateCount;
Alexei Filippov933306d2019-03-26 17:40:51300 this.refresh();
301 }
302
303 /**
304 * @override
305 * @param {string} columnId
Sigurd Schneider11657912020-06-30 13:26:52306 * @return {!HTMLElement}
Alexei Filippov933306d2019-03-26 17:40:51307 */
308 createCell(columnId) {
309 const cell = this.createTD(columnId);
310 switch (columnId) {
311 case 'url':
Alexei Filippov933306d2019-03-26 17:40:51312 cell.textContent = this._url;
313 break;
314 case 'size':
315 cell.textContent = Number.withThousandsSeparator(Math.round(this._size / 1e3));
Wolfgang Beyerd451ecd2020-10-23 08:35:54316 cell.createChild('span', 'size-units').textContent = ls`kB`;
Alexei Filippov933306d2019-03-26 17:40:51317 break;
Alexei Filippov44a88362019-05-09 01:41:05318 case 'isolates':
Sigurd Schneider11657912020-06-30 13:26:52319 cell.textContent = `${this._isolateCount}`;
Alexei Filippov44a88362019-05-09 01:41:05320 break;
Alexei Filippov933306d2019-03-26 17:40:51321 }
322 return cell;
323 }
Tim van der Lippe6288cf32020-01-07 16:58:40324}
Alexei Filippovfc6ad272019-06-10 18:51:55325
326/**
Tim van der Lippee54f66e2020-02-03 18:20:32327 * @implements {UI.ActionDelegate.ActionDelegate}
Alexei Filippovfc6ad272019-06-10 18:51:55328 */
Tim van der Lippe6288cf32020-01-07 16:58:40329export class ActionDelegate {
Alexei Filippovfc6ad272019-06-10 18:51:55330 /**
331 * @override
Tim van der Lippee54f66e2020-02-03 18:20:32332 * @param {!UI.Context.Context} context
Alexei Filippovfc6ad272019-06-10 18:51:55333 * @param {string} actionId
334 * @return {boolean}
335 */
336 handleAction(context, actionId) {
337 (async () => {
338 const profileViewId = 'live_heap_profile';
Paul Lewis75c7d0d2020-03-19 12:17:26339 await UI.ViewManager.ViewManager.instance().showView(profileViewId);
340 const widget = await UI.ViewManager.ViewManager.instance().view(profileViewId).widget();
Tim van der Lippe6288cf32020-01-07 16:58:40341 this._innerHandleAction(/** @type {!LiveHeapProfileView} */ (widget), actionId);
Alexei Filippovfc6ad272019-06-10 18:51:55342 })();
343 return true;
344 }
345
346 /**
Tim van der Lippe6288cf32020-01-07 16:58:40347 * @param {!LiveHeapProfileView} profilerView
Alexei Filippovfc6ad272019-06-10 18:51:55348 * @param {string} actionId
349 */
350 _innerHandleAction(profilerView, actionId) {
351 switch (actionId) {
352 case 'live-heap-profile.toggle-recording':
353 profilerView._toggleRecording();
354 break;
355 case 'live-heap-profile.start-with-reload':
356 profilerView._startRecording(true);
357 break;
358 default:
359 console.assert(false, `Unknown action: ${actionId}`);
360 }
361 }
Tim van der Lippe6288cf32020-01-07 16:58:40362}