| 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/helpers/uris.dart'; | 20 import 'package:observatory/src/elements/helpers/uris.dart'; |
| 21 import 'package:observatory/src/elements/nav/isolate_menu.dart'; | 21 import 'package:observatory/src/elements/nav/isolate_menu.dart'; |
| 22 import 'package:observatory/src/elements/nav/notify.dart'; | 22 import 'package:observatory/src/elements/nav/notify.dart'; |
| 23 import 'package:observatory/src/elements/nav/refresh.dart'; | 23 import 'package:observatory/src/elements/nav/refresh.dart'; |
| 24 import 'package:observatory/src/elements/nav/top_menu.dart'; | 24 import 'package:observatory/src/elements/nav/top_menu.dart'; |
| 25 import 'package:observatory/src/elements/nav/vm_menu.dart'; | 25 import 'package:observatory/src/elements/nav/vm_menu.dart'; |
| 26 import 'package:observatory/utils.dart'; | 26 import 'package:observatory/utils.dart'; |
| 27 | 27 |
| 28 enum HeapSnapshotTreeMode { dominatorTree, mergedDominatorTree, groupByClass } | 28 enum HeapSnapshotTreeMode { |
| 29 dominatorTree, |
| 30 mergedDominatorTree, |
| 31 ownershipTable, |
| 32 groupByClass |
| 33 } |
| 29 | 34 |
| 30 class HeapSnapshotElement extends HtmlElement implements Renderable { | 35 class HeapSnapshotElement extends HtmlElement implements Renderable { |
| 31 static const tag = | 36 static const tag = |
| 32 const Tag<HeapSnapshotElement>('heap-snapshot', dependencies: const [ | 37 const Tag<HeapSnapshotElement>('heap-snapshot', dependencies: const [ |
| 33 ClassRefElement.tag, | 38 ClassRefElement.tag, |
| 34 NavTopMenuElement.tag, | 39 NavTopMenuElement.tag, |
| 35 NavVMMenuElement.tag, | 40 NavVMMenuElement.tag, |
| 36 NavIsolateMenuElement.tag, | 41 NavIsolateMenuElement.tag, |
| 37 NavRefreshElement.tag, | 42 NavRefreshElement.tag, |
| 38 NavNotifyElement.tag, | 43 NavNotifyElement.tag, |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 287 final text = 'A heap dominator tree, where siblings with the same class' | 292 final text = 'A heap dominator tree, where siblings with the same class' |
| 288 ' have been merged into a single node.'; | 293 ' have been merged into a single node.'; |
| 289 report.addAll([ | 294 report.addAll([ |
| 290 new DivElement() | 295 new DivElement() |
| 291 ..classes = ['content-centered-big', 'explanation'] | 296 ..classes = ['content-centered-big', 'explanation'] |
| 292 ..text = text | 297 ..text = text |
| 293 ..title = text, | 298 ..title = text, |
| 294 _tree | 299 _tree |
| 295 ]); | 300 ]); |
| 296 break; | 301 break; |
| 302 case HeapSnapshotTreeMode.ownershipTable: |
| 303 final items = _snapshot.ownershipClasses.toList(); |
| 304 items.sort((a, b) => b.size - a.size); |
| 305 _tree = new VirtualTreeElement(_createOwnershipClass, |
| 306 _updateOwnershipClass, _getChildrenOwnershipClass, |
| 307 items: items, queue: _r.queue); |
| 308 _tree.expand(_snapshot.dominatorTree); |
| 309 report.add(_tree); |
| 310 break; |
| 297 case HeapSnapshotTreeMode.groupByClass: | 311 case HeapSnapshotTreeMode.groupByClass: |
| 298 final items = _snapshot.classReferences.toList(); | 312 final items = _snapshot.classReferences.toList(); |
| 299 items.sort((a, b) => b.shallowSize - a.shallowSize); | 313 items.sort((a, b) => b.shallowSize - a.shallowSize); |
| 300 _tree = new VirtualTreeElement( | 314 _tree = new VirtualTreeElement( |
| 301 _createGroup, _updateGroup, _getChildrenGroup, | 315 _createGroup, _updateGroup, _getChildrenGroup, |
| 302 items: items, queue: _r.queue); | 316 items: items, queue: _r.queue); |
| 303 _tree.expand(_snapshot.dominatorTree); | 317 _tree.expand(_snapshot.dominatorTree); |
| 304 report.add(_tree); | 318 report.add(_tree); |
| 305 break; | 319 break; |
| 306 default: | 320 default: |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 356 new ButtonElement() | 370 new ButtonElement() |
| 357 ..classes = ['expander'] | 371 ..classes = ['expander'] |
| 358 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), | 372 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), |
| 359 new SpanElement() | 373 new SpanElement() |
| 360 ..classes = ['count'] | 374 ..classes = ['count'] |
| 361 ..title = 'shallow size', | 375 ..title = 'shallow size', |
| 362 new SpanElement()..classes = ['name'] | 376 new SpanElement()..classes = ['name'] |
| 363 ]; | 377 ]; |
| 364 } | 378 } |
| 365 | 379 |
| 380 static Element _createOwnershipClass(toggle) { |
| 381 return new DivElement() |
| 382 ..classes = ['tree-item'] |
| 383 ..children = [ |
| 384 new SpanElement() |
| 385 ..classes = ['size'] |
| 386 ..title = 'size', |
| 387 new SpanElement()..classes = ['name'] |
| 388 ]; |
| 389 } |
| 390 |
| 366 static const int kMaxChildren = 100; | 391 static const int kMaxChildren = 100; |
| 367 static const int kMinRetainedSize = 4096; | 392 static const int kMinRetainedSize = 4096; |
| 368 | 393 |
| 369 static _getChildrenDominator(M.HeapSnapshotDominatorNode node) { | 394 static _getChildrenDominator(M.HeapSnapshotDominatorNode node) { |
| 370 final list = node.children.toList(); | 395 final list = node.children.toList(); |
| 371 list.sort((a, b) => b.retainedSize - a.retainedSize); | 396 list.sort((a, b) => b.retainedSize - a.retainedSize); |
| 372 return list | 397 return list |
| 373 .where((child) => child.retainedSize >= kMinRetainedSize) | 398 .where((child) => child.retainedSize >= kMinRetainedSize) |
| 374 .take(kMaxChildren); | 399 .take(kMaxChildren); |
| 375 } | 400 } |
| (...skipping 10 matching lines...) Expand all Loading... |
| 386 if (item is M.HeapSnapshotClassReferences) { | 411 if (item is M.HeapSnapshotClassReferences) { |
| 387 if (item.inbounds.isNotEmpty || item.outbounds.isNotEmpty) { | 412 if (item.inbounds.isNotEmpty || item.outbounds.isNotEmpty) { |
| 388 return [item.inbounds, item.outbounds]; | 413 return [item.inbounds, item.outbounds]; |
| 389 } | 414 } |
| 390 } else if (item is Iterable) { | 415 } else if (item is Iterable) { |
| 391 return item.toList()..sort((a, b) => b.shallowSize - a.shallowSize); | 416 return item.toList()..sort((a, b) => b.shallowSize - a.shallowSize); |
| 392 } | 417 } |
| 393 return const []; | 418 return const []; |
| 394 } | 419 } |
| 395 | 420 |
| 421 static _getChildrenOwnershipClass(item) { |
| 422 return const []; |
| 423 } |
| 424 |
| 396 void _updateDominator( | 425 void _updateDominator( |
| 397 HtmlElement element, M.HeapSnapshotDominatorNode node, int depth) { | 426 HtmlElement element, M.HeapSnapshotDominatorNode node, int depth) { |
| 398 element.children[0].text = Utils.formatSize(node.retainedSize); | 427 element.children[0].text = Utils.formatSize(node.retainedSize); |
| 399 _updateLines(element.children[1].children, depth); | 428 _updateLines(element.children[1].children, depth); |
| 400 if (_getChildrenDominator(node).isNotEmpty) { | 429 if (_getChildrenDominator(node).isNotEmpty) { |
| 401 element.children[2].text = _tree.isExpanded(node) ? '▼' : '►'; | 430 element.children[2].text = _tree.isExpanded(node) ? '▼' : '►'; |
| 402 } else { | 431 } else { |
| 403 element.children[2].text = ''; | 432 element.children[2].text = ''; |
| 404 } | 433 } |
| 405 element.children[3].text = | 434 element.children[3].text = |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 503 ]; | 532 ]; |
| 504 } else if (item is M.HeapSnapshotClassOutbound) { | 533 } else if (item is M.HeapSnapshotClassOutbound) { |
| 505 element.children[3]..text = '${item.count} references to instances of '; | 534 element.children[3]..text = '${item.count} references to instances of '; |
| 506 element.children[4].children = [ | 535 element.children[4].children = [ |
| 507 new ClassRefElement(_isolate, item.target, queue: _r.queue) | 536 new ClassRefElement(_isolate, item.target, queue: _r.queue) |
| 508 ]; | 537 ]; |
| 509 } | 538 } |
| 510 } | 539 } |
| 511 } | 540 } |
| 512 | 541 |
| 542 void _updateOwnershipClass(HtmlElement element, item, int depth) { |
| 543 _updateLines(element.children[1].children, depth); |
| 544 element.children[0].text = Utils.formatSize(item.size); |
| 545 element.children[1] = |
| 546 new ClassRefElement(_isolate, item.clazz, queue: _r.queue) |
| 547 ..classes = ['name']; |
| 548 } |
| 549 |
| 513 static _updateLines(List<Element> lines, int n) { | 550 static _updateLines(List<Element> lines, int n) { |
| 514 n = Math.max(0, n); | 551 n = Math.max(0, n); |
| 515 while (lines.length > n) { | 552 while (lines.length > n) { |
| 516 lines.removeLast(); | 553 lines.removeLast(); |
| 517 } | 554 } |
| 518 while (lines.length < n) { | 555 while (lines.length < n) { |
| 519 lines.add(new SpanElement()); | 556 lines.add(new SpanElement()); |
| 520 } | 557 } |
| 521 } | 558 } |
| 522 | 559 |
| (...skipping 24 matching lines...) Expand all Loading... |
| 547 }) | 584 }) |
| 548 ]; | 585 ]; |
| 549 } | 586 } |
| 550 | 587 |
| 551 static String modeToString(HeapSnapshotTreeMode mode) { | 588 static String modeToString(HeapSnapshotTreeMode mode) { |
| 552 switch (mode) { | 589 switch (mode) { |
| 553 case HeapSnapshotTreeMode.dominatorTree: | 590 case HeapSnapshotTreeMode.dominatorTree: |
| 554 return 'Dominator tree'; | 591 return 'Dominator tree'; |
| 555 case HeapSnapshotTreeMode.mergedDominatorTree: | 592 case HeapSnapshotTreeMode.mergedDominatorTree: |
| 556 return 'Dominator tree (merged siblings by class)'; | 593 return 'Dominator tree (merged siblings by class)'; |
| 594 case HeapSnapshotTreeMode.ownershipTable: |
| 595 return 'Ownership table'; |
| 557 case HeapSnapshotTreeMode.groupByClass: | 596 case HeapSnapshotTreeMode.groupByClass: |
| 558 return 'Group by class'; | 597 return 'Group by class'; |
| 559 } | 598 } |
| 560 throw new Exception('Unknown HeapSnapshotTreeMode'); | 599 throw new Exception('Unknown HeapSnapshotTreeMode'); |
| 561 } | 600 } |
| 562 | 601 |
| 563 List<Element> _createModeSelect() { | 602 List<Element> _createModeSelect() { |
| 564 var s; | 603 var s; |
| 565 return [ | 604 return [ |
| 566 s = new SelectElement() | 605 s = new SelectElement() |
| 567 ..classes = ['analysis-select'] | 606 ..classes = ['analysis-select'] |
| 568 ..value = modeToString(_mode) | 607 ..value = modeToString(_mode) |
| 569 ..children = HeapSnapshotTreeMode.values.map((mode) { | 608 ..children = HeapSnapshotTreeMode.values.map((mode) { |
| 570 return new OptionElement( | 609 return new OptionElement( |
| 571 value: modeToString(mode), selected: _mode == mode) | 610 value: modeToString(mode), selected: _mode == mode) |
| 572 ..text = modeToString(mode); | 611 ..text = modeToString(mode); |
| 573 }).toList(growable: false) | 612 }).toList(growable: false) |
| 574 ..onChange.listen((_) { | 613 ..onChange.listen((_) { |
| 575 _mode = HeapSnapshotTreeMode.values[s.selectedIndex]; | 614 _mode = HeapSnapshotTreeMode.values[s.selectedIndex]; |
| 576 _r.dirty(); | 615 _r.dirty(); |
| 577 }) | 616 }) |
| 578 ]; | 617 ]; |
| 579 } | 618 } |
| 580 } | 619 } |
| OLD | NEW |