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

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

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

Powered by Google App Engine
This is Rietveld 408576698