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

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: 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 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)) {
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 == null ? '---'
182 : '${_profile.lastServiceGC}',
183 ],
184 new DivElement()..classes = const ['memberItem']
185 ..children = [
186 new DivElement()..classes = const ['memberName']
187 ..text = 'last accumulator reset at',
188 new DivElement()..classes = const ['memberValue']
189 ..text = _profile.lastAccumulatorReset == null ? '---'
190 : '${_profile.lastAccumulatorReset}',
191 ]
192 ],
193 new HRElement(),
194 ],
195 new DivElement()..classes = const ['content-centered-big']
196 ..children = [
197 new DivElement()..classes = const ['heap-space', 'left']
198 ..children = [
199 new HeadingElement.h2()..text = 'New Generation',
200 new BRElement(),
201 new DivElement()..classes = const ['memberList']
202 ..children = _createSpaceMembers(_profile.newSpace),
203 new BRElement(),
204 new DivElement()..classes = const ['chart']
205 ..children = [newChartLegend, newChartHost]
206 ],
207 new DivElement()..classes = const ['heap-space', 'right']
208 ..children = [
209 new HeadingElement.h2()..text = 'Old Generation',
210 new BRElement(),
211 new DivElement()..classes = const ['memberList']
212 ..children = _createSpaceMembers(_profile.oldSpace),
213 new BRElement(),
214 new DivElement()..classes = const ['chart']
215 ..children = [oldChartLegend, oldChartHost]
216 ],
217 new BRElement(), new HRElement()
218 ],
219 new DivElement()..classes = const ['collection']
220 ..children = [
221 new VirtualCollectionElement(
222 _createCollectionLine,
223 _updateCollectionLine,
224 createHeader: _createCollectionHeader,
225 items: _profile.members.toList()..sort(_createSorter()),
226 queue: _r.queue)
227 ]
228 ]);
229 _renderGraph(newChartHost, newChartLegend, _profile.newSpace);
230 _renderGraph(oldChartHost, oldChartLegend, _profile.oldSpace);
231 }
232 }
233
234 _createSorter() {
235 var getter;
236 switch (_sortingField) {
237 case _SortingField.accumulatedSize:
238 getter = _getAccumulatedSize;
239 break;
240 case _SortingField.accumulatedInstances:
241 getter = _getAccumulatedInstances;
242 break;
243 case _SortingField.currentSize:
244 getter = _getCurrentSize;
245 break;
246 case _SortingField.currentInstances:
247 getter = _getCurrentInstances;
248 break;
249 case _SortingField.newAccumulatedSize:
250 getter = _getNewAccumulatedSize;
251 break;
252 case _SortingField.newAccumulatedInstances:
253 getter = _getNewAccumulatedInstances;
254 break;
255 case _SortingField.newCurrentSize:
256 getter = _getNewCurrentSize;
257 break;
258 case _SortingField.newCurrentInstances:
259 getter = _getNewCurrentInstances;
260 break;
261 case _SortingField.oldAccumulatedSize:
262 getter = _getOldAccumulatedSize;
263 break;
264 case _SortingField.oldAccumulatedInstances:
265 getter = _getOldAccumulatedInstances;
266 break;
267 case _SortingField.oldCurrentSize:
268 getter = _getOldCurrentSize;
269 break;
270 case _SortingField.oldCurrentInstances:
271 getter = _getOldCurrentInstances;
272 break;
273 case _SortingField.className:
274 getter = (M.ClassHeapStats s) => s.clazz.name;
275 break;
276 }
277 switch (_sortingDirection) {
278 case _SortingDirection.ascending:
279 return (a, b) => getter(a).compareTo(getter(b));
280 case _SortingDirection.descending:
281 return (a, b) => getter(b).compareTo(getter(a));
282 }
283 }
284
285 static Element _createCollectionLine() =>
286 new DivElement()
287 ..classes = const ['collection-item']
288 ..children = [
289 new SpanElement()..classes = const ['bytes']
290 ..text = '0B',
291 new SpanElement()..classes = const ['instances']
292 ..text = '0',
293 new SpanElement()..classes = const ['bytes']
294 ..text = '0B',
295 new SpanElement()..classes = const ['instances']
296 ..text = '0',
297 new SpanElement()..classes = const ['bytes']
298 ..text = '0B',
299 new SpanElement()..classes = const ['instances']
300 ..text = '0',
301 new SpanElement()..classes = const ['bytes']
302 ..text = '0B',
303 new SpanElement()..classes = const ['instances']
304 ..text = '0',
305 new SpanElement()..classes = const ['bytes']
306 ..text = '0B',
307 new SpanElement()..classes = const ['instances']
308 ..text = '0',
309 new SpanElement()..classes = const ['bytes']
310 ..text = '0B',
311 new SpanElement()..classes = const ['instances']
312 ..text = '0',
313 new SpanElement()..classes = const ['name']
314 ];
315
316 Element _createCollectionHeader() =>
317 new DivElement()
318 ..children = [
319 new DivElement()
320 ..classes = const ['collection-item']
321 ..children = [
322 new SpanElement()..classes = const ['group']
323 ..text = 'Accumulated',
324 new SpanElement()..classes = const ['group']
325 ..text = 'Current',
326 new SpanElement()..classes = const ['group']
327 ..text = '(NEW) Accumulated',
328 new SpanElement()..classes = const ['group']
329 ..text = '(NEW) Current',
330 new SpanElement()..classes = const ['group']
331 ..text = '(OLD) Accumulated',
332 new SpanElement()..classes = const ['group']
333 ..text = '(OLD) Current',
334 ],
335 new DivElement()
336 ..classes = const ['collection-item']
337 ..children = [
338 _createHeaderButton(const ['bytes'], 'Size',
339 _SortingField.accumulatedSize,
340 _SortingDirection.descending),
341 _createHeaderButton(const ['instances'], 'Instances',
342 _SortingField.accumulatedInstances,
343 _SortingDirection.descending),
344 _createHeaderButton(const ['bytes'], 'Size',
345 _SortingField.currentSize,
346 _SortingDirection.descending),
347 _createHeaderButton(const ['instances'], 'Instances',
348 _SortingField.currentInstances,
349 _SortingDirection.descending),
350 _createHeaderButton(const ['bytes'], 'Size',
351 _SortingField.newAccumulatedSize,
352 _SortingDirection.descending),
353 _createHeaderButton(const ['instances'], 'Instances',
354 _SortingField.newAccumulatedInstances,
355 _SortingDirection.descending),
356 _createHeaderButton(const ['bytes'], 'Size',
357 _SortingField.newCurrentSize,
358 _SortingDirection.descending),
359 _createHeaderButton(const ['instances'], 'Instances',
360 _SortingField.newCurrentInstances,
361 _SortingDirection.descending),
362 _createHeaderButton(const ['bytes'], 'Size',
363 _SortingField.oldAccumulatedSize,
364 _SortingDirection.descending),
365 _createHeaderButton(const ['instances'], 'Instances',
366 _SortingField.oldAccumulatedInstances,
367 _SortingDirection.descending),
368 _createHeaderButton(const ['bytes'], 'Size',
369 _SortingField.oldCurrentSize,
370 _SortingDirection.descending),
371 _createHeaderButton(const ['instances'], 'Instances',
372 _SortingField.oldCurrentInstances,
373 _SortingDirection.descending),
374 _createHeaderButton(const ['name'], 'Class',
375 _SortingField.className,
376 _SortingDirection.ascending)
377 ],
378 ];
379
380 ButtonElement _createHeaderButton(List<String> classes,
381 String text,
382 _SortingField field,
383 _SortingDirection direction) =>
384 new ButtonElement()..classes = classes
385 ..text = _sortingField != field ? text :
386 _sortingDirection == _SortingDirection.ascending
387 ? '$textâ–¼' : '$textâ–²'
388 ..onClick.listen((_) => _setSorting(field, direction));
389
390
391 void _setSorting(_SortingField field,
392 _SortingDirection defaultDirection) {
393 if (_sortingField == field) {
394 switch (_sortingDirection) {
395 case _SortingDirection.descending:
396 _sortingDirection = _SortingDirection.ascending;
397 break;
398 case _SortingDirection.ascending:
399 _sortingDirection = _SortingDirection.descending;
400 break;
401 }
402 } else {
403 _sortingDirection = defaultDirection;
404 _sortingField = field;
405 }
406 _r.dirty();
407 }
408
409 void _updateCollectionLine(Element e, M.ClassHeapStats item,
410 index) {
411 e.children[0].text = Utils.formatSize(_getAccumulatedSize(item));
412 e.children[1].text = '${_getAccumulatedInstances(item)}';
413 e.children[2].text = Utils.formatSize(_getCurrentSize(item));
414 e.children[3].text = '${_getCurrentInstances(item)}';
415 e.children[4].text = Utils.formatSize(_getNewAccumulatedSize(item));
416 e.children[5].text = '${_getNewAccumulatedInstances(item)}';
417 e.children[6].text = Utils.formatSize(_getNewCurrentSize(item));
418 e.children[7].text = '${_getNewCurrentInstances(item)}';
419 e.children[8].text = Utils.formatSize(_getOldAccumulatedSize(item));
420 e.children[9].text = '${_getOldAccumulatedInstances(item)}';
421 e.children[10].text = Utils.formatSize(_getOldCurrentSize(item));
422 e.children[11].text = '${_getOldCurrentInstances(item)}';
423 e.children[12] = new ClassRefElement(_isolate, item.clazz, queue: _r.queue)
424 ..classes = ['name'];
425 }
426
427 static List<Element> _createSpaceMembers(M.HeapSpace space) {
428 final used = '${Utils.formatSize(space.used)}'
429 ' of '
430 '${Utils.formatSize(space.capacity)}';
431 final ext = '${Utils.formatSize(space.external)}';
432 final collections = '${space.collections}';
433 final avgCollectionTime =
434 '${Utils.formatDurationInMilliseconds(space.avgCollectionTime)} ms';
435 final totalCollectionTime =
436 '${Utils.formatDurationInSeconds(space.totalCollectionTime)} secs';
437 final avgCollectionPeriod =
438 '${Utils.formatDurationInMilliseconds(space.avgCollectionPeriod)} ms';
439 return [
440 new DivElement()..classes = ['memberItem']
441 ..children = [
442 new DivElement()..classes = ['memberName']..text = 'used',
443 new DivElement()..classes = ['memberValue']
444 ..text = used
445 ],
446 new DivElement()..classes = ['memberItem']
447 ..children = [
448 new DivElement()..classes = ['memberName']
449 ..text = 'external',
450 new DivElement()..classes = ['memberValue']
451 ..text = ext
452 ],
453 new DivElement()..classes = ['memberItem']
454 ..children = [
455 new DivElement()..classes = ['memberName']
456 ..text = 'collections',
457 new DivElement()..classes = ['memberValue']
458 ..text = collections
459 ],
460 new DivElement()..classes = ['memberItem']
461 ..children = [
462 new DivElement()..classes = ['memberName']
463 ..text = 'average collection time',
464 new DivElement()..classes = ['memberValue']
465 ..text = avgCollectionTime
466 ],
467 new DivElement()..classes = ['memberItem']
468 ..children = [
469 new DivElement()..classes = ['memberName']
470 ..text = 'cumulative collection time',
471 new DivElement()..classes = ['memberValue']
472 ..text = totalCollectionTime
473 ],
474 new DivElement()..classes = ['memberItem']
475 ..children = [
476 new DivElement()..classes = ['memberName']
477 ..text = 'average time between collections',
478 new DivElement()..classes = ['memberValue']
479 ..text = avgCollectionPeriod
480 ]
481 ];
482 }
483
484 static final _columns = [
485 new ChartColumnSpec(label: 'Type', type: ChartColumnSpec.TYPE_STRING),
486 new ChartColumnSpec(label: 'Size', formatter: (v) => v.toString())
487 ];
488
489 static void _renderGraph(Element host, Element legend, M.HeapSpace space) {
490 final series = [new ChartSeries("Work", [1], new PieChartRenderer(
491 sortDataByValue: false
492 ))];
493 final rect = host.getBoundingClientRect();
494 final minSize = new Rect.size(rect.width, rect.height);
495 final config = new ChartConfig(series, [0])
496 ..minimumSize = minSize
497 ..legend = new ChartLegend(legend, showValues: true);
498 final data = new ChartData(_columns, [
499 ['Used', space.used],
500 ['Free', space.capacity - space.used],
501 ['External', space.external]
502 ]);
503
504 new LayoutArea(host, data, config, state: new ChartState(),
505 autoUpdate: true)
506 ..draw();
507 }
508
509 Future _refresh({bool gc: false, bool reset: false}) async {
510 _profile = null;
511 _r.dirty();
512 _profile = await _repository.get(_isolate, gc: gc, reset: reset);
513 _r.dirty();
514 }
515
516 void _downloadCSV() {
517 assert(_profile != null);
518 final header = ['"Accumulator Size"',
519 '"Accumulator Instances"',
520 '"Current Size"',
521 '"Current Instances"',
522 '"(NEW) Accumulator Size"',
523 '"(NEW) Accumulator Instances"',
524 '"(NEW) Current Size"',
525 '"(NEW) Current Instances"',
526 '"(OLD) Accumulator Size"',
527 '"(OLD) Accumulator Instances"',
528 '"(OLD) Current Size"',
529 '"(OLD) Current Instances"',
530 'Class'
531 ].join(';') + '\n';
532 AnchorElement tl = document.createElement('a');
533 tl..attributes['href'] = 'data:text/plain;charset=utf-8,' +
534 Uri.encodeComponent(header +
535 (_profile.members.toList()..sort(_createSorter()))
536 .map(_csvOut).join('\n'))
537 ..attributes['download'] = 'heap-profile.csv'
538 ..click();
539 }
540
541 static _csvOut(M.ClassHeapStats s) {
542 return [
543 _getAccumulatedSize(s),
544 _getAccumulatedInstances(s),
545 _getCurrentSize(s),
546 _getCurrentInstances(s),
547 _getNewAccumulatedSize(s),
548 _getNewAccumulatedInstances(s),
549 _getNewCurrentSize(s),
550 _getNewCurrentInstances(s),
551 _getOldAccumulatedSize(s),
552 _getOldAccumulatedInstances(s),
553 _getOldCurrentSize(s),
554 _getOldCurrentInstances(s),
555 s.clazz.name
556 ].join(';');
557 }
558
559 static int _getAccumulatedSize(M.ClassHeapStats s) =>
560 s.newSpace.accumulated.bytes + s.oldSpace.accumulated.bytes;
561 static int _getAccumulatedInstances(M.ClassHeapStats s) =>
562 s.newSpace.accumulated.instances + s.oldSpace.accumulated.instances;
563 static int _getCurrentSize(M.ClassHeapStats s) =>
564 s.newSpace.current.bytes + s.oldSpace.current.bytes;
565 static int _getCurrentInstances(M.ClassHeapStats s) =>
566 s.newSpace.current.instances + s.oldSpace.current.instances;
567 static int _getNewAccumulatedSize(M.ClassHeapStats s) =>
568 s.newSpace.accumulated.bytes;
569 static int _getNewAccumulatedInstances(M.ClassHeapStats s) =>
570 s.newSpace.accumulated.instances;
571 static int _getNewCurrentSize(M.ClassHeapStats s) =>
572 s.newSpace.current.bytes;
573 static int _getNewCurrentInstances(M.ClassHeapStats s) =>
574 s.newSpace.current.instances;
575 static int _getOldAccumulatedSize(M.ClassHeapStats s) =>
576 s.oldSpace.accumulated.bytes;
577 static int _getOldAccumulatedInstances(M.ClassHeapStats s) =>
578 s.oldSpace.accumulated.instances;
579 static int _getOldCurrentSize(M.ClassHeapStats s) =>
580 s.oldSpace.current.bytes;
581 static int _getOldCurrentInstances(M.ClassHeapStats s) =>
582 s.oldSpace.current.instances;
583 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/app/page.dart ('k') | runtime/observatory/lib/src/elements/containers/virtual_collection.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698