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