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

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

Issue 2255613002: Converted Observatory heap-profile element (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Optimizations & Dead code removal 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 import 'dart:async';
6 import 'dart:html';
7 import 'package:charted/charted.dart';
8 import "package:charted/charts/charts.dart";
9 import 'package:observatory/models.dart' as M;
10 import 'package:observatory/src/elements/class_ref.dart';
11 import 'package:observatory/src/elements/containers/virtual_collection.dart';
12 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
13 import 'package:observatory/src/elements/helpers/tag.dart';
14 import 'package:observatory/src/elements/helpers/uris.dart';
15 import 'package:observatory/src/elements/nav/bar.dart';
16 import 'package:observatory/src/elements/nav/isolate_menu.dart';
17 import 'package:observatory/src/elements/nav/menu.dart';
18 import 'package:observatory/src/elements/nav/notify.dart';
19 import 'package:observatory/src/elements/nav/refresh.dart';
20 import 'package:observatory/src/elements/nav/top_menu.dart';
21 import 'package:observatory/src/elements/nav/vm_menu.dart';
22 import 'package:observatory/utils.dart';
23
24 enum _SortingField {
25 accumulatedSize,
26 accumulatedInstances,
27 currentSize,
28 currentInstances,
29 newAccumulatedSize,
30 newAccumulatedInstances,
31 newCurrentSize,
32 newCurrentInstances,
33 oldAccumulatedSize,
34 oldAccumulatedInstances,
35 oldCurrentSize,
36 oldCurrentInstances,
37 className,
38 }
39
40 enum _SortingDirection {
41 ascending,
42 descending
43 }
44
45 class AllocationProfileElement extends HtmlElement implements Renderable {
46 static const tag = const Tag<AllocationProfileElement>('allocation-profile',
47 dependencies: const [
48 ClassRefElement.tag,
49 NavBarElement.tag,
50 NavTopMenuElement.tag,
51 NavVMMenuElement.tag,
52 NavIsolateMenuElement.tag,
53 NavMenuElement.tag,
54 NavRefreshElement.tag,
55 NavNotifyElement.tag,
56 VirtualCollectionElement.tag
57 ]);
58
59 RenderingScheduler<AllocationProfileElement> _r;
60
61 Stream<RenderedEvent<AllocationProfileElement>> get onRendered =>
62 _r.onRendered;
63
64 M.VM _vm;
65 M.IsolateRef _isolate;
66 M.EventRepository _events;
67 M.NotificationRepository _notifications;
68 M.AllocationProfileRepository _repository;
69 M.AllocationProfile _profile;
70 bool _autoRefresh = false;
71 StreamSubscription _gcSubscription;
72 _SortingField _sortingField =
73 _SortingField.className;
74 _SortingDirection _sortingDirection =
75 _SortingDirection.ascending;
76
77 M.VMRef get vm => _vm;
78 M.IsolateRef get isolate => _isolate;
79 M.NotificationRepository get notifications => _notifications;
80
81 factory AllocationProfileElement(M.VM vm, M.IsolateRef isolate,
82 M.EventRepository events,
83 M.NotificationRepository notifications,
84 M.AllocationProfileRepository repository,
85 {RenderingQueue queue}) {
86 assert(vm != null);
87 assert(isolate != null);
88 assert(events != null);
89 assert(notifications != null);
90 assert(repository != null);
91 AllocationProfileElement e = document.createElement(tag.name);
92 e._r = new RenderingScheduler(e, queue: queue);
93 e._vm = vm;
94 e._isolate = isolate;
95 e._events = events;
96 e._notifications = notifications;
97 e._repository = repository;
98 return e;
99 }
100
101 AllocationProfileElement.created() : super.created();
102
103 @override
104 attached() {
105 super.attached();
106 _r.enable();
107 _refresh();
108 _gcSubscription = _events.onGCEvent.listen((e) {
109 if (_autoRefresh && e.isolate.id == _isolate.id) {
Cutch 2016/08/17 14:28:44 (e.isolate.id == _isolate.id) but I think you can
cbernaschina 2016/08/17 17:01:27 Done. Checking id is meanly for test-ability, in t
110 _refresh();
111 }
112 });
113 }
114
115 @override
116 detached() {
117 super.detached();
118 _r.disable(notify: true);
119 children = [];
120 _gcSubscription.cancel();
121 }
122
123 void render() {
124 children = [
125 new NavBarElement(queue: _r.queue)
126 ..children = [
127 new NavTopMenuElement(queue: _r.queue),
128 new NavVMMenuElement(_vm, _events, queue: _r.queue),
129 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue),
130 new NavMenuElement('allocation profile', last: true,
131 link: Uris.profiler(_isolate), queue: _r.queue),
132 new NavRefreshElement(label: 'Download', disabled: _profile == null,
133 queue: _r.queue)
134 ..onRefresh.listen((_) => _downloadCSV()),
135 new NavRefreshElement(label: 'Reset Accumulator', queue: _r.queue)
136 ..onRefresh.listen((_) => _refresh(reset: true)),
137 new NavRefreshElement(label: 'GC', queue: _r.queue)
138 ..onRefresh.listen((_) => _refresh(gc: true)),
139 new NavRefreshElement(queue: _r.queue)
140 ..onRefresh.listen((_) => _refresh()),
141 new DivElement()..classes = const ['nav-option']
142 ..children = [
143 new CheckboxInputElement()
144 ..id = 'allocation-profile-auto-refresh'
145 ..checked = _autoRefresh
146 ..onChange.listen((_) => _autoRefresh = !_autoRefresh),
147 new LabelElement()
148 ..htmlFor = 'allocation-profile-auto-refresh'
149 ..text = 'Auto-refresh on GC'
150 ],
151 new NavNotifyElement(_notifications, queue: _r.queue)
152 ],
153 new DivElement()..classes = const ['content-centered-big']
154 ..children = [
155 new HeadingElement.h2()..text = 'Allocation Profile',
156 new HRElement()
157 ]
158 ];
159 if (_profile == null) {
160 children.addAll([
161 new DivElement()..classes = const ['content-centered-big']
162 ..children = [
163 new HeadingElement.h2()..text = 'Loading...'
164 ]
165 ]);
166 } else {
167 final newChartHost = new DivElement()..classes = const ['host'];
168 final newChartLegend = new DivElement()..classes = const ['legend'];
169 final oldChartHost = new DivElement()..classes = const ['host'];
170 final oldChartLegend = new DivElement()..classes = const ['legend'];
171 children.addAll([
172 new DivElement()..classes = const ['content-centered-big']
173 ..children = [
174 new DivElement()..classes = const ['memberList']
175 ..children = [
176 new DivElement()..classes = const ['memberItem']
177 ..children = [
178 new DivElement()..classes = const ['memberName']
179 ..text = 'last forced GC at',
180 new DivElement()..classes = const ['memberValue']
181 ..text = '${_profile.lastServiceGC}',
182 ],
183 new DivElement()..classes = const ['memberItem']
184 ..children = [
185 new DivElement()..classes = const ['memberName']
186 ..text = 'last accumulator reset at',
187 new DivElement()..classes = const ['memberValue']
188 ..text = '${_profile.lastAccumulatorReset}',
189 ]
190 ],
191 new HRElement(),
192 ],
193 new DivElement()..classes = const ['content-centered-big']
194 ..children = [
195 new DivElement()..classes = const ['heap-space', 'left']
196 ..children = [
197 new HeadingElement.h2()..text = 'New Generation',
198 new BRElement(),
199 new DivElement()..classes = const ['memberList']
200 ..children = _createSpaceMembers(_profile.newSpace),
201 new BRElement(),
202 new DivElement()..classes = const ['chart']
203 ..children = [newChartLegend, newChartHost]
204 ],
205 new DivElement()..classes = const ['heap-space', 'right']
206 ..children = [
207 new HeadingElement.h2()..text = 'Old Generation',
208 new BRElement(),
209 new DivElement()..classes = const ['memberList']
210 ..children = _createSpaceMembers(_profile.oldSpace),
211 new BRElement(),
212 new DivElement()..classes = const ['chart']
213 ..children = [oldChartLegend, oldChartHost]
214 ],
215 new BRElement(), new HRElement()
216 ],
217 new DivElement()..classes = const ['collection']
218 ..children = [
219 new VirtualCollectionElement(
220 _createCollectionLine,
221 _updateCollectionLine,
222 createHeader: _createCollectionHeader,
223 items: _profile.members.toList()..sort(_createSorter()),
224 queue: _r.queue)
225 ]
226 ]);
227 _renderGraph(newChartHost, newChartLegend, _profile.newSpace);
228 _renderGraph(oldChartHost, oldChartLegend, _profile.oldSpace);
229 }
230 }
231
232 _createSorter() {
233 var getter;
234 switch (_sortingField) {
235 case _SortingField.accumulatedSize:
236 getter = _getAccumulatedSize;
237 break;
238 case _SortingField.accumulatedInstances:
239 getter = _getAccumulatedInstances;
240 break;
241 case _SortingField.currentSize:
242 getter = _getCurrentSize;
243 break;
244 case _SortingField.currentInstances:
245 getter = _getCurrentInstances;
246 break;
247 case _SortingField.newAccumulatedSize:
248 getter = _getNewAccumulatedSize;
249 break;
250 case _SortingField.newAccumulatedInstances:
251 getter = _getNewAccumulatedInstances;
252 break;
253 case _SortingField.newCurrentSize:
254 getter = _getNewCurrentSize;
255 break;
256 case _SortingField.newCurrentInstances:
257 getter = _getNewCurrentInstances;
258 break;
259 case _SortingField.oldAccumulatedSize:
260 getter = _getOldAccumulatedSize;
261 break;
262 case _SortingField.oldAccumulatedInstances:
263 getter = _getOldAccumulatedInstances;
264 break;
265 case _SortingField.oldCurrentSize:
266 getter = _getOldCurrentSize;
267 break;
268 case _SortingField.oldCurrentInstances:
269 getter = _getOldCurrentInstances;
270 break;
271 case _SortingField.className:
272 getter = (M.ClassHeapStats s) => s.clazz.name;
Cutch 2016/08/17 14:28:44 missing final break
cbernaschina 2016/08/17 17:01:27 Done. Should the analyzer signal this?
273 }
274 switch (_sortingDirection) {
275 case _SortingDirection.ascending:
276 return (a, b) => getter(a).compareTo(getter(b));
277 case _SortingDirection.descending:
278 return (a, b) => getter(b).compareTo(getter(a));
279 }
280 }
281
282 static Element _createCollectionLine() =>
283 new DivElement()
284 ..classes = const ['collection-item']
285 ..children = [
286 new SpanElement()..classes = const ['bytes']
287 ..text = '0B',
288 new SpanElement()..classes = const ['instances']
289 ..text = '0',
290 new SpanElement()..classes = const ['bytes']
291 ..text = '0B',
292 new SpanElement()..classes = const ['instances']
293 ..text = '0',
294 new SpanElement()..classes = const ['bytes']
295 ..text = '0B',
296 new SpanElement()..classes = const ['instances']
297 ..text = '0',
298 new SpanElement()..classes = const ['bytes']
299 ..text = '0B',
300 new SpanElement()..classes = const ['instances']
301 ..text = '0',
302 new SpanElement()..classes = const ['bytes']
303 ..text = '0B',
304 new SpanElement()..classes = const ['instances']
305 ..text = '0',
306 new SpanElement()..classes = const ['bytes']
307 ..text = '0B',
308 new SpanElement()..classes = const ['instances']
309 ..text = '0',
310 new SpanElement()..classes = const ['name']
311 ];
312
313 Element _createCollectionHeader() =>
314 new DivElement()
315 ..children = [
316 new DivElement()
317 ..classes = const ['collection-item']
318 ..children = [
319 new SpanElement()..classes = const ['group']
320 ..text = 'Accumulated',
321 new SpanElement()..classes = const ['group']
322 ..text = 'Current',
323 new SpanElement()..classes = const ['group']
324 ..text = '(NEW) Accumulated',
325 new SpanElement()..classes = const ['group']
326 ..text = '(NEW) Current',
327 new SpanElement()..classes = const ['group']
328 ..text = '(OLD) Accumulated',
329 new SpanElement()..classes = const ['group']
330 ..text = '(OLD) Current',
331 ],
332 new DivElement()
333 ..classes = const ['collection-item']
334 ..children = [
335 _createHeaderButton(const ['bytes'], 'Size',
336 _SortingField.accumulatedSize,
337 _SortingDirection.descending),
338 _createHeaderButton(const ['instances'], 'Instances',
339 _SortingField.accumulatedInstances,
340 _SortingDirection.descending),
341 _createHeaderButton(const ['bytes'], 'Size',
342 _SortingField.currentSize,
343 _SortingDirection.descending),
344 _createHeaderButton(const ['instances'], 'Instances',
345 _SortingField.currentInstances,
346 _SortingDirection.descending),
347 _createHeaderButton(const ['bytes'], 'Size',
348 _SortingField.newAccumulatedSize,
349 _SortingDirection.descending),
350 _createHeaderButton(const ['instances'], 'Instances',
351 _SortingField.newAccumulatedInstances,
352 _SortingDirection.descending),
353 _createHeaderButton(const ['bytes'], 'Size',
354 _SortingField.newCurrentSize,
355 _SortingDirection.descending),
356 _createHeaderButton(const ['instances'], 'Instances',
357 _SortingField.newCurrentInstances,
358 _SortingDirection.descending),
359 _createHeaderButton(const ['bytes'], 'Size',
360 _SortingField.oldAccumulatedSize,
361 _SortingDirection.descending),
362 _createHeaderButton(const ['instances'], 'Instances',
363 _SortingField.oldAccumulatedInstances,
364 _SortingDirection.descending),
365 _createHeaderButton(const ['bytes'], 'Size',
366 _SortingField.oldCurrentSize,
367 _SortingDirection.descending),
368 _createHeaderButton(const ['instances'], 'Instances',
369 _SortingField.oldCurrentInstances,
370 _SortingDirection.descending),
371 _createHeaderButton(const ['name'], 'Class',
372 _SortingField.className,
373 _SortingDirection.ascending)
374 ],
375 ];
376
377 ButtonElement _createHeaderButton(List<String> classes,
378 String text,
379 _SortingField field,
380 _SortingDirection direction) =>
381 new ButtonElement()..classes = classes
382 ..text = _sortingField != field ? text :
383 _sortingDirection == _SortingDirection.ascending
384 ? '$textâ–¼' : '$textâ–²'
385 ..onClick.listen((_) => _setSorting(field, direction));
386
387
388 void _setSorting(_SortingField field,
389 _SortingDirection defaultDirection) {
390 if (_sortingField == field) {
391 switch (_sortingDirection) {
392 case _SortingDirection.descending:
393 _sortingDirection = _SortingDirection.ascending;
394 break;
395 case _SortingDirection.ascending:
396 _sortingDirection = _SortingDirection.descending;
397 break;
398 }
399 } else {
400 _sortingDirection = defaultDirection;
401 _sortingField = field;
402 }
403 _r.dirty();
404 }
405
406 void _updateCollectionLine(Element e, M.ClassHeapStats item,
407 index) {
408 e.children[0].text = Utils.formatSize(_getAccumulatedSize(item));
409 e.children[1].text = '${_getAccumulatedInstances(item)}';
410 e.children[2].text = Utils.formatSize(_getCurrentSize(item));
411 e.children[3].text = '${_getCurrentInstances(item)}';
412 e.children[4].text = Utils.formatSize(_getNewAccumulatedSize(item));
413 e.children[5].text = '${_getNewAccumulatedInstances(item)}';
414 e.children[6].text = Utils.formatSize(_getNewCurrentSize(item));
415 e.children[7].text = '${_getNewCurrentInstances(item)}';
416 e.children[8].text = Utils.formatSize(_getOldAccumulatedSize(item));
417 e.children[9].text = '${_getOldAccumulatedInstances(item)}';
418 e.children[10].text = Utils.formatSize(_getOldCurrentSize(item));
419 e.children[11].text = '${_getOldCurrentInstances(item)}';
420 e.children[12] = new ClassRefElement(_isolate, item.clazz, queue: _r.queue)
421 ..classes = ['name'];
422 }
423
424 static List<Element> _createSpaceMembers(M.HeapSpace space) {
425 final used = '${Utils.formatSize(space.used)}'
426 ' of '
427 '${Utils.formatSize(space.capacity)}';
428 final ext = '${Utils.formatSize(space.external)}';
429 final collections = '${space.collections}';
430 final avgCollectionTime =
431 '${Utils.formatDurationInMilliseconds(space.avgCollectionTime)} ms';
432 final totalCollectionTime =
433 '${Utils.formatDurationInSeconds(space.totalCollectionTime)} secs';
434 final avgCollectionPeriod =
435 '${Utils.formatDurationInMilliseconds(space.avgCollectionPeriod)} ms';
436 return [
437 new DivElement()..classes = ['memberItem']
438 ..children = [
439 new DivElement()..classes = ['memberName']..text = 'used',
440 new DivElement()..classes = ['memberValue']
441 ..text = used
442 ],
443 new DivElement()..classes = ['memberItem']
444 ..children = [
445 new DivElement()..classes = ['memberName']
446 ..text = 'external',
447 new DivElement()..classes = ['memberValue']
448 ..text = ext
449 ],
450 new DivElement()..classes = ['memberItem']
451 ..children = [
452 new DivElement()..classes = ['memberName']
453 ..text = 'collections',
454 new DivElement()..classes = ['memberValue']
455 ..text = collections
456 ],
457 new DivElement()..classes = ['memberItem']
458 ..children = [
459 new DivElement()..classes = ['memberName']
460 ..text = 'average collection time',
461 new DivElement()..classes = ['memberValue']
462 ..text = avgCollectionTime
463 ],
464 new DivElement()..classes = ['memberItem']
465 ..children = [
466 new DivElement()..classes = ['memberName']
467 ..text = 'cumulative collection time',
468 new DivElement()..classes = ['memberValue']
469 ..text = totalCollectionTime
470 ],
471 new DivElement()..classes = ['memberItem']
472 ..children = [
473 new DivElement()..classes = ['memberName']
474 ..text = 'average time between collections',
475 new DivElement()..classes = ['memberValue']
476 ..text = avgCollectionPeriod
477 ]
478 ];
479 }
480
481 static final _columns = [
482 new ChartColumnSpec(label: 'Type', type: ChartColumnSpec.TYPE_STRING),
483 new ChartColumnSpec(label: 'Size', formatter: (v) => v.toString())
484 ];
485
486 static void _renderGraph(Element host, Element legend, M.HeapSpace space) {
487 final series = [new ChartSeries("Work", [1], new PieChartRenderer(
488 sortDataByValue: false
489 ))];
490 final rect = host.getBoundingClientRect();
491 final minSize = new Rect.size(rect.width, rect.height);
492 final config = new ChartConfig(series, [0])
493 ..minimumSize = minSize
494 ..legend = new ChartLegend(legend, showValues: true);
495 final data = new ChartData(_columns, [
496 ['Used', space.used],
497 ['Free', space.capacity - space.used],
498 ['External', space.external]
499 ]);
500
501 new LayoutArea(host, data, config, state: new ChartState(),
502 autoUpdate: true)
503 ..draw();
504 }
505
506 Future _refresh({bool gc: false, bool reset: false}) async {
507 _profile = null;
508 _r.dirty();
509 _profile = await _repository.get(_isolate, gc: gc, reset: reset);
510 _r.dirty();
511 }
512
513 void _downloadCSV() {
514 assert(_profile != null);
515 final header = ['"Accumulator Size"',
516 '"Accumulator Instances"',
517 '"Current Size"',
518 '"Current Instances"',
519 '"(NEW) Accumulator Size"',
520 '"(NEW) Accumulator Instances"',
521 '"(NEW) Current Size"',
522 '"(NEW) Current Instances"',
523 '"(OLD) Accumulator Size"',
524 '"(OLD) Accumulator Instances"',
525 '"(OLD) Current Size"',
526 '"(OLD) Current Instances"',
527 'Class'
528 ].join(';') + '\n';
529 AnchorElement tl = document.createElement('a');
530 tl..attributes['href'] = 'data:text/plain;charset=utf-8,' +
531 Uri.encodeComponent(header +
532 (_profile.members.toList()..sort(_createSorter()))
533 .map(_csvOut).join('\n'))
534 ..attributes['download'] = 'heap-profile.csv'
535 ..click();
536 }
537
538 static _csvOut(M.ClassHeapStats s) {
539 return [
540 _getAccumulatedSize(s),
541 _getAccumulatedInstances(s),
542 _getCurrentSize(s),
543 _getCurrentInstances(s),
544 _getNewAccumulatedSize(s),
545 _getNewAccumulatedInstances(s),
546 _getNewCurrentSize(s),
547 _getNewCurrentInstances(s),
548 _getOldAccumulatedSize(s),
549 _getOldAccumulatedInstances(s),
550 _getOldCurrentSize(s),
551 _getOldCurrentInstances(s),
552 s.clazz.name
553 ].join(';');
554 }
555
556 static int _getAccumulatedSize(M.ClassHeapStats s) =>
557 s.newSpace.accumulated.bytes + s.oldSpace.accumulated.bytes;
558 static int _getAccumulatedInstances(M.ClassHeapStats s) =>
559 s.newSpace.accumulated.instances + s.oldSpace.accumulated.instances;
560 static int _getCurrentSize(M.ClassHeapStats s) =>
561 s.newSpace.current.bytes + s.oldSpace.current.bytes;
562 static int _getCurrentInstances(M.ClassHeapStats s) =>
563 s.newSpace.current.instances + s.oldSpace.current.instances;
564 static int _getNewAccumulatedSize(M.ClassHeapStats s) =>
565 s.newSpace.accumulated.bytes;
566 static int _getNewAccumulatedInstances(M.ClassHeapStats s) =>
567 s.newSpace.accumulated.instances;
568 static int _getNewCurrentSize(M.ClassHeapStats s) =>
569 s.newSpace.current.bytes;
570 static int _getNewCurrentInstances(M.ClassHeapStats s) =>
571 s.newSpace.current.instances;
572 static int _getOldAccumulatedSize(M.ClassHeapStats s) =>
573 s.oldSpace.accumulated.bytes;
574 static int _getOldAccumulatedInstances(M.ClassHeapStats s) =>
575 s.oldSpace.accumulated.instances;
576 static int _getOldCurrentSize(M.ClassHeapStats s) =>
577 s.oldSpace.current.bytes;
578 static int _getOldCurrentInstances(M.ClassHeapStats s) =>
579 s.oldSpace.current.instances;
580 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698