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; |
(...skipping 11 matching lines...) Expand all Loading... |
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.SampleProfileType _type; |
32 M.IsolateRef _isolate; | 33 M.IsolateRef _isolate; |
33 M.SampleProfile _profile; | 34 M.SampleProfile _profile; |
34 Iterable<M.CallTreeNodeFilter> _filters; | 35 Iterable<M.CallTreeNodeFilter> _filters; |
35 | 36 |
36 M.ProfileTreeDirection get direction => _direction; | 37 M.ProfileTreeDirection get direction => _direction; |
37 ProfileTreeMode get mode => _mode; | 38 ProfileTreeMode get mode => _mode; |
| 39 M.SampleProfileType get type => _type; |
38 M.IsolateRef get isolate => _isolate; | 40 M.IsolateRef get isolate => _isolate; |
39 M.SampleProfile get profile => _profile; | 41 M.SampleProfile get profile => _profile; |
40 Iterable<M.CallTreeNodeFilter> get filters => _filters; | 42 Iterable<M.CallTreeNodeFilter> get filters => _filters; |
41 | 43 |
42 set direction(M.ProfileTreeDirection value) => | 44 set direction(M.ProfileTreeDirection value) => |
43 _direction = _r.checkAndReact(_direction, value); | 45 _direction = _r.checkAndReact(_direction, value); |
44 set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value); | 46 set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value); |
45 set filters(Iterable<M.CallTreeNodeFilter> value) { | 47 set filters(Iterable<M.CallTreeNodeFilter> value) { |
46 _filters = new List.unmodifiable(value); | 48 _filters = new List.unmodifiable(value); |
47 _r.dirty(); | 49 _r.dirty(); |
48 } | 50 } |
49 | 51 |
50 factory CpuProfileVirtualTreeElement( | 52 factory CpuProfileVirtualTreeElement( |
51 M.IsolateRef isolate, M.SampleProfile profile, | 53 M.IsolateRef isolate, M.SampleProfile profile, |
52 {ProfileTreeMode mode: ProfileTreeMode.function, | 54 {ProfileTreeMode mode: ProfileTreeMode.function, |
| 55 M.SampleProfileType type: M.SampleProfileType.cpu, |
53 M.ProfileTreeDirection direction: M.ProfileTreeDirection.exclusive, | 56 M.ProfileTreeDirection direction: M.ProfileTreeDirection.exclusive, |
54 RenderingQueue queue}) { | 57 RenderingQueue queue}) { |
55 assert(isolate != null); | |
56 assert(profile != null); | 58 assert(profile != null); |
57 assert(mode != null); | 59 assert(mode != null); |
58 assert(direction != null); | 60 assert(direction != null); |
59 CpuProfileVirtualTreeElement e = document.createElement(tag.name); | 61 CpuProfileVirtualTreeElement e = document.createElement(tag.name); |
60 e._r = new RenderingScheduler(e, queue: queue); | 62 e._r = new RenderingScheduler(e, queue: queue); |
61 e._isolate = isolate; | 63 e._isolate = isolate; |
62 e._profile = profile; | 64 e._profile = profile; |
63 e._mode = mode; | 65 e._mode = mode; |
| 66 e._type = type; |
64 e._direction = direction; | 67 e._direction = direction; |
65 return e; | 68 return e; |
66 } | 69 } |
67 | 70 |
68 CpuProfileVirtualTreeElement.created() : super.created(); | 71 CpuProfileVirtualTreeElement.created() : super.created(); |
69 | 72 |
70 @override | 73 @override |
71 attached() { | 74 attached() { |
72 super.attached(); | 75 super.attached(); |
73 _r.enable(); | 76 _r.enable(); |
74 } | 77 } |
75 | 78 |
76 @override | 79 @override |
77 detached() { | 80 detached() { |
78 super.detached(); | 81 super.detached(); |
79 _r.disable(notify: true); | 82 _r.disable(notify: true); |
80 children = []; | 83 children = []; |
81 } | 84 } |
82 | 85 |
83 VirtualTreeElement _tree; | 86 VirtualTreeElement _tree; |
84 | 87 |
85 void render() { | 88 void render() { |
86 var tree; | 89 var tree; |
| 90 var create; |
87 var update; | 91 var update; |
88 switch (mode) { | 92 |
89 case ProfileTreeMode.code: | 93 switch (type) { |
90 tree = _profile.loadCodeTree(_direction); | 94 case M.SampleProfileType.cpu: |
91 update = _updateCodeRow; | 95 create = _createCpuRow; |
| 96 if (mode == ProfileTreeMode.code) { |
| 97 update = _updateCpuCodeRow; |
| 98 tree = _profile.loadCodeTree(_direction); |
| 99 } else if (mode == ProfileTreeMode.function) { |
| 100 update = _updateCpuFunctionRow; |
| 101 tree = _profile.loadFunctionTree(_direction); |
| 102 } else { |
| 103 throw new Exception('Unknown ProfileTreeMode: $mode'); |
| 104 } |
92 break; | 105 break; |
93 case ProfileTreeMode.function: | 106 case M.SampleProfileType.memory: |
94 tree = _profile.loadFunctionTree(_direction); | 107 create = _createMemoryRow; |
95 update = _updateFunctionRow; | 108 if (mode == ProfileTreeMode.code) { |
| 109 update = _updateMemoryCodeRow; |
| 110 tree = _profile.loadCodeTree(_direction); |
| 111 } else if (mode == ProfileTreeMode.function) { |
| 112 update = _updateMemoryFunctionRow; |
| 113 tree = _profile.loadFunctionTree(_direction); |
| 114 } else { |
| 115 throw new Exception('Unknown ProfileTreeMode: $mode'); |
| 116 } |
96 break; | 117 break; |
97 default: | 118 default: |
98 throw new Exception('Unknown ProfileTreeMode: $mode'); | 119 throw new Exception('Unknown SampleProfileType: $type'); |
| 120 break; |
99 } | 121 } |
100 if (filters != null) { | 122 if (filters != null) { |
101 tree = filters.fold(tree, (tree, filter) { | 123 tree = filters.fold(tree, (tree, filter) { |
102 return tree?.filtered(filter); | 124 return tree?.filtered(filter); |
103 }); | 125 }); |
104 } | 126 } |
105 if (tree == null) { | 127 if (tree == null) { |
106 children = [new HeadingElement.h1()..text = 'No Results']; | 128 children = [new HeadingElement.h1()..text = 'No Results']; |
107 return; | 129 return; |
108 } | 130 } |
109 _tree = new VirtualTreeElement(_createRow, update, _getChildren, | 131 _tree = new VirtualTreeElement(create, update, _getChildren, |
110 items: tree.root.children, queue: _r.queue); | 132 items: tree.root.children, queue: _r.queue); |
111 if (tree.root.children.length == 1) { | 133 if (tree.root.children.length == 1) { |
112 _tree.expand(tree.root.children.first, autoExpandSingleChildNodes: true); | 134 _tree.expand(tree.root.children.first, autoExpandSingleChildNodes: true); |
113 } | 135 } |
114 children = [_tree]; | 136 children = [_tree]; |
115 } | 137 } |
116 | 138 |
117 static Element _createRow(toggle) { | 139 static Element _createCpuRow(toggle) { |
118 return new DivElement() | 140 return new DivElement() |
119 ..classes = ['tree-item'] | 141 ..classes = ['tree-item'] |
120 ..children = [ | 142 ..children = [ |
121 new SpanElement() | 143 new SpanElement() |
122 ..classes = ['inclusive'] | 144 ..classes = ['inclusive'] |
123 ..title = 'global % on stack', | 145 ..title = 'global % on stack', |
124 new SpanElement() | 146 new SpanElement() |
125 ..classes = ['exclusive'] | 147 ..classes = ['exclusive'] |
126 ..title = 'global % executing', | 148 ..title = 'global % executing', |
127 new SpanElement()..classes = ['lines'], | 149 new SpanElement()..classes = ['lines'], |
128 new ButtonElement() | 150 new ButtonElement() |
129 ..classes = ['expander'] | 151 ..classes = ['expander'] |
130 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), | 152 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), |
131 new SpanElement() | 153 new SpanElement() |
132 ..classes = ['percentage'] | 154 ..classes = ['percentage'] |
133 ..title = 'tree node %', | 155 ..title = 'tree node %', |
134 new SpanElement()..classes = ['name'] | 156 new SpanElement()..classes = ['name'] |
135 ]; | 157 ]; |
136 } | 158 } |
137 | 159 |
| 160 static Element _createMemoryRow(toggle) { |
| 161 return new DivElement() |
| 162 ..classes = ['tree-item'] |
| 163 ..children = [ |
| 164 new SpanElement() |
| 165 ..classes = ['inclusive'] |
| 166 ..title = 'memory allocated from resulting calls: ', |
| 167 new SpanElement() |
| 168 ..classes = ['exclusive'] |
| 169 ..title = 'memory allocated during execution: ', |
| 170 new SpanElement()..classes = ['lines'], |
| 171 new ButtonElement() |
| 172 ..classes = ['expander'] |
| 173 ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)), |
| 174 new SpanElement() |
| 175 ..classes = ['percentage'] |
| 176 ..title = 'tree node %', |
| 177 new SpanElement()..classes = ['name'] |
| 178 ]; |
| 179 } |
| 180 |
138 static _getChildren(M.CallTreeNode node) => node.children; | 181 static _getChildren(M.CallTreeNode node) => node.children; |
139 | 182 |
140 void _updateFunctionRow( | 183 static const String _expandedIcon = '▼'; |
| 184 static const String _collapsedIcon = '►'; |
| 185 |
| 186 void _updateCpuFunctionRow( |
141 HtmlElement element, M.FunctionCallTreeNode item, int depth) { | 187 HtmlElement element, M.FunctionCallTreeNode item, int depth) { |
142 element.children[0].text = Utils | 188 element.children[0].text = Utils |
143 .formatPercentNormalized(item.profileFunction.normalizedInclusiveTicks); | 189 .formatPercentNormalized(item.profileFunction.normalizedInclusiveTicks); |
144 element.children[1].text = Utils | 190 element.children[1].text = Utils |
145 .formatPercentNormalized(item.profileFunction.normalizedExclusiveTicks); | 191 .formatPercentNormalized(item.profileFunction.normalizedExclusiveTicks); |
146 _updateLines(element.children[2].children, depth); | 192 _updateLines(element.children[2].children, depth); |
147 if (item.children.isNotEmpty) { | 193 if (item.children.isNotEmpty) { |
148 element.children[3].text = _tree.isExpanded(item) ? '▼' : '►'; | 194 element.children[3].text = _tree.isExpanded(item) ? _expandedIcon : |
| 195 _collapsedIcon; |
149 } else { | 196 } else { |
150 element.children[3].text = ''; | 197 element.children[3].text = ''; |
151 } | 198 } |
152 element.children[4].text = Utils.formatPercentNormalized(item.percentage); | 199 element.children[4].text = Utils.formatPercentNormalized(item.percentage); |
153 element.children[5] = new FunctionRefElement( | 200 element.children[5] = new FunctionRefElement( |
154 _isolate, item.profileFunction.function, queue: _r.queue) | 201 _isolate, item.profileFunction.function, queue: _r.queue) |
155 ..classes = ['name']; | 202 ..classes = ['name']; |
156 } | 203 } |
157 | 204 |
158 void _updateCodeRow(HtmlElement element, M.CodeCallTreeNode item, int depth) { | 205 void _updateMemoryFunctionRow( |
| 206 HtmlElement element, M.FunctionCallTreeNode item, int depth) { |
| 207 element.children[0].text = |
| 208 Utils.formatSize(item.inclusiveNativeAllocations); |
| 209 element.children[0].title = 'memory allocated from resulting calls: ' + |
| 210 '${item.inclusiveNativeAllocations}B'; |
| 211 element.children[1].text = |
| 212 Utils.formatSize(item.exclusiveNativeAllocations); |
| 213 element.children[1].title = 'memory allocated during execution: ' + |
| 214 '${item.exclusiveNativeAllocations}B'; |
| 215 _updateLines(element.children[2].children, depth); |
| 216 if (item.children.isNotEmpty) { |
| 217 element.children[3].text = _tree.isExpanded(item) ? _expandedIcon : |
| 218 _collapsedIcon; |
| 219 } else { |
| 220 element.children[3].text = ''; |
| 221 } |
| 222 element.children[4].text = Utils.formatPercentNormalized(item.percentage); |
| 223 element.children[5] = new FunctionRefElement( |
| 224 null, item.profileFunction.function, queue: _r.queue) |
| 225 ..classes = ['name']; |
| 226 } |
| 227 |
| 228 void _updateCpuCodeRow( |
| 229 HtmlElement element, M.CodeCallTreeNode item, int depth) { |
159 element.children[0].text = Utils | 230 element.children[0].text = Utils |
160 .formatPercentNormalized(item.profileCode.normalizedInclusiveTicks); | 231 .formatPercentNormalized(item.profileCode.normalizedInclusiveTicks); |
161 element.children[1].text = Utils | 232 element.children[1].text = Utils |
162 .formatPercentNormalized(item.profileCode.normalizedExclusiveTicks); | 233 .formatPercentNormalized(item.profileCode.normalizedExclusiveTicks); |
163 _updateLines(element.children[2].children, depth); | 234 _updateLines(element.children[2].children, depth); |
164 if (item.children.isNotEmpty) { | 235 if (item.children.isNotEmpty) { |
165 element.children[3].text = _tree.isExpanded(item) ? '▼' : '►'; | 236 element.children[3].text = _tree.isExpanded(item) ? _expandedIcon : |
| 237 _collapsedIcon; |
166 } else { | 238 } else { |
167 element.children[3].text = ''; | 239 element.children[3].text = ''; |
168 } | 240 } |
169 element.children[4].text = Utils.formatPercentNormalized(item.percentage); | 241 element.children[4].text = Utils.formatPercentNormalized(item.percentage); |
170 element.children[5] = new CodeRefElement(_isolate, item.profileCode.code, | 242 element.children[5] = new CodeRefElement(_isolate, item.profileCode.code, |
171 queue: _r.queue)..classes = ['name']; | 243 queue: _r.queue)..classes = ['name']; |
172 } | 244 } |
173 | 245 |
| 246 void _updateMemoryCodeRow( |
| 247 HtmlElement element, M.CodeCallTreeNode item, int depth) { |
| 248 element.children[0].text = |
| 249 Utils.formatSize(item.inclusiveNativeAllocations); |
| 250 element.children[0].title = 'memory allocated from resulting calls: ' + |
| 251 '${item.inclusiveNativeAllocations}B'; |
| 252 element.children[1].text = |
| 253 Utils.formatSize(item.exclusiveNativeAllocations); |
| 254 element.children[1].title = 'memory allocated during execution: ' + |
| 255 '${item.exclusiveNativeAllocations}B'; |
| 256 _updateLines(element.children[2].children, depth); |
| 257 if (item.children.isNotEmpty) { |
| 258 element.children[3].text = _tree.isExpanded(item) ? _expandedIcon : |
| 259 _collapsedIcon; |
| 260 } else { |
| 261 element.children[3].text = ''; |
| 262 } |
| 263 element.children[4].text = Utils.formatPercentNormalized(item.percentage); |
| 264 element.children[5] = new CodeRefElement(null, item.profileCode.code, |
| 265 queue: _r.queue)..classes = ['name']; |
| 266 } |
| 267 |
174 static _updateLines(List<Element> lines, int n) { | 268 static _updateLines(List<Element> lines, int n) { |
175 n = Math.max(0, n); | 269 n = Math.max(0, n); |
176 while (lines.length > n) { | 270 while (lines.length > n) { |
177 lines.removeLast(); | 271 lines.removeLast(); |
178 } | 272 } |
179 while (lines.length < n) { | 273 while (lines.length < n) { |
180 lines.add(new SpanElement()); | 274 lines.add(new SpanElement()); |
181 } | 275 } |
182 } | 276 } |
183 } | 277 } |
OLD | NEW |