| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 5 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 6 // for details. All rights reserved. Use of this source code is governed by a | 6 // for details. All rights reserved. Use of this source code is governed by a |
| 7 // BSD-style license that can be found in the LICENSE file. | 7 // BSD-style license that can be found in the LICENSE file. |
| 8 | 8 |
| 9 import 'dart:async'; | 9 import 'dart:async'; |
| 10 import 'dart:html'; | 10 import 'dart:html'; |
| 11 import 'dart:math' as Math; | 11 import 'dart:math' as Math; |
| 12 import 'package:observatory/models.dart' as M; | 12 import 'package:observatory/models.dart' as M; |
| 13 import 'package:observatory/src/elements/class_ref.dart'; | 13 import 'package:observatory/src/elements/class_ref.dart'; |
| 14 import 'package:observatory/src/elements/containers/virtual_tree.dart'; | 14 import 'package:observatory/src/elements/containers/virtual_tree.dart'; |
| 15 import 'package:observatory/src/elements/helpers/any_ref.dart'; | 15 import 'package:observatory/src/elements/helpers/any_ref.dart'; |
| 16 import 'package:observatory/src/elements/helpers/nav_bar.dart'; | 16 import 'package:observatory/src/elements/helpers/nav_bar.dart'; |
| 17 import 'package:observatory/src/elements/helpers/nav_menu.dart'; | 17 import 'package:observatory/src/elements/helpers/nav_menu.dart'; |
| 18 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; | 18 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| 19 import 'package:observatory/src/elements/helpers/tag.dart'; | 19 import 'package:observatory/src/elements/helpers/tag.dart'; |
| 20 import 'package:observatory/src/elements/nav/isolate_menu.dart'; | 20 import 'package:observatory/src/elements/nav/isolate_menu.dart'; |
| 21 import 'package:observatory/src/elements/nav/notify.dart'; | 21 import 'package:observatory/src/elements/nav/notify.dart'; |
| 22 import 'package:observatory/src/elements/nav/refresh.dart'; | 22 import 'package:observatory/src/elements/nav/refresh.dart'; |
| 23 import 'package:observatory/src/elements/nav/top_menu.dart'; | 23 import 'package:observatory/src/elements/nav/top_menu.dart'; |
| 24 import 'package:observatory/src/elements/nav/vm_menu.dart'; | 24 import 'package:observatory/src/elements/nav/vm_menu.dart'; |
| 25 import 'package:observatory/utils.dart'; | 25 import 'package:observatory/utils.dart'; |
| 26 | 26 |
| 27 enum HeapSnapshotTreeMode { | 27 enum HeapSnapshotTreeMode { dominatorTree, groupByClass } |
| 28 dominatorTree, | |
| 29 groupByClass | |
| 30 } | |
| 31 | 28 |
| 32 class HeapSnapshotElement extends HtmlElement implements Renderable { | 29 class HeapSnapshotElement extends HtmlElement implements Renderable { |
| 33 static const tag = const Tag<HeapSnapshotElement>('heap-snapshot', | 30 static const tag = |
| 34 dependencies: const [ | 31 const Tag<HeapSnapshotElement>('heap-snapshot', dependencies: const [ |
| 35 ClassRefElement.tag, | 32 ClassRefElement.tag, |
| 36 NavTopMenuElement.tag, | 33 NavTopMenuElement.tag, |
| 37 NavVMMenuElement.tag, | 34 NavVMMenuElement.tag, |
| 38 NavIsolateMenuElement.tag, | 35 NavIsolateMenuElement.tag, |
| 39 NavRefreshElement.tag, | 36 NavRefreshElement.tag, |
| 40 NavNotifyElement.tag, | 37 NavNotifyElement.tag, |
| 41 VirtualTreeElement.tag, | 38 VirtualTreeElement.tag, |
| 42 ]); | 39 ]); |
| 43 | 40 |
| 44 RenderingScheduler<HeapSnapshotElement> _r; | 41 RenderingScheduler<HeapSnapshotElement> _r; |
| 45 | 42 |
| 46 Stream<RenderedEvent<HeapSnapshotElement>> get onRendered => _r.onRendered; | 43 Stream<RenderedEvent<HeapSnapshotElement>> get onRendered => _r.onRendered; |
| 47 | 44 |
| 48 M.VM _vm; | 45 M.VM _vm; |
| 49 M.IsolateRef _isolate; | 46 M.IsolateRef _isolate; |
| 50 M.EventRepository _events; | 47 M.EventRepository _events; |
| 51 M.NotificationRepository _notifications; | 48 M.NotificationRepository _notifications; |
| 52 M.HeapSnapshotRepository _snapshots; | 49 M.HeapSnapshotRepository _snapshots; |
| 53 M.InstanceRepository _instances; | 50 M.InstanceRepository _instances; |
| 54 M.HeapSnapshot _snapshot; | 51 M.HeapSnapshot _snapshot; |
| 55 Stream<M.HeapSnapshotLoadingProgressEvent> _progressStream; | 52 Stream<M.HeapSnapshotLoadingProgressEvent> _progressStream; |
| 56 M.HeapSnapshotLoadingProgress _progress; | 53 M.HeapSnapshotLoadingProgress _progress; |
| 57 HeapSnapshotTreeMode _mode = HeapSnapshotTreeMode.dominatorTree; | 54 HeapSnapshotTreeMode _mode = HeapSnapshotTreeMode.dominatorTree; |
| 58 | 55 |
| 59 | |
| 60 M.IsolateRef get isolate => _isolate; | 56 M.IsolateRef get isolate => _isolate; |
| 61 M.NotificationRepository get notifications => _notifications; | 57 M.NotificationRepository get notifications => _notifications; |
| 62 M.HeapSnapshotRepository get profiles => _snapshots; | 58 M.HeapSnapshotRepository get profiles => _snapshots; |
| 63 M.VMRef get vm => _vm; | 59 M.VMRef get vm => _vm; |
| 64 | 60 |
| 65 factory HeapSnapshotElement(M.VM vm, M.IsolateRef isolate, | 61 factory HeapSnapshotElement( |
| 66 M.EventRepository events, | 62 M.VM vm, |
| 67 M.NotificationRepository notifications, | 63 M.IsolateRef isolate, |
| 68 M.HeapSnapshotRepository snapshots, | 64 M.EventRepository events, |
| 69 M.InstanceRepository instances, | 65 M.NotificationRepository notifications, |
| 70 {RenderingQueue queue}) { | 66 M.HeapSnapshotRepository snapshots, |
| 67 M.InstanceRepository instances, |
| 68 {RenderingQueue queue}) { |
| 71 assert(vm != null); | 69 assert(vm != null); |
| 72 assert(isolate != null); | 70 assert(isolate != null); |
| 73 assert(events != null); | 71 assert(events != null); |
| 74 assert(notifications != null); | 72 assert(notifications != null); |
| 75 assert(snapshots != null); | 73 assert(snapshots != null); |
| 76 assert(instances != null); | 74 assert(instances != null); |
| 77 HeapSnapshotElement e = document.createElement(tag.name); | 75 HeapSnapshotElement e = document.createElement(tag.name); |
| 78 e._r = new RenderingScheduler(e, queue: queue); | 76 e._r = new RenderingScheduler(e, queue: queue); |
| 79 e._vm = vm; | 77 e._vm = vm; |
| 80 e._isolate = isolate; | 78 e._isolate = isolate; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 102 } | 100 } |
| 103 | 101 |
| 104 void render() { | 102 void render() { |
| 105 final content = [ | 103 final content = [ |
| 106 navBar([ | 104 navBar([ |
| 107 new NavTopMenuElement(queue: _r.queue), | 105 new NavTopMenuElement(queue: _r.queue), |
| 108 new NavVMMenuElement(_vm, _events, queue: _r.queue), | 106 new NavVMMenuElement(_vm, _events, queue: _r.queue), |
| 109 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), | 107 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), |
| 110 navMenu('heap snapshot'), | 108 navMenu('heap snapshot'), |
| 111 new NavRefreshElement(queue: _r.queue) | 109 new NavRefreshElement(queue: _r.queue) |
| 112 ..disabled = M.isHeapSnapshotProgressRunning(_progress?.status) | 110 ..disabled = M.isHeapSnapshotProgressRunning(_progress?.status) |
| 113 ..onRefresh.listen((e) { | 111 ..onRefresh.listen((e) { |
| 114 _refresh(); | 112 _refresh(); |
| 115 }), | 113 }), |
| 116 new NavNotifyElement(_notifications, queue: _r.queue) | 114 new NavNotifyElement(_notifications, queue: _r.queue) |
| 117 ]), | 115 ]), |
| 118 ]; | 116 ]; |
| 119 if (_progress == null) { | 117 if (_progress == null) { |
| 120 children = content; | 118 children = content; |
| 121 return; | 119 return; |
| 122 } | 120 } |
| 123 switch (_progress.status) { | 121 switch (_progress.status) { |
| 124 case M.HeapSnapshotLoadingStatus.fetching : | 122 case M.HeapSnapshotLoadingStatus.fetching: |
| 125 content.addAll(_createStatusMessage('Fetching snapshot from VM...', | 123 content.addAll(_createStatusMessage('Fetching snapshot from VM...', |
| 126 description: _progress.stepDescription, | 124 description: _progress.stepDescription, |
| 127 progress: _progress.progress)); | 125 progress: _progress.progress)); |
| 128 break; | 126 break; |
| 129 case M.HeapSnapshotLoadingStatus.loading : | 127 case M.HeapSnapshotLoadingStatus.loading: |
| 130 content.addAll(_createStatusMessage('Loading snapshot...', | 128 content.addAll(_createStatusMessage('Loading snapshot...', |
| 131 description: _progress.stepDescription, | 129 description: _progress.stepDescription, |
| 132 progress: _progress.progress)); | 130 progress: _progress.progress)); |
| 133 break; | 131 break; |
| 134 case M.HeapSnapshotLoadingStatus.loaded: | 132 case M.HeapSnapshotLoadingStatus.loaded: |
| 135 content.addAll(_createReport()); | 133 content.addAll(_createReport()); |
| 136 break; | 134 break; |
| 137 } | 135 } |
| 138 children = content; | 136 children = content; |
| 139 } | 137 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 151 if (M.isHeapSnapshotProgressRunning(_progress.status)) { | 149 if (M.isHeapSnapshotProgressRunning(_progress.status)) { |
| 152 _progress = (await _progressStream.last).progress; | 150 _progress = (await _progressStream.last).progress; |
| 153 _snapshot = _progress.snapshot; | 151 _snapshot = _progress.snapshot; |
| 154 _r.dirty(); | 152 _r.dirty(); |
| 155 } | 153 } |
| 156 } | 154 } |
| 157 | 155 |
| 158 static List<Element> _createStatusMessage(String message, | 156 static List<Element> _createStatusMessage(String message, |
| 159 {String description: '', double progress: 0.0}) { | 157 {String description: '', double progress: 0.0}) { |
| 160 return [ | 158 return [ |
| 161 new DivElement()..classes = ['content-centered-big'] | 159 new DivElement() |
| 160 ..classes = ['content-centered-big'] |
| 162 ..children = [ | 161 ..children = [ |
| 163 new DivElement()..classes = ['statusBox', 'shadow', 'center'] | 162 new DivElement() |
| 163 ..classes = ['statusBox', 'shadow', 'center'] |
| 164 ..children = [ | 164 ..children = [ |
| 165 new DivElement()..classes = ['statusMessage'] | 165 new DivElement() |
| 166 ..classes = ['statusMessage'] |
| 166 ..text = message, | 167 ..text = message, |
| 167 new DivElement()..classes = ['statusDescription'] | 168 new DivElement() |
| 169 ..classes = ['statusDescription'] |
| 168 ..text = description, | 170 ..text = description, |
| 169 new DivElement()..style.background = '#0489c3' | 171 new DivElement() |
| 172 ..style.background = '#0489c3' |
| 170 ..style.width = '$progress%' | 173 ..style.width = '$progress%' |
| 171 ..style.height = '15px' | 174 ..style.height = '15px' |
| 172 ..style.borderRadius = '4px' | 175 ..style.borderRadius = '4px' |
| 173 ] | 176 ] |
| 174 ] | 177 ] |
| 175 ]; | 178 ]; |
| 176 } | 179 } |
| 177 | 180 |
| 178 VirtualTreeElement _tree; | 181 VirtualTreeElement _tree; |
| 179 | 182 |
| 180 List<Element> _createReport() { | 183 List<Element> _createReport() { |
| 181 var report = [ | 184 var report = [ |
| 182 new DivElement()..classes = ['content-centered-big'] | 185 new DivElement() |
| 186 ..classes = ['content-centered-big'] |
| 183 ..children = [ | 187 ..children = [ |
| 184 new DivElement()..classes = ['memberList'] | 188 new DivElement() |
| 189 ..classes = ['memberList'] |
| 185 ..children = [ | 190 ..children = [ |
| 186 new DivElement()..classes = ['memberItem'] | 191 new DivElement() |
| 192 ..classes = ['memberItem'] |
| 187 ..children = [ | 193 ..children = [ |
| 188 new DivElement()..classes = ['memberName'] | 194 new DivElement() |
| 195 ..classes = ['memberName'] |
| 189 ..text = 'Refreshed ', | 196 ..text = 'Refreshed ', |
| 190 new DivElement()..classes = ['memberName'] | 197 new DivElement() |
| 198 ..classes = ['memberName'] |
| 191 ..text = Utils.formatDateTime(_snapshot.timestamp) | 199 ..text = Utils.formatDateTime(_snapshot.timestamp) |
| 192 ], | 200 ], |
| 193 new DivElement()..classes = ['memberItem'] | 201 new DivElement() |
| 202 ..classes = ['memberItem'] |
| 194 ..children = [ | 203 ..children = [ |
| 195 new DivElement()..classes = ['memberName'] | 204 new DivElement() |
| 205 ..classes = ['memberName'] |
| 196 ..text = 'Objects ', | 206 ..text = 'Objects ', |
| 197 new DivElement()..classes = ['memberName'] | 207 new DivElement() |
| 208 ..classes = ['memberName'] |
| 198 ..text = '${_snapshot.objects}' | 209 ..text = '${_snapshot.objects}' |
| 199 ], | 210 ], |
| 200 new DivElement()..classes = ['memberItem'] | 211 new DivElement() |
| 212 ..classes = ['memberItem'] |
| 201 ..children = [ | 213 ..children = [ |
| 202 new DivElement()..classes = ['memberName'] | 214 new DivElement() |
| 215 ..classes = ['memberName'] |
| 203 ..text = 'References ', | 216 ..text = 'References ', |
| 204 new DivElement()..classes = ['memberName'] | 217 new DivElement() |
| 218 ..classes = ['memberName'] |
| 205 ..text = '${_snapshot.references}' | 219 ..text = '${_snapshot.references}' |
| 206 ], | 220 ], |
| 207 new DivElement()..classes = ['memberItem'] | 221 new DivElement() |
| 222 ..classes = ['memberItem'] |
| 208 ..children = [ | 223 ..children = [ |
| 209 new DivElement()..classes = ['memberName'] | 224 new DivElement() |
| 225 ..classes = ['memberName'] |
| 210 ..text = 'Size ', | 226 ..text = 'Size ', |
| 211 new DivElement()..classes = ['memberName'] | 227 new DivElement() |
| 228 ..classes = ['memberName'] |
| 212 ..text = Utils.formatSize(_snapshot.size) | 229 ..text = Utils.formatSize(_snapshot.size) |
| 213 ], | 230 ], |
| 214 new DivElement()..classes = ['memberItem'] | 231 new DivElement() |
| 232 ..classes = ['memberItem'] |
| 215 ..children = [ | 233 ..children = [ |
| 216 new DivElement()..classes = ['memberName'] | 234 new DivElement() |
| 235 ..classes = ['memberName'] |
| 217 ..text = 'Analysis ', | 236 ..text = 'Analysis ', |
| 218 new DivElement()..classes = ['memberName'] | 237 new DivElement() |
| 238 ..classes = ['memberName'] |
| 219 ..children = _createModeSelect() | 239 ..children = _createModeSelect() |
| 220 ] | 240 ] |
| 221 ] | 241 ] |
| 222 ], | 242 ], |
| 223 ]; | 243 ]; |
| 224 switch (_mode) { | 244 switch (_mode) { |
| 225 case HeapSnapshotTreeMode.dominatorTree: | 245 case HeapSnapshotTreeMode.dominatorTree: |
| 226 _tree = new VirtualTreeElement(_createDominator, _updateDominator, | 246 _tree = new VirtualTreeElement( |
| 227 _getChildrenDominator, | 247 _createDominator, _updateDominator, _getChildrenDominator, |
| 228 items: _getChildrenDominator(_snapshot.dominatorTree), | 248 items: _getChildrenDominator(_snapshot.dominatorTree), |
| 229 queue: _r.queue); | 249 queue: _r.queue); |
| 230 _tree.expand(_snapshot.dominatorTree); | 250 _tree.expand(_snapshot.dominatorTree); |
| 231 final text = 'In a heap dominator tree, an object X is a parent of ' | 251 final text = 'In a heap dominator tree, an object X is a parent of ' |
| 232 'object Y if every path from the root to Y goes through ' | 252 'object Y if every path from the root to Y goes through ' |
| 233 'X. This allows you to find "choke points" that are ' | 253 'X. This allows you to find "choke points" that are ' |
| 234 'holding onto a lot of memory. If an object becomes ' | 254 'holding onto a lot of memory. If an object becomes ' |
| 235 'garbage, all its children in the dominator tree become ' | 255 'garbage, all its children in the dominator tree become ' |
| 236 'garbage as well. ' | 256 'garbage as well. ' |
| 237 'The retained size of an object is the sum of the ' | 257 'The retained size of an object is the sum of the ' |
| 238 'retained sizes of its children in the dominator tree ' | 258 'retained sizes of its children in the dominator tree ' |
| 239 'plus its own shallow size, and is the amount of memory ' | 259 'plus its own shallow size, and is the amount of memory ' |
| 240 'that would be freed if the object became garbage.'; | 260 'that would be freed if the object became garbage.'; |
| 241 report.addAll([ | 261 report.addAll([ |
| 242 new DivElement()..classes = ['content-centered-big', 'explanation'] | 262 new DivElement() |
| 263 ..classes = ['content-centered-big', 'explanation'] |
| 243 ..text = text | 264 ..text = text |
| 244 ..title = text, | 265 ..title = text, |
| 245 _tree | 266 _tree |
| 246 ]); | 267 ]); |
| 247 break; | 268 break; |
| 248 case HeapSnapshotTreeMode.groupByClass: | 269 case HeapSnapshotTreeMode.groupByClass: |
| 249 final items = _snapshot.classReferences.toList(); | 270 final items = _snapshot.classReferences.toList(); |
| 250 items.sort((a, b) => b.shallowSize - a.shallowSize); | 271 items.sort((a, b) => b.shallowSize - a.shallowSize); |
| 251 _tree = new VirtualTreeElement(_createGroup, _updateGroup, | 272 _tree = new VirtualTreeElement( |
| 252 _getChildrenGroup, items: items, queue: _r.queue); | 273 _createGroup, _updateGroup, _getChildrenGroup, |
| 274 items: items, queue: _r.queue); |
| 253 _tree.expand(_snapshot.dominatorTree); | 275 _tree.expand(_snapshot.dominatorTree); |
| 254 report.add(_tree); | 276 report.add(_tree); |
| 255 break; | 277 break; |
| 256 default: | 278 default: |
| 257 break; | 279 break; |
| 258 } | 280 } |
| 259 return report; | 281 return report; |
| 260 } | 282 } |
| 261 | 283 |
| 262 static Element _createDominator(toggle) { | 284 static Element _createDominator(toggle) { |
| 263 return new DivElement() | 285 return new DivElement() |
| 264 ..classes = ['tree-item'] | 286 ..classes = ['tree-item'] |
| 265 ..children = [ | 287 ..children = [ |
| 266 new SpanElement()..classes = ['size'] | 288 new SpanElement() |
| 289 ..classes = ['size'] |
| 267 ..title = 'retained size', | 290 ..title = 'retained size', |
| 268 new SpanElement()..classes = ['lines'], | 291 new SpanElement()..classes = ['lines'], |
| 269 new ButtonElement()..classes = ['expander'] | 292 new ButtonElement() |
| 293 ..classes = ['expander'] |
| 270 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), | 294 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), |
| 271 new SpanElement()..classes = ['percentage'] | 295 new SpanElement() |
| 296 ..classes = ['percentage'] |
| 272 ..title = 'percentage of heap being retained', | 297 ..title = 'percentage of heap being retained', |
| 273 new SpanElement()..classes = ['name'] | 298 new SpanElement()..classes = ['name'] |
| 274 ]; | 299 ]; |
| 275 } | 300 } |
| 276 | 301 |
| 277 static Element _createGroup(toggle) { | 302 static Element _createGroup(toggle) { |
| 278 return new DivElement() | 303 return new DivElement() |
| 279 ..classes = ['tree-item'] | 304 ..classes = ['tree-item'] |
| 280 ..children = [ | 305 ..children = [ |
| 281 new SpanElement()..classes = ['size'] | 306 new SpanElement() |
| 307 ..classes = ['size'] |
| 282 ..title = 'shallow size', | 308 ..title = 'shallow size', |
| 283 new SpanElement()..classes = ['lines'], | 309 new SpanElement()..classes = ['lines'], |
| 284 new ButtonElement()..classes = ['expander'] | 310 new ButtonElement() |
| 311 ..classes = ['expander'] |
| 285 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), | 312 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), |
| 286 new SpanElement()..classes = ['count'] | 313 new SpanElement() |
| 314 ..classes = ['count'] |
| 287 ..title = 'shallow size', | 315 ..title = 'shallow size', |
| 288 new SpanElement()..classes = ['name'] | 316 new SpanElement()..classes = ['name'] |
| 289 ]; | 317 ]; |
| 290 } | 318 } |
| 291 | 319 |
| 292 static const int kMaxChildren = 100; | 320 static const int kMaxChildren = 100; |
| 293 static const int kMinRetainedSize = 4096; | 321 static const int kMinRetainedSize = 4096; |
| 294 | 322 |
| 295 static _getChildrenDominator(M.HeapSnapshotDominatorNode node) { | 323 static _getChildrenDominator(M.HeapSnapshotDominatorNode node) { |
| 296 final list = node.children.toList(); | 324 final list = node.children.toList(); |
| 297 list.sort((a, b) => b.retainedSize - a.retainedSize); | 325 list.sort((a, b) => b.retainedSize - a.retainedSize); |
| 298 return list.where((child) => child.retainedSize >= kMinRetainedSize) | 326 return list |
| 299 .take(kMaxChildren); | 327 .where((child) => child.retainedSize >= kMinRetainedSize) |
| 328 .take(kMaxChildren); |
| 300 } | 329 } |
| 301 | 330 |
| 302 static _getChildrenGroup(item) { | 331 static _getChildrenGroup(item) { |
| 303 if (item is M.HeapSnapshotClassReferences) { | 332 if (item is M.HeapSnapshotClassReferences) { |
| 304 if (item.inbounds.isNotEmpty || item.outbounds.isNotEmpty) { | 333 if (item.inbounds.isNotEmpty || item.outbounds.isNotEmpty) { |
| 305 return [item.inbounds, item.outbounds]; | 334 return [item.inbounds, item.outbounds]; |
| 306 } | 335 } |
| 307 } else if (item is Iterable) { | 336 } else if (item is Iterable) { |
| 308 return item.toList()..sort((a, b) => b.shallowSize - a.shallowSize); | 337 return item.toList()..sort((a, b) => b.shallowSize - a.shallowSize); |
| 309 } | 338 } |
| 310 return const []; | 339 return const []; |
| 311 } | 340 } |
| 312 | 341 |
| 313 void _updateDominator(HtmlElement element, M.HeapSnapshotDominatorNode node, | 342 void _updateDominator( |
| 314 int depth) { | 343 HtmlElement element, M.HeapSnapshotDominatorNode node, int depth) { |
| 315 element.children[0].text = Utils.formatSize(node.retainedSize); | 344 element.children[0].text = Utils.formatSize(node.retainedSize); |
| 316 _updateLines(element.children[1].children, depth); | 345 _updateLines(element.children[1].children, depth); |
| 317 if (_getChildrenDominator(node).isNotEmpty) { | 346 if (_getChildrenDominator(node).isNotEmpty) { |
| 318 element.children[2].text = _tree.isExpanded(node) ? '▼' : '►'; | 347 element.children[2].text = _tree.isExpanded(node) ? '▼' : '►'; |
| 319 } else { | 348 } else { |
| 320 element.children[2].text = ''; | 349 element.children[2].text = ''; |
| 321 } | 350 } |
| 322 element.children[3].text = Utils.formatPercentNormalized( | 351 element.children[3].text = |
| 323 node.retainedSize * 1.0 / _snapshot.size); | 352 Utils.formatPercentNormalized(node.retainedSize * 1.0 / _snapshot.size); |
| 324 final wrapper = new SpanElement()..classes = ['name'] | 353 final wrapper = new SpanElement() |
| 325 ..text = 'Loading...'; | 354 ..classes = ['name'] |
| 355 ..text = 'Loading...'; |
| 326 element.children[4] = wrapper; | 356 element.children[4] = wrapper; |
| 327 node.object.then((object) { | 357 node.object.then((object) { |
| 328 wrapper..text = '' | 358 wrapper |
| 359 ..text = '' |
| 329 ..children = [anyRef(_isolate, object, _instances, queue: _r.queue)]; | 360 ..children = [anyRef(_isolate, object, _instances, queue: _r.queue)]; |
| 330 }); | 361 }); |
| 331 } | 362 } |
| 332 | 363 |
| 333 void _updateGroup(HtmlElement element, item, int depth) { | 364 void _updateGroup(HtmlElement element, item, int depth) { |
| 334 _updateLines(element.children[1].children, depth); | 365 _updateLines(element.children[1].children, depth); |
| 335 if (item is M.HeapSnapshotClassReferences) { | 366 if (item is M.HeapSnapshotClassReferences) { |
| 336 element.children[0].text = Utils.formatSize(item.shallowSize); | 367 element.children[0].text = Utils.formatSize(item.shallowSize); |
| 337 element.children[2].text = _tree.isExpanded(item) ? '▼' : '►'; | 368 element.children[2].text = _tree.isExpanded(item) ? '▼' : '►'; |
| 338 element.children[3].text = '${item.instances} instances of '; | 369 element.children[3].text = '${item.instances} instances of '; |
| 339 element.children[4] = new ClassRefElement(_isolate, item.clazz, | 370 element.children[4] = new ClassRefElement(_isolate, item.clazz, |
| 340 queue: _r.queue)..classes = ['name']; | 371 queue: _r.queue)..classes = ['name']; |
| 341 } else if (item is Iterable) { | 372 } else if (item is Iterable) { |
| 342 element.children[0].text = ''; | 373 element.children[0].text = ''; |
| 343 if (item.isNotEmpty) { | 374 if (item.isNotEmpty) { |
| 344 element.children[2].text = _tree.isExpanded(item) ? '▼' : '►'; | 375 element.children[2].text = _tree.isExpanded(item) ? '▼' : '►'; |
| 345 } else { | 376 } else { |
| 346 element.children[2].text = ''; | 377 element.children[2].text = ''; |
| 347 } | 378 } |
| 348 element.children[3].text = ''; | 379 element.children[3].text = ''; |
| 349 int references = 0; | 380 int references = 0; |
| 350 for (var referenceGroup in item) { | 381 for (var referenceGroup in item) { |
| 351 references += referenceGroup.count; | 382 references += referenceGroup.count; |
| 352 } | 383 } |
| 353 if (item is Iterable<M.HeapSnapshotClassInbound>) { | 384 if (item is Iterable<M.HeapSnapshotClassInbound>) { |
| 354 element.children[4] = new SpanElement()..classes = ['name'] | 385 element.children[4] = new SpanElement() |
| 355 ..text = '$references incoming references'; | 386 ..classes = ['name'] |
| 387 ..text = '$references incoming references'; |
| 356 } else { | 388 } else { |
| 357 element.children[4] = new SpanElement()..classes = ['name'] | 389 element.children[4] = new SpanElement() |
| 358 ..text = '$references outgoing references'; | 390 ..classes = ['name'] |
| 391 ..text = '$references outgoing references'; |
| 359 } | 392 } |
| 360 } else { | 393 } else { |
| 361 element.children[0].text = ''; | 394 element.children[0].text = ''; |
| 362 element.children[2].text = ''; | 395 element.children[2].text = ''; |
| 363 element.children[3].text = ''; | 396 element.children[3].text = ''; |
| 364 element.children[4] = new SpanElement()..classes = ['name']; | 397 element.children[4] = new SpanElement()..classes = ['name']; |
| 365 if (item is M.HeapSnapshotClassInbound){ | 398 if (item is M.HeapSnapshotClassInbound) { |
| 366 element.children[3].text = | 399 element.children[3].text = |
| 367 '${item.count} references from instances of '; | 400 '${item.count} references from instances of '; |
| 368 element.children[4].children = [ | 401 element.children[4].children = [ |
| 369 new ClassRefElement(_isolate, item.source, | 402 new ClassRefElement(_isolate, item.source, queue: _r.queue) |
| 370 queue: _r.queue) | |
| 371 ]; | 403 ]; |
| 372 } else if (item is M.HeapSnapshotClassOutbound){ | 404 } else if (item is M.HeapSnapshotClassOutbound) { |
| 373 element.children[3]..text = '${item.count} references to instances of '; | 405 element.children[3]..text = '${item.count} references to instances of '; |
| 374 element.children[4].children = [ | 406 element.children[4].children = [ |
| 375 new ClassRefElement(_isolate, item.target, | 407 new ClassRefElement(_isolate, item.target, queue: _r.queue) |
| 376 queue: _r.queue) | |
| 377 ]; | 408 ]; |
| 378 } | 409 } |
| 379 } | 410 } |
| 380 } | 411 } |
| 381 | 412 |
| 382 static _updateLines(List<Element> lines, int n) { | 413 static _updateLines(List<Element> lines, int n) { |
| 383 n = Math.max(0, n); | 414 n = Math.max(0, n); |
| 384 while (lines.length > n) { | 415 while (lines.length > n) { |
| 385 lines.removeLast(); | 416 lines.removeLast(); |
| 386 } | 417 } |
| 387 while (lines.length < n) { | 418 while (lines.length < n) { |
| 388 lines.add(new SpanElement()); | 419 lines.add(new SpanElement()); |
| 389 } | 420 } |
| 390 } | 421 } |
| 391 | 422 |
| 392 static String modeToString(HeapSnapshotTreeMode mode) { | 423 static String modeToString(HeapSnapshotTreeMode mode) { |
| 393 switch (mode) { | 424 switch (mode) { |
| 394 case HeapSnapshotTreeMode.dominatorTree: return 'Dominator tree'; | 425 case HeapSnapshotTreeMode.dominatorTree: |
| 395 case HeapSnapshotTreeMode.groupByClass: return 'Group by class'; | 426 return 'Dominator tree'; |
| 427 case HeapSnapshotTreeMode.groupByClass: |
| 428 return 'Group by class'; |
| 396 } | 429 } |
| 397 throw new Exception('Unknown ProfileTreeMode'); | 430 throw new Exception('Unknown ProfileTreeMode'); |
| 398 } | 431 } |
| 399 | 432 |
| 400 List<Element> _createModeSelect() { | 433 List<Element> _createModeSelect() { |
| 401 var s; | 434 var s; |
| 402 return [ | 435 return [ |
| 403 s = new SelectElement()..classes = ['analysis-select'] | 436 s = new SelectElement() |
| 437 ..classes = ['analysis-select'] |
| 404 ..value = modeToString(_mode) | 438 ..value = modeToString(_mode) |
| 405 ..children = HeapSnapshotTreeMode.values.map((mode) { | 439 ..children = HeapSnapshotTreeMode.values.map((mode) { |
| 406 return new OptionElement(value: modeToString(mode), | 440 return new OptionElement( |
| 407 selected: _mode == mode) | 441 value: modeToString(mode), |
| 408 ..text = modeToString(mode); | 442 selected: _mode == mode)..text = modeToString(mode); |
| 409 }).toList(growable: false) | 443 }).toList(growable: false) |
| 410 ..onChange.listen((_) { | 444 ..onChange.listen((_) { |
| 411 _mode = HeapSnapshotTreeMode.values[s.selectedIndex]; | 445 _mode = HeapSnapshotTreeMode.values[s.selectedIndex]; |
| 412 _r.dirty(); | 446 _r.dirty(); |
| 413 }) | 447 }) |
| 414 ]; | 448 ]; |
| 415 } | 449 } |
| 416 } | 450 } |
| OLD | NEW |