| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:html'; | 6 import 'dart:html'; |
| 7 import 'dart:math' as Math; | 7 import 'dart:math' as Math; |
| 8 import 'package:observatory/models.dart' as M; | 8 import 'package:observatory/models.dart' as M; |
| 9 import 'package:observatory/src/elements/stack_trace_tree_config.dart' | 9 import 'package:observatory/src/elements/stack_trace_tree_config.dart' |
| 10 show ProfileTreeMode; | 10 show ProfileTreeMode; |
| 11 import 'package:observatory/src/elements/code_ref.dart'; | 11 import 'package:observatory/src/elements/code_ref.dart'; |
| 12 import 'package:observatory/src/elements/containers/virtual_tree.dart'; | 12 import 'package:observatory/src/elements/containers/virtual_tree.dart'; |
| 13 import 'package:observatory/src/elements/function_ref.dart'; | 13 import 'package:observatory/src/elements/function_ref.dart'; |
| 14 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; | 14 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| 15 import 'package:observatory/src/elements/helpers/tag.dart'; | 15 import 'package:observatory/src/elements/helpers/tag.dart'; |
| 16 import 'package:observatory/utils.dart'; | 16 import 'package:observatory/utils.dart'; |
| 17 | 17 |
| 18 export 'package:observatory/src/elements/stack_trace_tree_config.dart' | 18 export 'package:observatory/src/elements/stack_trace_tree_config.dart' |
| 19 show ProfileTreeMode; | 19 show ProfileTreeMode; |
| 20 | 20 |
| 21 class CpuProfileVirtualTreeElement extends HtmlElement implements Renderable { | 21 class CpuProfileVirtualTreeElement extends HtmlElement implements Renderable { |
| 22 static const tag = | 22 static const tag = |
| 23 const Tag<CpuProfileVirtualTreeElement>('cpu-profile-virtual-tree'); | 23 const Tag<CpuProfileVirtualTreeElement>('cpu-profile-virtual-tree'); |
| 24 | 24 |
| 25 RenderingScheduler<CpuProfileVirtualTreeElement> _r; | 25 RenderingScheduler<CpuProfileVirtualTreeElement> _r; |
| 26 | 26 |
| 27 Stream<RenderedEvent<CpuProfileVirtualTreeElement>> get onRendered => | 27 Stream<RenderedEvent<CpuProfileVirtualTreeElement>> get onRendered => |
| 28 _r.onRendered; | 28 _r.onRendered; |
| 29 | 29 |
| 30 M.ProfileTreeDirection _direction; | 30 M.ProfileTreeDirection _direction; |
| 31 ProfileTreeMode _mode; | 31 ProfileTreeMode _mode; |
| 32 M.IsolateRef _isolate; | 32 M.IsolateRef _isolate; |
| 33 M.SampleProfile _profile; | 33 M.SampleProfile _profile; |
| 34 Iterable<M.CallTreeNodeFilter> _filters; | 34 Iterable<M.CallTreeNodeFilter> _filters; |
| 35 | 35 |
| 36 M.ProfileTreeDirection get direction => _direction; | 36 M.ProfileTreeDirection get direction => _direction; |
| 37 ProfileTreeMode get mode => _mode; | 37 ProfileTreeMode get mode => _mode; |
| 38 M.IsolateRef get isolate => _isolate; | 38 M.IsolateRef get isolate => _isolate; |
| 39 M.SampleProfile get profile => _profile; | 39 M.SampleProfile get profile => _profile; |
| 40 Iterable<M.CallTreeNodeFilter> get filters => _filters; | 40 Iterable<M.CallTreeNodeFilter> get filters => _filters; |
| 41 | 41 |
| 42 set direction(M.ProfileTreeDirection value) => | 42 set direction(M.ProfileTreeDirection value) => |
| 43 _direction = _r.checkAndReact(_direction, value); | 43 _direction = _r.checkAndReact(_direction, value); |
| 44 set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value); | 44 set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value); |
| 45 set filters(Iterable<M.CallTreeNodeFilter> value) { | 45 set filters(Iterable<M.CallTreeNodeFilter> value) { |
| 46 _filters = new List.unmodifiable(value); | 46 _filters = new List.unmodifiable(value); |
| 47 _r.dirty(); | 47 _r.dirty(); |
| 48 } | 48 } |
| 49 | 49 |
| 50 factory CpuProfileVirtualTreeElement(M.IsolateRef isolate, | 50 factory CpuProfileVirtualTreeElement( |
| 51 M.SampleProfile profile, {ProfileTreeMode mode: ProfileTreeMode.function, | 51 M.IsolateRef isolate, M.SampleProfile profile, |
| 52 {ProfileTreeMode mode: ProfileTreeMode.function, |
| 52 M.ProfileTreeDirection direction: M.ProfileTreeDirection.exclusive, | 53 M.ProfileTreeDirection direction: M.ProfileTreeDirection.exclusive, |
| 53 RenderingQueue queue}) { | 54 RenderingQueue queue}) { |
| 54 assert(isolate != null); | 55 assert(isolate != null); |
| 55 assert(profile != null); | 56 assert(profile != null); |
| 56 assert(mode != null); | 57 assert(mode != null); |
| 57 assert(direction != null); | 58 assert(direction != null); |
| 58 CpuProfileVirtualTreeElement e = document.createElement(tag.name); | 59 CpuProfileVirtualTreeElement e = document.createElement(tag.name); |
| 59 e._r = new RenderingScheduler(e, queue: queue); | 60 e._r = new RenderingScheduler(e, queue: queue); |
| 60 e._isolate = isolate; | 61 e._isolate = isolate; |
| 61 e._profile = profile; | 62 e._profile = profile; |
| 62 e._mode = mode; | 63 e._mode = mode; |
| 63 e._direction = direction; | 64 e._direction = direction; |
| 64 return e; | 65 return e; |
| 65 } | 66 } |
| 66 | 67 |
| 67 CpuProfileVirtualTreeElement.created() : super.created(); | 68 CpuProfileVirtualTreeElement.created() : super.created(); |
| 68 | 69 |
| 69 @override | 70 @override |
| 70 attached() { | 71 attached() { |
| 71 super.attached(); | 72 super.attached(); |
| 72 _r.enable(); | 73 _r.enable(); |
| 73 } | 74 } |
| 74 | 75 |
| 75 @override | 76 @override |
| 76 detached() { | 77 detached() { |
| 77 super.detached(); | 78 super.detached(); |
| 78 _r.disable(notify: true); | 79 _r.disable(notify: true); |
| 79 children = []; | 80 children = []; |
| 80 } | 81 } |
| 81 | 82 |
| 82 VirtualTreeElement _tree; | 83 VirtualTreeElement _tree; |
| 83 | 84 |
| 84 void render() { | 85 void render() { |
| 85 var tree; | 86 var tree; |
| 86 var update; | 87 var update; |
| 87 switch (mode) { | 88 switch (mode) { |
| 88 case ProfileTreeMode.code: | 89 case ProfileTreeMode.code: |
| 89 tree = _profile.loadCodeTree(_direction); | 90 tree = _profile.loadCodeTree(_direction); |
| 90 update = _updateCodeRow; | 91 update = _updateCodeRow; |
| 91 break; | 92 break; |
| 92 case ProfileTreeMode.function: | 93 case ProfileTreeMode.function: |
| 93 tree = _profile.loadFunctionTree(_direction); | 94 tree = _profile.loadFunctionTree(_direction); |
| 94 update = _updateFunctionRow; | 95 update = _updateFunctionRow; |
| 95 break; | 96 break; |
| 96 default: | 97 default: |
| 97 throw new Exception('Unknown ProfileTreeMode: $mode'); | 98 throw new Exception('Unknown ProfileTreeMode: $mode'); |
| 98 } | 99 } |
| 99 if (filters != null) { | 100 if (filters != null) { |
| 100 tree = filters.fold(tree, (tree, filter) { | 101 tree = filters.fold(tree, (tree, filter) { |
| 101 return tree?.filtered(filter); | 102 return tree?.filtered(filter); |
| 102 }); | 103 }); |
| 103 } | 104 } |
| 104 if (tree == null) { | 105 if (tree == null) { |
| 105 children = [ | 106 children = [new HeadingElement.h1()..text = 'No Results']; |
| 106 new HeadingElement.h1()..text = 'No Results' | |
| 107 ]; | |
| 108 return; | 107 return; |
| 109 } | 108 } |
| 110 _tree = new VirtualTreeElement(_createRow, update, _getChildren, | 109 _tree = new VirtualTreeElement(_createRow, update, _getChildren, |
| 111 items: tree.root.children, queue: _r.queue); | 110 items: tree.root.children, queue: _r.queue); |
| 112 if (tree.root.children.length == 1) { | 111 if (tree.root.children.length == 1) { |
| 113 _tree.expand(tree.root.children.first, autoExpandSingleChildNodes: true); | 112 _tree.expand(tree.root.children.first, autoExpandSingleChildNodes: true); |
| 114 } | 113 } |
| 115 children = [_tree]; | 114 children = [_tree]; |
| 116 } | 115 } |
| 117 | 116 |
| 118 static Element _createRow(toggle) { | 117 static Element _createRow(toggle) { |
| 119 return new DivElement() | 118 return new DivElement() |
| 120 ..classes = ['tree-item'] | 119 ..classes = ['tree-item'] |
| 121 ..children = [ | 120 ..children = [ |
| 122 new SpanElement()..classes = ['inclusive'] | 121 new SpanElement() |
| 122 ..classes = ['inclusive'] |
| 123 ..title = 'global % on stack', | 123 ..title = 'global % on stack', |
| 124 new SpanElement()..classes = ['exclusive'] | 124 new SpanElement() |
| 125 ..classes = ['exclusive'] |
| 125 ..title = 'global % executing', | 126 ..title = 'global % executing', |
| 126 new SpanElement()..classes = ['lines'], | 127 new SpanElement()..classes = ['lines'], |
| 127 new ButtonElement()..classes = ['expander'] | 128 new ButtonElement() |
| 129 ..classes = ['expander'] |
| 128 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), | 130 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), |
| 129 new SpanElement()..classes = ['percentage'] | 131 new SpanElement() |
| 132 ..classes = ['percentage'] |
| 130 ..title = 'tree node %', | 133 ..title = 'tree node %', |
| 131 new SpanElement()..classes = ['name'] | 134 new SpanElement()..classes = ['name'] |
| 132 ]; | 135 ]; |
| 133 } | 136 } |
| 134 | 137 |
| 135 static _getChildren(M.CallTreeNode node) => node.children; | 138 static _getChildren(M.CallTreeNode node) => node.children; |
| 136 | 139 |
| 137 void _updateFunctionRow(HtmlElement element, M.FunctionCallTreeNode item, | 140 void _updateFunctionRow( |
| 138 int depth) { | 141 HtmlElement element, M.FunctionCallTreeNode item, int depth) { |
| 139 element.children[0].text = Utils.formatPercentNormalized( | 142 element.children[0].text = Utils |
| 140 item.profileFunction.normalizedInclusiveTicks); | 143 .formatPercentNormalized(item.profileFunction.normalizedInclusiveTicks); |
| 141 element.children[1].text = Utils.formatPercentNormalized( | 144 element.children[1].text = Utils |
| 142 item.profileFunction.normalizedExclusiveTicks); | 145 .formatPercentNormalized(item.profileFunction.normalizedExclusiveTicks); |
| 143 _updateLines(element.children[2].children, depth); | 146 _updateLines(element.children[2].children, depth); |
| 144 if (item.children.isNotEmpty) { | 147 if (item.children.isNotEmpty) { |
| 145 element.children[3].text = _tree.isExpanded(item) ? '▼' : '►'; | 148 element.children[3].text = _tree.isExpanded(item) ? '▼' : '►'; |
| 146 } else { | 149 } else { |
| 147 element.children[3].text = ''; | 150 element.children[3].text = ''; |
| 148 } | 151 } |
| 149 element.children[4].text = Utils.formatPercentNormalized( | 152 element.children[4].text = Utils.formatPercentNormalized(item.percentage); |
| 150 item.percentage); | 153 element.children[5] = new FunctionRefElement( |
| 151 element.children[5] = new FunctionRefElement(_isolate, | 154 _isolate, item.profileFunction.function, queue: _r.queue) |
| 152 item.profileFunction.function, queue: _r.queue) | 155 ..classes = ['name']; |
| 153 ..classes = ['name']; | |
| 154 } | 156 } |
| 155 | 157 |
| 156 void _updateCodeRow(HtmlElement element, M.CodeCallTreeNode item, int depth) { | 158 void _updateCodeRow(HtmlElement element, M.CodeCallTreeNode item, int depth) { |
| 157 element.children[0].text = Utils.formatPercentNormalized( | 159 element.children[0].text = Utils |
| 158 item.profileCode.normalizedInclusiveTicks); | 160 .formatPercentNormalized(item.profileCode.normalizedInclusiveTicks); |
| 159 element.children[1].text = Utils.formatPercentNormalized( | 161 element.children[1].text = Utils |
| 160 item.profileCode.normalizedExclusiveTicks); | 162 .formatPercentNormalized(item.profileCode.normalizedExclusiveTicks); |
| 161 _updateLines(element.children[2].children, depth); | 163 _updateLines(element.children[2].children, depth); |
| 162 if (item.children.isNotEmpty) { | 164 if (item.children.isNotEmpty) { |
| 163 element.children[3].text = _tree.isExpanded(item) ? '▼' : '►'; | 165 element.children[3].text = _tree.isExpanded(item) ? '▼' : '►'; |
| 164 } else { | 166 } else { |
| 165 element.children[3].text = ''; | 167 element.children[3].text = ''; |
| 166 } | 168 } |
| 167 element.children[4].text = Utils.formatPercentNormalized( | 169 element.children[4].text = Utils.formatPercentNormalized(item.percentage); |
| 168 item.percentage); | 170 element.children[5] = new CodeRefElement(_isolate, item.profileCode.code, |
| 169 element.children[5] = new CodeRefElement(_isolate, | 171 queue: _r.queue)..classes = ['name']; |
| 170 item.profileCode.code, queue: _r.queue) | |
| 171 ..classes = ['name']; | |
| 172 } | 172 } |
| 173 | 173 |
| 174 static _updateLines(List<Element> lines, int n) { | 174 static _updateLines(List<Element> lines, int n) { |
| 175 n = Math.max(0, n); | 175 n = Math.max(0, n); |
| 176 while (lines.length > n) { | 176 while (lines.length > n) { |
| 177 lines.removeLast(); | 177 lines.removeLast(); |
| 178 } | 178 } |
| 179 while (lines.length < n) { | 179 while (lines.length < n) { |
| 180 lines.add(new SpanElement()); | 180 lines.add(new SpanElement()); |
| 181 } | 181 } |
| 182 } | 182 } |
| 183 } | 183 } |
| OLD | NEW |