| 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 |