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

Side by Side Diff: runtime/bin/vmservice/client/lib/src/elements/heap_profile.dart

Issue 342513004: Visual refresh of allocation profile page (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 6 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library heap_profile_element; 5 library heap_profile_element;
6 6
7 import 'dart:html'; 7 import 'dart:html';
8 import 'observatory_element.dart'; 8 import 'observatory_element.dart';
9 import 'package:observatory/app.dart'; 9 import 'package:observatory/app.dart';
10 import 'package:observatory/service.dart'; 10 import 'package:observatory/service.dart';
11 import 'package:logging/logging.dart'; 11 import 'package:observatory/elements.dart';
12 import 'package:polymer/polymer.dart'; 12 import 'package:polymer/polymer.dart';
13 13
14 class ClassSortedTable extends SortedTable {
15
16 ClassSortedTable(columns) : super(columns);
17
18 @override
19 dynamic getSortKeyFor(int row, int col) {
20 if (col == 0) {
21 // Use class name as sort key.
22 return rows[row].values[col].name;
23 }
24 return super.getSortKeyFor(row, col);
25 }
26 }
27
14 /// Displays an Error response. 28 /// Displays an Error response.
15 @CustomTag('heap-profile') 29 @CustomTag('heap-profile')
16 class HeapProfileElement extends ObservatoryElement { 30 class HeapProfileElement extends ObservatoryElement {
17 // Indexes into VM provided map. 31 @observable String lastServiceGC = '---';
18 static const ALLOCATED_BEFORE_GC = 0; 32 @observable String lastAccumulatorReset = '---';
19 static const ALLOCATED_BEFORE_GC_SIZE = 1;
20 static const LIVE_AFTER_GC = 2;
21 static const LIVE_AFTER_GC_SIZE = 3;
22 static const ALLOCATED_SINCE_GC = 4;
23 static const ALLOCATED_SINCE_GC_SIZE = 5;
24 static const ACCUMULATED = 6;
25 static const ACCUMULATED_SIZE = 7;
26 33
27 // Pie chart of new space usage. 34 // Pie chart of new space usage.
28 var _newPieDataTable; 35 var _newPieDataTable;
29 var _newPieChart; 36 var _newPieChart;
30 37
31 // Pie chart of old space usage. 38 // Pie chart of old space usage.
32 var _oldPieDataTable; 39 var _oldPieDataTable;
33 var _oldPieChart; 40 var _oldPieChart;
34 41
35 @observable SortedTable classTable; 42 @observable ClassSortedTable classTable;
43 var _classTableBody;
36 44
37 @published ServiceMap profile; 45 @published ServiceMap profile;
38 46
47 @observable Isolate isolate;
48
39 HeapProfileElement.created() : super.created() { 49 HeapProfileElement.created() : super.created() {
50 // Create pie chart models.
40 _newPieDataTable = new DataTable(); 51 _newPieDataTable = new DataTable();
41 _newPieDataTable.addColumn('string', 'Type'); 52 _newPieDataTable.addColumn('string', 'Type');
42 _newPieDataTable.addColumn('number', 'Size'); 53 _newPieDataTable.addColumn('number', 'Size');
43 _oldPieDataTable = new DataTable(); 54 _oldPieDataTable = new DataTable();
44 _oldPieDataTable.addColumn('string', 'Type'); 55 _oldPieDataTable.addColumn('string', 'Type');
45 _oldPieDataTable.addColumn('number', 'Size'); 56 _oldPieDataTable.addColumn('number', 'Size');
57
58 // Create class table model.
46 var columns = [ 59 var columns = [
47 new SortedTableColumn('Class'), 60 new SortedTableColumn('Class'),
48 new SortedTableColumn.withFormatter('Accumulator Size (New)', 61 new SortedTableColumn(''), // Spacer column.
49 Utils.formatSize), 62 new SortedTableColumn.withFormatter('Accumulated Size (New)',
50 new SortedTableColumn.withFormatter('Accumulator (New)', 63 Utils.formatSize),
64 new SortedTableColumn.withFormatter('Accumulated Instances',
51 Utils.formatCommaSeparated), 65 Utils.formatCommaSeparated),
52 new SortedTableColumn.withFormatter('Current Size (New)', 66 new SortedTableColumn.withFormatter('Current Size',
53 Utils.formatSize), 67 Utils.formatSize),
54 new SortedTableColumn.withFormatter('Current (New)', 68 new SortedTableColumn.withFormatter('Current Instances',
55 Utils.formatCommaSeparated), 69 Utils.formatCommaSeparated),
70 new SortedTableColumn(''), // Spacer column.
56 new SortedTableColumn.withFormatter('Accumulator Size (Old)', 71 new SortedTableColumn.withFormatter('Accumulator Size (Old)',
57 Utils.formatSize), 72 Utils.formatSize),
58 new SortedTableColumn.withFormatter('Accumulator (Old)', 73 new SortedTableColumn.withFormatter('Accumulator Instances',
59 Utils.formatCommaSeparated), 74 Utils.formatCommaSeparated),
60 new SortedTableColumn.withFormatter('Current Size (Old)', 75 new SortedTableColumn.withFormatter('Current Size',
61 Utils.formatSize), 76 Utils.formatSize),
62 new SortedTableColumn.withFormatter('Current (Old)', 77 new SortedTableColumn.withFormatter('Current Instances',
63 Utils.formatCommaSeparated) 78 Utils.formatCommaSeparated)
64 ]; 79 ];
65 classTable = new SortedTable(columns); 80 classTable = new ClassSortedTable(columns);
66 classTable.sortColumnIndex = 1; 81 // By default, start with accumulated new space bytes.
67 } 82 classTable.sortColumnIndex = 2;
68 83 }
69 void enteredView() { 84
70 super.enteredView(); 85 @override
86 void attached() {
87 super.attached();
88 // Grab the pie chart divs.
71 _newPieChart = new Chart('PieChart', 89 _newPieChart = new Chart('PieChart',
72 shadowRoot.querySelector('#newPieChart')); 90 shadowRoot.querySelector('#newPieChart'));
73 _newPieChart.options['title'] = 'New Space';
74 _oldPieChart = new Chart('PieChart', 91 _oldPieChart = new Chart('PieChart',
75 shadowRoot.querySelector('#oldPieChart')); 92 shadowRoot.querySelector('#oldPieChart'));
76 _oldPieChart.options['title'] = 'Old Space'; 93 _classTableBody = shadowRoot.querySelector('#classTableBody');
77 _draw(); 94 }
78 } 95
79 96 void _updatePieCharts() {
80 void _updateChartData() { 97 assert(profile != null);
81 if ((profile == null) || (profile['members'] is! List) || 98 _newPieDataTable.clearRows();
82 (profile['members'].length == 0)) { 99 var isolate = profile.isolate;
83 return; 100 _newPieDataTable.addRow(['Used', isolate.newSpace.used]);
84 } 101 _newPieDataTable.addRow(['Free',
85 assert(classTable != null); 102 isolate.newSpace.capacity - isolate.newSpace.used]);
103 _newPieDataTable.addRow(['External', isolate.newSpace.external]);
104 _oldPieDataTable.clearRows();
105 _oldPieDataTable.addRow(['Used', isolate.oldSpace.used]);
106 _oldPieDataTable.addRow(['Free',
107 isolate.oldSpace.capacity - isolate.oldSpace.used]);
108 _oldPieDataTable.addRow(['External', isolate.oldSpace.external]);
109 }
110
111 void _updateClasses() {
112 for (ServiceMap clsAllocations in profile['members']) {
113 Class cls = clsAllocations['class'];
114 if (cls == null) {
115 continue;
116 }
117 cls.newSpace.update(clsAllocations['new']);
118 cls.oldSpace.update(clsAllocations['old']);
119 }
120 }
121
122 void _updateClassTable() {
86 classTable.clearRows(); 123 classTable.clearRows();
87 for (ServiceMap cls in profile['members']) { 124 for (ServiceMap clsAllocations in profile['members']) {
88 if (_classHasNoAllocations(cls)) { 125 Class cls = clsAllocations['class'];
126 if (cls == null) {
127 continue;
128 }
129 if (cls.hasNoAllocations) {
89 // If a class has no allocations, don't display it. 130 // If a class has no allocations, don't display it.
90 continue; 131 continue;
91 } 132 }
92 var row = [cls['class'], 133 var row = [cls,
93 _combinedTableColumnValue(cls, 1), 134 '', // Spacer column.
94 _combinedTableColumnValue(cls, 2), 135 cls.newSpace.accumulated.bytes,
95 _combinedTableColumnValue(cls, 3), 136 cls.newSpace.accumulated.instances,
96 _combinedTableColumnValue(cls, 4), 137 cls.newSpace.current.bytes,
97 _combinedTableColumnValue(cls, 5), 138 cls.newSpace.current.instances,
98 _combinedTableColumnValue(cls, 6), 139 '', // Spacer column.
99 _combinedTableColumnValue(cls, 7), 140 cls.oldSpace.accumulated.bytes,
100 _combinedTableColumnValue(cls, 8)]; 141 cls.oldSpace.accumulated.instances,
142 cls.oldSpace.current.bytes,
143 cls.oldSpace.current.instances];
101 classTable.addRow(new SortedTableRow(row)); 144 classTable.addRow(new SortedTableRow(row));
102 } 145 }
103 classTable.sort(); 146 classTable.sort();
104 _newPieDataTable.clearRows(); 147 }
105 var heap = profile['heaps']['new']; 148
106 _newPieDataTable.addRow(['Used', heap['used']]); 149 void _addClassTableDomRow() {
107 _newPieDataTable.addRow(['Free', heap['capacity'] - heap['used']]); 150 assert(_classTableBody != null);
108 _newPieDataTable.addRow(['External', heap['external']]); 151 var tr = new TableRowElement();
109 _oldPieDataTable.clearRows(); 152
110 heap = profile['heaps']['old']; 153 // Add class ref.
111 _oldPieDataTable.addRow(['Used', heap['used']]); 154 var cell = tr.insertCell(-1);
112 _oldPieDataTable.addRow(['Free', heap['capacity'] - heap['used']]); 155 ClassRefElement classRef = new Element.tag('class-ref');
113 _oldPieDataTable.addRow(['External', heap['external']]); 156 cell.children.add(classRef);
114 _draw(); 157
115 } 158 // Add spacer.
116 159 cell = tr.insertCell(-1);
117 void _draw() { 160 cell.classes.add('left-border-spacer');
118 if (_newPieChart == null) { 161
119 return; 162 // Add new space.
120 } 163 cell = tr.insertCell(-1);
164 cell = tr.insertCell(-1);
165 cell = tr.insertCell(-1);
166 cell = tr.insertCell(-1);
167
168 // Add spacer.
169 cell = tr.insertCell(-1);
170 cell.classes.add('left-border-spacer');
171
172 // Add old space.
173 cell = tr.insertCell(-1);
174 cell = tr.insertCell(-1);
175 cell = tr.insertCell(-1);
176 cell = tr.insertCell(-1);
177
178 // Add row to table.
179 _classTableBody.children.add(tr);
180 }
181
182 void _fillClassTableDomRow(TableRowElement tr, int rowIndex) {
183 const SPACER_COLUMNS = const [1, 6];
184
185 var row = classTable.rows[rowIndex];
186 // Add class ref.
187 ClassRefElement classRef = tr.children[0].children[0];
188 classRef.ref = row.values[0];
189
190 for (var i = 1; i < row.values.length; i++) {
191 if (SPACER_COLUMNS.contains(i)) {
192 // Skip spacer columns.
193 continue;
194 }
195 var cell = tr.children[i];
196 cell.title = row.values[i].toString();
197 cell.text = classTable.getFormattedValue(rowIndex, i);
198 }
199 }
200
201 void _updateClassTableInDom() {
202 assert(_classTableBody != null);
203 // Resize DOM table.
204 if (_classTableBody.children.length > classTable.sortedRows.length) {
205 // Shrink the table.
206 var deadRows =
207 _classTableBody.children.length - classTable.sortedRows.length;
208 for (var i = 0; i < deadRows; i++) {
209 _classTableBody.children.removeLast();
210 }
211 } else if (_classTableBody.children.length < classTable.sortedRows.length) {
212 // Grow table.
213 var newRows =
214 classTable.sortedRows.length - _classTableBody.children.length;
215 for (var i = 0; i < newRows; i++) {
216 _addClassTableDomRow();
217 }
218 }
219 assert(_classTableBody.children.length == classTable.sortedRows.length);
220 // Fill table.
221 for (var i = 0; i < classTable.sortedRows.length; i++) {
222 var rowIndex = classTable.sortedRows[i];
223 var tr = _classTableBody.children[i];
224 _fillClassTableDomRow(tr, rowIndex);
225 }
226 }
227
228 void _drawCharts() {
121 _newPieChart.draw(_newPieDataTable); 229 _newPieChart.draw(_newPieDataTable);
122 _oldPieChart.draw(_oldPieDataTable); 230 _oldPieChart.draw(_oldPieDataTable);
123 } 231 }
124 232
125 @observable void changeSort(Event e, var detail, Element target) { 233 @observable void changeSort(Event e, var detail, Element target) {
126 if (target is TableCellElement) { 234 if (target is TableCellElement) {
127 if (classTable.sortColumnIndex != target.cellIndex) { 235 if (classTable.sortColumnIndex != target.cellIndex) {
128 classTable.sortColumnIndex = target.cellIndex; 236 classTable.sortColumnIndex = target.cellIndex;
129 classTable.sort(); 237 classTable.sortDescending = true;
130 } 238 } else {
131 } 239 classTable.sortDescending = !classTable.sortDescending;
132 } 240 }
133 241 classTable.sort();
134 bool _classHasNoAllocations(Map v) { 242 _updateClassTableInDom();
135 var newSpace = v['new']; 243 }
136 var oldSpace = v['old'];
137 for (var allocation in newSpace) {
138 if (allocation != 0) {
139 return false;
140 }
141 }
142 for (var allocation in oldSpace) {
143 if (allocation != 0) {
144 return false;
145 }
146 }
147 return true;
148 }
149
150 dynamic _combinedTableColumnValue(Map v, int index) {
151 assert(index >= 0);
152 assert(index < 9);
153 switch (index) {
154 case 0:
155 return v['class']['user_name'];
156 case 1:
157 return v['new'][ACCUMULATED_SIZE];
158 case 2:
159 return v['new'][ACCUMULATED];
160 case 3:
161 return v['new'][LIVE_AFTER_GC_SIZE] +
162 v['new'][ALLOCATED_SINCE_GC_SIZE];
163 case 4:
164 return v['new'][LIVE_AFTER_GC] +
165 v['new'][ALLOCATED_SINCE_GC];
166 case 5:
167 return v['old'][ACCUMULATED_SIZE];
168 case 6:
169 return v['old'][ACCUMULATED];
170 case 7:
171 return v['old'][LIVE_AFTER_GC_SIZE] +
172 v['old'][ALLOCATED_SINCE_GC_SIZE];
173 case 8:
174 return v['old'][LIVE_AFTER_GC] +
175 v['old'][ALLOCATED_SINCE_GC];
176 }
177 throw new FallThroughError();
178 } 244 }
179 245
180 void refresh(var done) { 246 void refresh(var done) {
181 if (profile == null) { 247 if (profile == null) {
182 return; 248 return;
183 } 249 }
184 var isolate = profile.isolate; 250 var isolate = profile.isolate;
185 isolate.get('/allocationprofile').then((ServiceMap response) { 251 isolate.get('/allocationprofile').then(_update).whenComplete(done);
186 assert(response['type'] == 'AllocationProfile');
187 profile = response;
188 }).catchError((e, st) {
189 Logger.root.info('$e $st');
190 }).whenComplete(done);
191 } 252 }
192 253
193 void refreshGC(var done) { 254 void refreshGC(var done) {
194 if (profile == null) { 255 if (profile == null) {
195 return; 256 return;
196 }
197 var isolate = profile.isolate;
198 isolate.get('/allocationprofile?gc=full').then((ServiceMap response) {
199 assert(response['type'] == 'AllocationProfile');
200 profile = response;
201 }).catchError((e, st) {
202 Logger.root.info('$e $st');
203 }).whenComplete(done);
204 } 257 }
258 var isolate = profile.isolate;
259 isolate.get('/allocationprofile?gc=full').then(_update).whenComplete(done);
260 }
205 261
206 void resetAccumulator(var done) { 262 void resetAccumulator(var done) {
207 if (profile == null) { 263 if (profile == null) {
208 return; 264 return;
209 } 265 }
210 var isolate = profile.isolate; 266 var isolate = profile.isolate;
211 isolate.get('/allocationprofile?reset=true').then((ServiceMap response) { 267 isolate.get('/allocationprofile?reset=true').then(_update).
212 assert(response['type'] == 'AllocationProfile'); 268 whenComplete(done);
213 profile = response; 269 }
214 }).catchError((e, st) { 270
215 Logger.root.info('$e $st'); 271 void _update(ServiceMap newProfile) {
216 }).whenComplete(done); 272 profile = newProfile;
217 } 273 }
218 274
219 void profileChanged(oldValue) { 275 void profileChanged(oldValue) {
220 try { 276 if (profile == null) {
221 _updateChartData(); 277 return;
222 } catch (e, st) {
223 Logger.root.info('$e $st');
224 } 278 }
225 notifyPropertyChange(#formattedAverage, [], formattedAverage); 279 isolate = profile.isolate;
226 notifyPropertyChange(#formattedTotalCollectionTime, [], 280 isolate.updateHeapsFromMap(profile['heaps']);
227 formattedTotalCollectionTime); 281 var millis = int.parse(profile['dateLastAccumulatorReset']);
228 notifyPropertyChange(#formattedCollections, [], formattedCollections); 282 if (millis != 0) {
283 lastAccumulatorReset =
284 new DateTime.fromMillisecondsSinceEpoch(millis).toString();
285 }
286 millis = int.parse(profile['dateLastServiceGC']);
287 if (millis != 0) {
288 lastServiceGC =
289 new DateTime.fromMillisecondsSinceEpoch(millis).toString();
290 }
291 _updatePieCharts();
292 _updateClasses();
293 _updateClassTable();
294 _updateClassTableInDom();
295 _drawCharts();
296 notifyPropertyChange(#formattedAverage, 0, 1);
297 notifyPropertyChange(#formattedTotalCollectionTime, 0, 1);
298 notifyPropertyChange(#formattedCollections, 0, 1);
229 } 299 }
230 300
231 @observable String formattedAverage(bool newSpace) { 301 @observable String formattedAverage(bool newSpace) {
232 if (profile == null) { 302 if (profile == null) {
233 return ''; 303 return '';
234 } 304 }
235 String space = newSpace ? 'new' : 'old'; 305 var heap = newSpace ? profile.isolate.newSpace : profile.isolate.oldSpace;
236 Map heap = profile['heaps'][space]; 306 var avg = ((heap.totalCollectionTimeInSeconds * 1000.0) / heap.collections);
237 var r = ((heap['time'] * 1000.0) / heap['collections']).toStringAsFixed(2); 307 return '${avg.toStringAsFixed(2)} ms';
238 return '$r ms';
239 } 308 }
240 309
241 @observable String formattedCollections(bool newSpace) { 310 @observable String formattedCollections(bool newSpace) {
242 if (profile == null) { 311 if (profile == null) {
243 return ''; 312 return '';
244 } 313 }
245 String space = newSpace ? 'new' : 'old'; 314 var heap = newSpace ? profile.isolate.newSpace : profile.isolate.oldSpace;
246 Map heap = profile['heaps'][space]; 315 return heap.collections.toString();
247 return '${heap['collections']}';
248 } 316 }
249 317
250 @observable String formattedTotalCollectionTime(bool newSpace) { 318 @observable String formattedTotalCollectionTime(bool newSpace) {
251 if (profile == null) { 319 if (profile == null) {
252 return ''; 320 return '';
253 } 321 }
254 String space = newSpace ? 'new' : 'old'; 322 var heap = newSpace ? profile.isolate.newSpace : profile.isolate.oldSpace;
255 Map heap = profile['heaps'][space]; 323 return '${Utils.formatSeconds(heap.totalCollectionTimeInSeconds)} secs';
256 return '${Utils.formatSeconds(heap['time'])} secs';
257 } 324 }
258 } 325 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698