Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, 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 /// This Element is part of MemoryDashboardElement. | 5 /// This Element is part of MemoryDashboardElement. |
| 6 /// | 6 /// |
| 7 /// The Element is stripped down version of AllocationProfileElement where | 7 /// The Element is stripped down version of AllocationProfileElement where |
| 8 /// concepts like old and new space has been hidden away. | 8 /// concepts like old and new space has been hidden away. |
| 9 /// | 9 /// |
| 10 /// For each class in the system it is shown the Total number of instances | 10 /// For each class in the system it is shown the Total number of instances |
| 11 /// alive, the Total memory used by these instances, the number of instances | 11 /// alive, the Total memory used by these instances, the number of instances |
| 12 /// created since the last reset, the memory used by these instances. | 12 /// created since the last reset, the memory used by these instances. |
| 13 /// | 13 /// |
| 14 /// When a GC event is received the profile is reloaded. | 14 /// When a GC event is received the profile is reloaded. |
| 15 | 15 |
| 16 import 'dart:async'; | 16 import 'dart:async'; |
| 17 import 'dart:html'; | 17 import 'dart:html'; |
| 18 import 'package:observatory/models.dart' as M; | 18 import 'package:observatory/models.dart' as M; |
| 19 import 'package:observatory/src/elements/class_ref.dart'; | |
| 20 import 'package:observatory/src/elements/containers/virtual_collection.dart'; | |
| 21 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; | 19 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| 22 import 'package:observatory/src/elements/helpers/tag.dart'; | 20 import 'package:observatory/src/elements/helpers/tag.dart'; |
| 23 import 'package:observatory/utils.dart'; | 21 import 'package:observatory/src/elements/memory/allocations.dart'; |
| 22 import 'package:observatory/src/elements/memory/snapshot.dart'; | |
| 24 | 23 |
| 25 enum _SortingField { | 24 enum _Analysis { allocations, dominatorTree } |
| 26 accumulatedSize, | |
| 27 accumulatedInstances, | |
| 28 currentSize, | |
| 29 currentInstances, | |
| 30 className, | |
| 31 } | |
| 32 | |
| 33 enum _SortingDirection { ascending, descending } | |
| 34 | 25 |
| 35 class MemoryProfileElement extends HtmlElement implements Renderable { | 26 class MemoryProfileElement extends HtmlElement implements Renderable { |
| 36 static const tag = const Tag<MemoryProfileElement>('memory-profile', | 27 static const tag = const Tag<MemoryProfileElement>('memory-profile', |
| 37 dependencies: const [ClassRefElement.tag, VirtualCollectionElement.tag]); | 28 dependencies: const [ |
| 29 MemoryAllocationsElement.tag, | |
| 30 MemorySnapshotElement.tag | |
| 31 ]); | |
| 38 | 32 |
| 39 RenderingScheduler<MemoryProfileElement> _r; | 33 RenderingScheduler<MemoryProfileElement> _r; |
| 40 | 34 |
| 41 Stream<RenderedEvent<MemoryProfileElement>> get onRendered => _r.onRendered; | 35 Stream<RenderedEvent<MemoryProfileElement>> get onRendered => _r.onRendered; |
| 42 | 36 |
| 43 M.IsolateRef _isolate; | 37 M.IsolateRef _isolate; |
| 44 M.EventRepository _events; | 38 M.EventRepository _events; |
| 45 M.AllocationProfileRepository _repository; | 39 M.AllocationProfileRepository _allocations; |
| 46 M.AllocationProfile _profile; | |
| 47 M.EditorRepository _editor; | 40 M.EditorRepository _editor; |
| 48 StreamSubscription _gcSubscription; | 41 M.HeapSnapshotRepository _snapshots; |
| 49 _SortingField _sortingField = _SortingField.accumulatedInstances; | 42 M.ObjectRepository _objects; |
| 50 _SortingDirection _sortingDirection = _SortingDirection.descending; | 43 |
| 44 _Analysis _analisys = _Analysis.allocations; | |
|
siva
2017/08/19 00:35:46
is the typo in name intentional (_analisys instead
cbernaschina
2017/08/19 01:03:37
done
| |
| 51 | 45 |
| 52 M.IsolateRef get isolate => _isolate; | 46 M.IsolateRef get isolate => _isolate; |
| 53 | 47 |
| 54 factory MemoryProfileElement(M.IsolateRef isolate, M.EditorRepository editor, | 48 factory MemoryProfileElement( |
| 55 M.EventRepository events, M.AllocationProfileRepository repository, | 49 M.IsolateRef isolate, |
| 50 M.EditorRepository editor, | |
| 51 M.EventRepository events, | |
| 52 M.AllocationProfileRepository allocations, | |
| 53 M.HeapSnapshotRepository snapshots, | |
| 54 M.ObjectRepository objects, | |
| 56 {RenderingQueue queue}) { | 55 {RenderingQueue queue}) { |
| 57 assert(isolate != null); | 56 assert(isolate != null); |
| 58 assert(events != null); | 57 assert(events != null); |
| 59 assert(editor != null); | 58 assert(editor != null); |
| 60 assert(repository != null); | 59 assert(allocations != null); |
| 60 assert(snapshots != null); | |
| 61 assert(objects != null); | |
| 61 MemoryProfileElement e = document.createElement(tag.name); | 62 MemoryProfileElement e = document.createElement(tag.name); |
| 62 e._r = new RenderingScheduler(e, queue: queue); | 63 e._r = new RenderingScheduler(e, queue: queue); |
| 63 e._isolate = isolate; | 64 e._isolate = isolate; |
| 64 e._editor = editor; | 65 e._editor = editor; |
| 65 e._events = events; | 66 e._events = events; |
| 66 e._repository = repository; | 67 e._allocations = allocations; |
| 68 e._snapshots = snapshots; | |
| 69 e._objects = objects; | |
| 67 return e; | 70 return e; |
| 68 } | 71 } |
| 69 | 72 |
| 70 MemoryProfileElement.created() : super.created(); | 73 MemoryProfileElement.created() : super.created(); |
| 71 | 74 |
| 72 @override | 75 @override |
| 73 attached() { | 76 attached() { |
| 74 super.attached(); | 77 super.attached(); |
| 75 _r.enable(); | 78 _r.enable(); |
| 76 _refresh(); | |
| 77 _gcSubscription = _events.onGCEvent.listen((e) { | |
| 78 if (e.isolate.id == _isolate.id) { | |
| 79 _refresh(); | |
| 80 } | |
| 81 }); | |
| 82 } | 79 } |
| 83 | 80 |
| 84 @override | 81 @override |
| 85 detached() { | 82 detached() { |
| 86 super.detached(); | 83 super.detached(); |
| 87 _r.disable(notify: true); | 84 _r.disable(notify: true); |
| 88 children = []; | 85 children = []; |
| 89 _gcSubscription.cancel(); | |
| 90 } | |
| 91 | |
| 92 Future reload({bool gc = false, bool reset = false}) async { | |
| 93 return _refresh(gc: gc, reset: reset); | |
| 94 } | 86 } |
| 95 | 87 |
| 96 void render() { | 88 void render() { |
| 97 if (_profile == null) { | 89 HtmlElement current; |
| 98 children = [ | 90 var reload; |
| 99 new DivElement() | 91 switch (_analisys) { |
| 100 ..classes = ['content-centered-big'] | 92 case _Analysis.allocations: |
| 101 ..children = [new HeadingElement.h2()..text = 'Loading...'] | 93 final MemoryAllocationsElement allocations = |
| 102 ]; | 94 new MemoryAllocationsElement( |
| 103 } else { | 95 _isolate, _editor, _events, _allocations); |
| 104 children = [ | 96 current = allocations; |
| 105 new VirtualCollectionElement( | 97 reload = ({bool gc: false}) => allocations.reload(gc: gc); |
| 106 _createCollectionLine, _updateCollectionLine, | |
| 107 createHeader: _createCollectionHeader, | |
| 108 items: _profile.members.toList()..sort(_createSorter()), | |
| 109 queue: _r.queue) | |
| 110 ]; | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 _createSorter() { | |
| 115 var getter; | |
| 116 switch (_sortingField) { | |
| 117 case _SortingField.accumulatedSize: | |
| 118 getter = _getAccumulatedSize; | |
| 119 break; | 98 break; |
| 120 case _SortingField.accumulatedInstances: | 99 case _Analysis.dominatorTree: |
| 121 getter = _getAccumulatedInstances; | 100 final MemorySnapshotElement snapshot = |
| 122 break; | 101 new MemorySnapshotElement(_isolate, _editor, _snapshots, _objects); |
| 123 case _SortingField.currentSize: | 102 current = snapshot; |
| 124 getter = _getCurrentSize; | 103 reload = ({bool gc: false}) => snapshot.reload(gc: gc); |
| 125 break; | |
| 126 case _SortingField.currentInstances: | |
| 127 getter = _getCurrentInstances; | |
| 128 break; | |
| 129 case _SortingField.className: | |
| 130 getter = (M.ClassHeapStats s) => s.clazz.name; | |
| 131 break; | 104 break; |
| 132 } | 105 } |
| 133 switch (_sortingDirection) { | |
| 134 case _SortingDirection.ascending: | |
| 135 return (a, b) => getter(a).compareTo(getter(b)); | |
| 136 case _SortingDirection.descending: | |
| 137 return (a, b) => getter(b).compareTo(getter(a)); | |
| 138 } | |
| 139 } | |
| 140 | 106 |
| 141 static HtmlElement _createCollectionLine() => new DivElement() | 107 assert(current != null); |
| 142 ..classes = ['collection-item'] | |
| 143 ..children = [ | |
| 144 new SpanElement() | |
| 145 ..classes = ['bytes'] | |
| 146 ..text = '0B', | |
| 147 new SpanElement() | |
| 148 ..classes = ['instances'] | |
| 149 ..text = '0', | |
| 150 new SpanElement() | |
| 151 ..classes = ['bytes'] | |
| 152 ..text = '0B', | |
| 153 new SpanElement() | |
| 154 ..classes = ['instances'] | |
| 155 ..text = '0', | |
| 156 new SpanElement()..classes = ['name'] | |
| 157 ]; | |
| 158 | 108 |
| 159 List<HtmlElement> _createCollectionHeader() { | 109 final ButtonElement bReload = new ButtonElement(); |
| 160 final resetAccumulators = new ButtonElement(); | 110 final ButtonElement bGC = new ButtonElement(); |
| 161 return [ | 111 children = [ |
| 162 new DivElement() | 112 new DivElement() |
| 163 ..classes = ['collection-item'] | 113 ..classes = ['content-centered-big'] |
| 164 ..children = [ | 114 ..children = [ |
| 165 new SpanElement() | 115 new HeadingElement.h1() |
| 166 ..classes = ['group'] | |
| 167 ..children = [ | 116 ..children = [ |
| 168 new Text('Since Last '), | 117 new Text("Isolate ${_isolate.name}"), |
| 169 resetAccumulators | 118 bReload |
| 170 ..text = 'Reset↺' | 119 ..classes = ['link', 'big'] |
| 171 ..title = 'Reset' | 120 ..text = ' ↺ ' |
| 172 ..onClick.listen((_) async { | 121 ..title = 'Refresh' |
| 173 resetAccumulators.disabled = true; | 122 ..onClick.listen((e) async { |
| 174 await _refresh(reset: true); | 123 bReload.disabled = true; |
| 175 resetAccumulators.disabled = false; | 124 bGC.disabled = true; |
| 176 }) | 125 await reload(); |
| 126 bReload.disabled = false; | |
| 127 bGC.disabled = false; | |
| 128 }), | |
| 129 bGC | |
| 130 ..classes = ['link', 'big'] | |
| 131 ..text = ' ♺ ' | |
| 132 ..title = 'Collect Garbage' | |
| 133 ..onClick.listen((e) async { | |
| 134 bGC.disabled = true; | |
| 135 bReload.disabled = true; | |
| 136 await reload(gc: true); | |
| 137 bGC.disabled = false; | |
| 138 bReload.disabled = false; | |
| 139 }), | |
| 140 new ButtonElement() | |
| 141 ..classes = ['tab_button'] | |
| 142 ..text = 'Dominator Tree' | |
| 143 ..disabled = _analisys == _Analysis.dominatorTree | |
| 144 ..onClick.listen((_) { | |
| 145 _analisys = _Analysis.dominatorTree; | |
| 146 _r.dirty(); | |
| 147 }), | |
| 148 new ButtonElement() | |
| 149 ..classes = ['tab_button'] | |
| 150 ..text = 'Allocations' | |
| 151 ..disabled = _analisys == _Analysis.allocations | |
| 152 ..onClick.listen((_) { | |
| 153 _analisys = _Analysis.allocations; | |
| 154 _r.dirty(); | |
| 155 }), | |
| 177 ], | 156 ], |
| 178 new SpanElement() | |
| 179 ..classes = ['group'] | |
| 180 ..text = 'Current' | |
| 181 ], | 157 ], |
| 182 new DivElement() | 158 current |
| 183 ..classes = ['collection-item'] | |
| 184 ..children = [ | |
| 185 _createHeaderButton(const ['bytes'], 'Size', | |
| 186 _SortingField.accumulatedSize, _SortingDirection.descending), | |
| 187 _createHeaderButton(const ['instances'], 'Instances', | |
| 188 _SortingField.accumulatedInstances, _SortingDirection.descending), | |
| 189 _createHeaderButton(const ['bytes'], 'Size', | |
| 190 _SortingField.currentSize, _SortingDirection.descending), | |
| 191 _createHeaderButton(const ['instances'], 'Instances', | |
| 192 _SortingField.currentInstances, _SortingDirection.descending), | |
| 193 _createHeaderButton(const ['name'], 'Class', _SortingField.className, | |
| 194 _SortingDirection.ascending) | |
| 195 ], | |
| 196 ]; | 159 ]; |
| 197 } | 160 } |
| 198 | |
| 199 ButtonElement _createHeaderButton(List<String> classes, String text, | |
| 200 _SortingField field, _SortingDirection direction) => | |
| 201 new ButtonElement() | |
| 202 ..classes = classes | |
| 203 ..text = _sortingField != field | |
| 204 ? text | |
| 205 : _sortingDirection == _SortingDirection.ascending | |
| 206 ? '$text▼' | |
| 207 : '$text▲' | |
| 208 ..onClick.listen((_) => _setSorting(field, direction)); | |
| 209 | |
| 210 void _setSorting(_SortingField field, _SortingDirection defaultDirection) { | |
| 211 if (_sortingField == field) { | |
| 212 switch (_sortingDirection) { | |
| 213 case _SortingDirection.descending: | |
| 214 _sortingDirection = _SortingDirection.ascending; | |
| 215 break; | |
| 216 case _SortingDirection.ascending: | |
| 217 _sortingDirection = _SortingDirection.descending; | |
| 218 break; | |
| 219 } | |
| 220 } else { | |
| 221 _sortingDirection = defaultDirection; | |
| 222 _sortingField = field; | |
| 223 } | |
| 224 _r.dirty(); | |
| 225 } | |
| 226 | |
| 227 void _updateCollectionLine(Element e, M.ClassHeapStats item, index) { | |
| 228 e.children[0].text = Utils.formatSize(_getAccumulatedSize(item)); | |
| 229 e.children[1].text = '${_getAccumulatedInstances(item)}'; | |
| 230 e.children[2].text = Utils.formatSize(_getCurrentSize(item)); | |
| 231 e.children[3].text = '${_getCurrentInstances(item)}'; | |
| 232 e.children[4] = new ClassRefElement(_isolate, item.clazz, queue: _r.queue) | |
| 233 ..classes = ['name']; | |
| 234 Element.clickEvent.forTarget(e.children[4], useCapture: true).listen((e) { | |
| 235 if (_editor.canOpenClass) { | |
| 236 e.preventDefault(); | |
| 237 _editor.openClass(isolate, item.clazz); | |
| 238 } | |
| 239 }); | |
| 240 } | |
| 241 | |
| 242 Future _refresh({bool gc: false, bool reset: false}) async { | |
| 243 _profile = null; | |
| 244 _r.dirty(); | |
| 245 _profile = await _repository.get(_isolate, gc: gc, reset: reset); | |
| 246 _r.dirty(); | |
| 247 } | |
| 248 | |
| 249 static int _getAccumulatedSize(M.ClassHeapStats s) => | |
| 250 s.newSpace.accumulated.bytes + s.oldSpace.accumulated.bytes; | |
| 251 static int _getAccumulatedInstances(M.ClassHeapStats s) => | |
| 252 s.newSpace.accumulated.instances + s.oldSpace.accumulated.instances; | |
| 253 static int _getCurrentSize(M.ClassHeapStats s) => | |
| 254 s.newSpace.current.bytes + s.oldSpace.current.bytes; | |
| 255 static int _getCurrentInstances(M.ClassHeapStats s) => | |
| 256 s.newSpace.current.instances + s.oldSpace.current.instances; | |
| 257 } | 161 } |
| OLD | NEW |