Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(159)

Side by Side Diff: runtime/observatory/lib/src/elements/cpu_profile.dart

Issue 839633004: Improve performance of table trees by around 3x (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 library isolate_profile_element; 5 library cpu_profile_element;
6 6
7 import 'dart:html'; 7 import 'dart:html';
8 import 'observatory_element.dart'; 8 import 'observatory_element.dart';
9 import 'package:logging/logging.dart'; 9 import 'package:logging/logging.dart';
10 import 'package:observatory/service.dart'; 10 import 'package:observatory/service.dart';
11 import 'package:observatory/app.dart'; 11 import 'package:observatory/app.dart';
12 import 'package:observatory/elements.dart';
12 import 'package:polymer/polymer.dart'; 13 import 'package:polymer/polymer.dart';
13 14
14 class ProfileCodeTrieNodeTreeRow extends TableTreeRow { 15 class ProfileCodeTrieNodeTreeRow extends TableTreeRow {
15 final ServiceMap profile; 16 final ServiceMap profile;
16 @reflectable final CodeTrieNode root; 17 @reflectable final CodeTrieNode root;
17 @reflectable final CodeTrieNode node; 18 @reflectable final CodeTrieNode node;
18 @reflectable Code get code => node.code; 19 @reflectable Code get code => node.code;
19 20
20 @reflectable String tipKind = ''; 21 @reflectable String tipKind = '';
21 @reflectable String tipParent = ''; 22 @reflectable String tipParent = '';
22 @reflectable String tipExclusive = ''; 23 @reflectable String tipExclusive = '';
23 @reflectable String tipTicks = ''; 24 @reflectable String tipTicks = '';
24 @reflectable String tipTime = ''; 25 @reflectable String tipTime = '';
25 26
26 ProfileCodeTrieNodeTreeRow(this.profile, this.root, this.node, 27 ProfileCodeTrieNodeTreeRow(this.profile, this.root, this.node,
28 TableTree tree,
27 ProfileCodeTrieNodeTreeRow parent) 29 ProfileCodeTrieNodeTreeRow parent)
28 : super(parent) { 30 : super(tree, parent) {
29 assert(root != null); 31 assert(root != null);
30 assert(node != null); 32 assert(node != null);
31 tipTicks = '${node.count}'; 33 tipTicks = '${node.count}';
32 var period = profile['period']; 34 var period = profile['period'];
33 var MICROSECONDS_PER_SECOND = 1000000.0; 35 var MICROSECONDS_PER_SECOND = 1000000.0;
34 var seconds = (period * node.count) / MICROSECONDS_PER_SECOND; // seconds 36 var seconds = (period * node.count) / MICROSECONDS_PER_SECOND; // seconds
35 tipTime = Utils.formatTimePrecise(seconds); 37 tipTime = Utils.formatTimePrecise(seconds);
36 if (code.kind == CodeKind.Tag) { 38 if (code.kind == CodeKind.Tag) {
37 tipKind = 'Tag (category)'; 39 tipKind = 'Tag (category)';
38 if (parent == null) { 40 if (parent == null) {
39 tipParent = Utils.formatPercent(node.count, root.count); 41 tipParent = Utils.formatPercent(node.count, root.count);
40 } else { 42 } else {
41 tipParent = Utils.formatPercent(node.count, parent.node.count); 43 tipParent = Utils.formatPercent(node.count, parent.node.count);
42 } 44 }
43 tipExclusive = Utils.formatPercent(node.count, root.count); 45 tipExclusive = Utils.formatPercent(node.count, root.count);
44 } else { 46 } else {
45 if ((code.kind == CodeKind.Collected) || 47 if ((code.kind == CodeKind.Collected) ||
46 (code.kind == CodeKind.Reused)) { 48 (code.kind == CodeKind.Reused)) {
47 tipKind = 'Garbage Collected Code'; 49 tipKind = 'Garbage Collected Code';
48 } else { 50 } else {
49 tipKind = '${code.kind} (Function)'; 51 tipKind = '${code.kind} (Function)';
50 } 52 }
51 if (parent == null) { 53 if (parent == null) {
52 tipParent = Utils.formatPercent(node.count, root.count); 54 tipParent = Utils.formatPercent(node.count, root.count);
53 } else { 55 } else {
54 tipParent = Utils.formatPercent(node.count, parent.node.count); 56 tipParent = Utils.formatPercent(node.count, parent.node.count);
55 } 57 }
56 tipExclusive = Utils.formatPercent(node.code.exclusiveTicks, root.count); 58 tipExclusive = Utils.formatPercent(node.code.exclusiveTicks, root.count);
57 } 59 }
58 columns.add(tipParent);
59 columns.add(tipExclusive);
60 } 60 }
61 61
62 bool shouldDisplayChild(CodeTrieNode childNode, double threshold) { 62 bool shouldDisplayChild(CodeTrieNode childNode, double threshold) {
63 return ((childNode.count / node.count) > threshold) || 63 return ((childNode.count / node.count) > threshold) ||
64 ((childNode.code.exclusiveTicks / root.count) > threshold); 64 ((childNode.code.exclusiveTicks / root.count) > threshold);
65 } 65 }
66 66
67 void onShow() { 67 void _buildTooltip(DivElement memberList, Map<String, String> items) {
68 var threshold = profile['threshold']; 68 items.forEach((k, v) {
69 if (children.length > 0) { 69 var item = new DivElement();
70 // Child rows already created. 70 item.classes.add('memberItem');
71 return; 71 var name = new DivElement();
72 } 72 name.classes.add('memberName');
73 for (var childNode in node.children) { 73 name.classes.add('white');
74 if (!shouldDisplayChild(childNode, threshold)) { 74 name.text = k;
75 continue; 75 var value = new DivElement();
76 } 76 value.classes.add('memberValue');
77 var row = new ProfileCodeTrieNodeTreeRow(profile, root, childNode, this); 77 value.classes.add('white');
78 children.add(row); 78 value.text = v;
79 } 79 item.children.add(name);
80 item.children.add(value);
81 memberList.children.add(item);
82 });
80 } 83 }
81 84
82 void onHide() { 85 void onShow() {
86 super.onShow();
87 if (children.length == 0) {
88 var threshold = profile['threshold'];
89 for (var childNode in node.children) {
90 if (!shouldDisplayChild(childNode, threshold)) {
91 continue;
92 }
93 var row =
94 new ProfileCodeTrieNodeTreeRow(profile, root, childNode, tree, this) ;
95 children.add(row);
96 }
97 }
98 var row = tr;
99
100 var methodCell = tableColumns[0];
101 // Enable expansion by clicking anywhere on the method column.
102 methodCell.onClick.listen(onClick);
103
104 // Insert the parent percentage
105 var parentPercent = new DivElement();
106 parentPercent.style.position = 'relative';
107 parentPercent.style.display = 'inline';
108 parentPercent.text = tipParent;
109 methodCell.children.add(parentPercent);
110
111 var codeRef = new Element.tag('code-ref');
112 codeRef.ref = code;
113 methodCell.children.add(codeRef);
114
115 var selfCell = tableColumns[1];
116 selfCell.style.position = 'relative';
117 selfCell.text = tipExclusive;
118
119 var tooltipDiv = new DivElement();
120 tooltipDiv.classes.add('tooltip');
121
122 var memberListDiv = new DivElement();
123 memberListDiv.classes.add('memberList');
124 tooltipDiv.children.add(memberListDiv);
125 _buildTooltip(memberListDiv, {
126 'Kind' : tipKind,
127 'Percent of Parent' : tipParent,
128 'Sample Count' : tipTicks,
129 'Approximate Execution Time': tipTime,
130 });
131 selfCell.children.add(tooltipDiv);
83 } 132 }
84 133
85 bool hasChildren() { 134 bool hasChildren() {
86 return node.children.length > 0; 135 return node.children.length > 0;
87 } 136 }
88 } 137 }
89 138
90 /// Displays an IsolateProfile 139 /// Displays a CpuProfile
91 @CustomTag('isolate-profile') 140 @CustomTag('cpu-profile')
92 class IsolateProfileElement extends ObservatoryElement { 141 class CpuProfileElement extends ObservatoryElement {
93 IsolateProfileElement.created() : super.created(); 142 CpuProfileElement.created() : super.created();
94 @published ServiceMap profile; 143 @published ServiceMap profile;
95 @observable bool hideTagsChecked; 144 @observable bool hideTagsChecked;
96 @observable String sampleCount = ''; 145 @observable String sampleCount = '';
97 @observable String refreshTime = ''; 146 @observable String refreshTime = '';
98 @observable String sampleRate = ''; 147 @observable String sampleRate = '';
99 @observable String sampleDepth = ''; 148 @observable String sampleDepth = '';
100 @observable String displayCutoff = ''; 149 @observable String displayCutoff = '';
101 @observable String timeSpan = ''; 150 @observable String timeSpan = '';
102 @reflectable double displayThreshold = 0.0002; // 0.02%. 151 @reflectable double displayThreshold = 0.0002; // 0.02%.
103 152
(...skipping 19 matching lines...) Expand all
123 displayCutoff = '${(displayThreshold * 100.0).toString()}%'; 172 displayCutoff = '${(displayThreshold * 100.0).toString()}%';
124 profile.isolate.processProfile(profile); 173 profile.isolate.processProfile(profile);
125 profile['threshold'] = displayThreshold; 174 profile['threshold'] = displayThreshold;
126 _update(); 175 _update();
127 } 176 }
128 177
129 178
130 @override 179 @override
131 void attached() { 180 void attached() {
132 super.attached(); 181 super.attached();
133 tree = new TableTree(); 182 var tableBody = shadowRoot.querySelector('#tableTreeBody');
183 assert(tableBody != null);
184 tree = new TableTree(tableBody, 2);
134 _update(); 185 _update();
135 } 186 }
136 187
137 void tagSelectorChanged(oldValue) { 188 void tagSelectorChanged(oldValue) {
138 refresh(null); 189 refresh(null);
139 } 190 }
140 191
141 void refresh(var done) { 192 void refresh(var done) {
142 var request = 'profile?tags=$tagSelector'; 193 var request = 'profile?tags=$tagSelector';
143 profile.isolate.get(request).then((ServiceMap m) { 194 profile.isolate.get(request).then((ServiceMap m) {
(...skipping 10 matching lines...) Expand all
154 _buildTree(); 205 _buildTree();
155 } 206 }
156 207
157 void _buildStackTree() { 208 void _buildStackTree() {
158 var root = profile.isolate.profileTrieRoot; 209 var root = profile.isolate.profileTrieRoot;
159 if (root == null) { 210 if (root == null) {
160 return; 211 return;
161 } 212 }
162 try { 213 try {
163 tree.initialize( 214 tree.initialize(
164 new ProfileCodeTrieNodeTreeRow(profile, root, root, null)); 215 new ProfileCodeTrieNodeTreeRow(profile, root, root, tree, null));
165 } catch (e, stackTrace) { 216 } catch (e, stackTrace) {
217 print(e);
218 print(stackTrace);
166 Logger.root.warning('_buildStackTree', e, stackTrace); 219 Logger.root.warning('_buildStackTree', e, stackTrace);
167 } 220 }
168 // Check if we only have one node at the root and expand it. 221 // Check if we only have one node at the root and expand it.
169 if (tree.rows.length == 1) { 222 if (tree.rows.length == 1) {
170 tree.toggle(0); 223 tree.toggle(tree.rows[0]);
171 } 224 }
172 notifyPropertyChange(#tree, null, tree); 225 notifyPropertyChange(#tree, null, tree);
173 } 226 }
174 227
175 void _buildTree() { 228 void _buildTree() {
176 _buildStackTree(); 229 _buildStackTree();
177 } 230 }
178
179 @observable String padding(TableTreeRow row) {
180 return 'padding-left: ${row.depth * 16}px;';
181 }
182
183 @observable String coloring(TableTreeRow row) {
184 const colors = const ['rowColor0', 'rowColor1', 'rowColor2', 'rowColor3',
185 'rowColor4', 'rowColor5', 'rowColor6', 'rowColor7',
186 'rowColor8'];
187 var index = (row.depth - 1) % colors.length;
188 return colors[index];
189 }
190
191 @observable void toggleExpanded(Event e, var detail, Element target) {
192 // We only want to expand a tree row if the target of the click is
193 // the table cell (passed in as target) or the span containing the
194 // expander symbol (#expand).
195 var eventTarget = e.target;
196 if ((eventTarget.id != 'expand') && (e.target != target)) {
197 // Target of click was not the expander span or the table cell.
198 return;
199 }
200 var row = target.parent;
201 if (row is TableRowElement) {
202 // Subtract 1 to get 0 based indexing.
203 try {
204 tree.toggle(row.rowIndex - 1);
205 } catch (e, stackTrace) {
206 Logger.root.warning('toggleExpanded', e, stackTrace);
207 }
208 }
209 }
210 } 231 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698