Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(53)

Side by Side Diff: runtime/observatory/lib/src/elements/heap_profile.dart

Issue 2255613002: Converted Observatory heap-profile element (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Better sorting tests Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 library heap_profile_element;
6
7 import 'dart:async';
8 import 'dart:html';
9 import 'class_ref_wrapper.dart';
10 import 'observatory_element.dart';
11 import 'package:charted/charted.dart';
12 import 'package:observatory/app.dart';
13 import 'package:observatory/service.dart';
14 import 'package:observatory/elements.dart';
15 import 'package:polymer/polymer.dart';
16
17 class ClassSortedTable extends SortedTable {
18
19 ClassSortedTable(columns) : super(columns);
20
21 @override
22 dynamic getSortKeyFor(int row, int col) {
23 if (col == 0) {
24 // Use class name as sort key.
25 return rows[row].values[col].name;
26 }
27 return super.getSortKeyFor(row, col);
28 }
29 }
30
31 @CustomTag('heap-profile')
32 class HeapProfileElement extends ObservatoryElement {
33 @observable String lastServiceGC = '---';
34 @observable String lastAccumulatorReset = '---';
35
36 // Pie chart of new space usage.
37 var _newPieChart;
38 final _newPieChartRows = [];
39 // Pie chart of old space usage.
40 var _oldPieChart;
41 final _oldPieChartRows = [];
42
43 @observable ClassSortedTable classTable;
44 var _classTableBody;
45
46 @published bool autoRefresh = false;
47 var _subscriptionFuture;
48
49 @published Isolate isolate;
50 @observable ServiceMap profile;
51
52 final _pieChartColumns = [
53 new ChartColumnSpec(label: 'Type', type: ChartColumnSpec.TYPE_STRING),
54 new ChartColumnSpec(label: 'Size', formatter: (v) => v.toString())
55 ];
56
57 HeapProfileElement.created() : super.created() {
58 _initPieChartData(_newPieChartRows);
59 _initPieChartData(_oldPieChartRows);
60
61 // Create class table model.
62 var columns = [
63 new SortedTableColumn('Class'),
64 new SortedTableColumn(''), // Spacer column.
65 new SortedTableColumn.withFormatter('Accumulated Size (New)',
66 Utils.formatSize),
67 new SortedTableColumn.withFormatter('Accumulated Instances',
68 Utils.formatCommaSeparated),
69 new SortedTableColumn.withFormatter('Current Size',
70 Utils.formatSize),
71 new SortedTableColumn.withFormatter('Current Instances',
72 Utils.formatCommaSeparated),
73 new SortedTableColumn(''), // Spacer column.
74 new SortedTableColumn.withFormatter('Accumulator Size (Old)',
75 Utils.formatSize),
76 new SortedTableColumn.withFormatter('Accumulator Instances',
77 Utils.formatCommaSeparated),
78 new SortedTableColumn.withFormatter('Current Size',
79 Utils.formatSize),
80 new SortedTableColumn.withFormatter('Current Instances',
81 Utils.formatCommaSeparated)
82 ];
83 classTable = new ClassSortedTable(columns);
84 // By default, start with accumulated new space bytes.
85 classTable.sortColumnIndex = 2;
86 }
87
88 LayoutArea _makePieChart(String id, List rows) {
89 var wrapper = shadowRoot.querySelector(id);
90 var areaHost = wrapper.querySelector('.chart-host');
91 assert(areaHost != null);
92 var legendHost = wrapper.querySelector('.chart-legend-host');
93 assert(legendHost != null);
94 var series = new ChartSeries(id, [1], new PieChartRenderer(
95 sortDataByValue: false
96 ));
97 var config = new ChartConfig([series], [0]);
98 config.minimumSize = new Rect(300, 300);
99 config.legend = new ChartLegend(legendHost, showValues: true);
100 var data = new ChartData(_pieChartColumns, rows);
101 var area = new LayoutArea(areaHost,
102 data,
103 config,
104 state: new ChartState(),
105 autoUpdate: false);
106 area.addChartBehavior(new Hovercard());
107 area.addChartBehavior(new AxisLabelTooltip());
108 return area;
109 }
110
111 @override
112 void attached() {
113 super.attached();
114 _newPieChart = _makePieChart('#new-pie-chart', _newPieChartRows);
115 _oldPieChart = _makePieChart('#old-pie-chart', _oldPieChartRows);
116 _classTableBody = shadowRoot.querySelector('#classTableBody');
117 _subscriptionFuture =
118 app.vm.listenEventStream(VM.kGCStream, _onEvent);
119 }
120
121 @override
122 void detached() {
123 cancelFutureSubscription(_subscriptionFuture);
124 _subscriptionFuture = null;
125 super.detached();
126 }
127
128 // Keep at most one outstanding auto-refresh RPC.
129 bool refreshAutoPending = false;
130 bool refreshAutoQueued = false;
131
132 void _onEvent(ServiceEvent event) {
133 assert(event.kind == 'GC');
134 if (autoRefresh) {
135 if (!refreshAutoPending) {
136 refreshAuto();
137 } else {
138 // Remember to refresh once more, to ensure latest profile.
139 refreshAutoQueued = true;
140 }
141 }
142 }
143
144 void refreshAuto() {
145 refreshAutoPending = true;
146 refreshAutoQueued = false;
147 refresh().then((_) {
148 refreshAutoPending = false;
149 // Keep refreshing if at least one GC event was received while waiting.
150 if (refreshAutoQueued) {
151 refreshAuto();
152 }
153 }).catchError(app.handleException);
154 }
155
156 static const _USED_INDEX = 0;
157 static const _FREE_INDEX = 1;
158 static const _EXTERNAL_INDEX = 2;
159
160 static const _VALUE_INDEX = 1;
161
162 void _initPieChartData(List rows) {
163 rows.add(['Used', 0]);
164 rows.add(['Free', 0]);
165 rows.add(['External', 0]);
166 }
167
168 void _updatePieChartData(List rows, HeapSpace space) {
169 rows[_USED_INDEX][_VALUE_INDEX] = space.used;
170 rows[_FREE_INDEX][_VALUE_INDEX] = space.capacity - space.used;
171 rows[_EXTERNAL_INDEX][_VALUE_INDEX] = space.external;
172 }
173
174 void _updatePieCharts() {
175 assert(profile != null);
176 _updatePieChartData(_newPieChartRows, isolate.newSpace);
177 _updatePieChartData(_oldPieChartRows, isolate.oldSpace);
178 }
179
180 void _updateClasses() {
181 for (ServiceMap clsAllocations in profile['members']) {
182 Class cls = clsAllocations['class'];
183 if (cls == null) {
184 continue;
185 }
186 cls.newSpace.update(clsAllocations['new']);
187 cls.oldSpace.update(clsAllocations['old']);
188 }
189 }
190
191 void _updateClassTable() {
192 classTable.clearRows();
193 for (ServiceMap clsAllocations in profile['members']) {
194 Class cls = clsAllocations['class'];
195 if (cls == null) {
196 continue;
197 }
198 if (cls.hasNoAllocations) {
199 // If a class has no allocations, don't display it.
200 continue;
201 }
202 var row = [cls,
203 '', // Spacer column.
204 cls.newSpace.accumulated.bytes,
205 cls.newSpace.accumulated.instances,
206 cls.newSpace.current.bytes,
207 cls.newSpace.current.instances,
208 '', // Spacer column.
209 cls.oldSpace.accumulated.bytes,
210 cls.oldSpace.accumulated.instances,
211 cls.oldSpace.current.bytes,
212 cls.oldSpace.current.instances];
213 classTable.addRow(new SortedTableRow(row));
214 }
215 classTable.sort();
216 }
217
218 void _addClassTableDomRow() {
219 assert(_classTableBody != null);
220 var tr = new TableRowElement();
221
222 // Add class ref.
223 var cell = tr.insertCell(-1);
224 ClassRefElement classRef = new Element.tag('class-ref');
225 cell.children.add(classRef);
226
227 // Add spacer.
228 cell = tr.insertCell(-1);
229 cell.classes.add('left-border-spacer');
230
231 // Add new space.
232 cell = tr.insertCell(-1);
233 cell = tr.insertCell(-1);
234 cell = tr.insertCell(-1);
235 cell = tr.insertCell(-1);
236
237 // Add spacer.
238 cell = tr.insertCell(-1);
239 cell.classes.add('left-border-spacer');
240
241 // Add old space.
242 cell = tr.insertCell(-1);
243 cell = tr.insertCell(-1);
244 cell = tr.insertCell(-1);
245 cell = tr.insertCell(-1);
246
247 // Add row to table.
248 _classTableBody.children.add(tr);
249 }
250
251 void _fillClassTableDomRow(TableRowElement tr, int rowIndex) {
252 const SPACER_COLUMNS = const [1, 6];
253
254 var row = classTable.rows[rowIndex];
255 // Add class ref.
256 ClassRefElementWrapper classRef = tr.children[0].children[0];
257 classRef.ref = row.values[0];
258
259 for (var i = 1; i < row.values.length; i++) {
260 if (SPACER_COLUMNS.contains(i)) {
261 // Skip spacer columns.
262 continue;
263 }
264 var cell = tr.children[i];
265 cell.title = row.values[i].toString();
266 cell.text = classTable.getFormattedValue(rowIndex, i);
267 if (i > 1) { // Numbers.
268 cell.style.textAlign = 'right';
269 cell.style.paddingLeft = '1em';
270 }
271 }
272 }
273
274 void _updateClassTableInDom() {
275 assert(_classTableBody != null);
276 // Resize DOM table.
277 if (_classTableBody.children.length > classTable.sortedRows.length) {
278 // Shrink the table.
279 var deadRows =
280 _classTableBody.children.length - classTable.sortedRows.length;
281 for (var i = 0; i < deadRows; i++) {
282 _classTableBody.children.removeLast();
283 }
284 } else if (_classTableBody.children.length < classTable.sortedRows.length) {
285 // Grow table.
286 var newRows =
287 classTable.sortedRows.length - _classTableBody.children.length;
288 for (var i = 0; i < newRows; i++) {
289 _addClassTableDomRow();
290 }
291 }
292 assert(_classTableBody.children.length == classTable.sortedRows.length);
293 // Fill table.
294 for (var i = 0; i < classTable.sortedRows.length; i++) {
295 var rowIndex = classTable.sortedRows[i];
296 var tr = _classTableBody.children[i];
297 _fillClassTableDomRow(tr, rowIndex);
298 }
299 }
300
301 void _drawCharts() {
302 _newPieChart.draw();
303 _oldPieChart.draw();
304 }
305
306 @observable void changeSort(Event e, var detail, Element target) {
307 if (target is TableCellElement) {
308 if (classTable.sortColumnIndex != target.cellIndex) {
309 classTable.sortColumnIndex = target.cellIndex;
310 classTable.sortDescending = true;
311 } else {
312 classTable.sortDescending = !classTable.sortDescending;
313 }
314 classTable.sort();
315 _updateClassTableInDom();
316 }
317 }
318
319 void isolateChanged(oldValue) {
320 if (isolate == null) {
321 profile = null;
322 return;
323 }
324 isolate.invokeRpc('_getAllocationProfile', {})
325 .then(_update)
326 .catchError(app.handleException);
327 }
328
329 Future refresh() {
330 if (isolate == null) {
331 return new Future.value(null);
332 }
333 return isolate.invokeRpc('_getAllocationProfile', {})
334 .then(_update);
335 }
336
337 Future refreshGC() {
338 if (isolate == null) {
339 return new Future.value(null);
340 }
341 return isolate.invokeRpc('_getAllocationProfile', { 'gc': 'full' })
342 .then(_update);
343 }
344
345 Future resetAccumulator() {
346 if (isolate == null) {
347 return new Future.value(null);
348 }
349 return isolate.invokeRpc('_getAllocationProfile', { 'reset': 'true' })
350 .then(_update);
351 }
352
353 void _update(ServiceMap newProfile) {
354 profile = newProfile;
355 }
356
357 void profileChanged(oldValue) {
358 if (profile == null) {
359 return;
360 }
361 isolate.updateHeapsFromMap(profile['heaps']);
362 var millis = int.parse(profile['dateLastAccumulatorReset']);
363 if (millis != 0) {
364 lastAccumulatorReset =
365 new DateTime.fromMillisecondsSinceEpoch(millis).toString();
366 }
367 millis = int.parse(profile['dateLastServiceGC']);
368 if (millis != 0) {
369 lastServiceGC =
370 new DateTime.fromMillisecondsSinceEpoch(millis).toString();
371 }
372 _updatePieCharts();
373 _updateClasses();
374 _updateClassTable();
375 _updateClassTableInDom();
376 _drawCharts();
377 notifyPropertyChange(#formattedAverage, 0, 1);
378 notifyPropertyChange(#formattedTotalCollectionTime, 0, 1);
379 notifyPropertyChange(#formattedCollections, 0, 1);
380 }
381
382 @observable String formattedAverage(bool newSpace) {
383 if (profile == null) {
384 return '';
385 }
386 var heap = newSpace ? isolate.newSpace : isolate.oldSpace;
387 var avg = ((heap.totalCollectionTimeInSeconds * 1000.0) / heap.collections);
388 return '${avg.toStringAsFixed(2)} ms';
389 }
390
391 @observable String formattedCollections(bool newSpace) {
392 if (profile == null) {
393 return '';
394 }
395 var heap = newSpace ? isolate.newSpace : isolate.oldSpace;
396 return heap.collections.toString();
397 }
398
399 @observable String formattedTotalCollectionTime(bool newSpace) {
400 if (profile == null) {
401 return '';
402 }
403 var heap = newSpace ? isolate.newSpace : isolate.oldSpace;
404 return '${Utils.formatSeconds(heap.totalCollectionTimeInSeconds)} secs';
405 }
406 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/elements/css/shared.css ('k') | runtime/observatory/lib/src/elements/heap_profile.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698