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 |