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

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

Issue 965593002: Improved profiler view and inclusive profile tree (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 9 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 cpu_profile_element; 5 library cpu_profile_element;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 import 'dart:html'; 8 import 'dart:html';
9 import 'observatory_element.dart'; 9 import 'observatory_element.dart';
10 import 'package:logging/logging.dart'; 10 import 'package:logging/logging.dart';
11 import 'package:observatory/service.dart'; 11 import 'package:observatory/service.dart';
12 import 'package:observatory/app.dart'; 12 import 'package:observatory/app.dart';
13 import 'package:observatory/cpu_profile.dart'; 13 import 'package:observatory/cpu_profile.dart';
14 import 'package:observatory/elements.dart'; 14 import 'package:observatory/elements.dart';
15 import 'package:polymer/polymer.dart'; 15 import 'package:polymer/polymer.dart';
16 16
17 class ProfileCodeTrieNodeTreeRow extends TableTreeRow { 17 List<String> sorted(Set<String> attributes) {
18 var list = attributes.toList();
19 list.sort();
20 return list;
21 }
22
23 abstract class ProfileTreeRow<T> extends TableTreeRow {
18 final CpuProfile profile; 24 final CpuProfile profile;
19 @reflectable final CodeTrieNode root; 25 final T node;
20 @reflectable final CodeTrieNode node; 26 final String selfPercent;
21 @reflectable Code get code => node.profileCode.code; 27 final String percent;
28 bool _infoBoxShown = false;
29 HtmlElement infoBox;
30 HtmlElement infoButton;
22 31
23 @reflectable String tipKind = ''; 32 ProfileTreeRow(TableTree tree, TableTreeRow parent,
24 @reflectable String tipParent = ''; 33 this.profile, this.node, double selfPercent, double percent)
25 @reflectable String tipExclusive = ''; 34 : super(tree, parent),
26 @reflectable String tipTicks = ''; 35 selfPercent = Utils.formatPercentNormalized(selfPercent),
27 @reflectable String tipTime = ''; 36 percent = Utils.formatPercentNormalized(percent);
28 37
29 ProfileCodeTrieNodeTreeRow(this.profile, this.root, this.node, 38 static _addToMemberList(DivElement memberList, Map<String, String> items) {
30 TableTree tree,
31 ProfileCodeTrieNodeTreeRow parent)
32 : super(tree, parent) {
33 assert(root != null);
34 assert(node != null);
35 tipTicks = '${node.count}';
36 var seconds = profile.approximateSecondsForCount(node.count);
37 tipTime = Utils.formatTimePrecise(seconds);
38 if (code.kind == CodeKind.Tag) {
39 tipKind = 'Tag (category)';
40 if (parent == null) {
41 tipParent = Utils.formatPercent(node.count, root.count);
42 } else {
43 tipParent = Utils.formatPercent(node.count, parent.node.count);
44 }
45 tipExclusive = Utils.formatPercent(node.count, root.count);
46 } else {
47 if ((code.kind == CodeKind.Collected) ||
48 (code.kind == CodeKind.Reused)) {
49 tipKind = 'Garbage Collected Code';
50 } else {
51 tipKind = '${code.kind} (Function)';
52 }
53 if (parent == null) {
54 tipParent = Utils.formatPercent(node.count, root.count);
55 } else {
56 tipParent = Utils.formatPercent(node.count, parent.node.count);
57 }
58 tipExclusive =
59 Utils.formatPercent(node.profileCode.exclusiveTicks, root.count);
60 }
61 }
62
63 bool shouldDisplayChild(CodeTrieNode childNode, double threshold) {
64 return ((childNode.count / node.count) > threshold) ||
65 ((childNode.profileCode.exclusiveTicks / root.count) > threshold);
66 }
67
68 void _buildTooltip(DivElement memberList, Map<String, String> items) {
69 items.forEach((k, v) { 39 items.forEach((k, v) {
70 var item = new DivElement(); 40 var item = new DivElement();
71 item.classes.add('memberItem'); 41 item.classes.add('memberItem');
72 var name = new DivElement(); 42 var name = new DivElement();
73 name.classes.add('memberName'); 43 name.classes.add('memberName');
74 name.classes.add('white');
75 name.text = k; 44 name.text = k;
76 var value = new DivElement(); 45 var value = new DivElement();
77 value.classes.add('memberValue'); 46 value.classes.add('memberValue');
78 value.classes.add('white');
79 value.text = v; 47 value.text = v;
80 item.children.add(name); 48 item.children.add(name);
81 item.children.add(value); 49 item.children.add(value);
82 memberList.children.add(item); 50 memberList.children.add(item);
83 }); 51 });
84 } 52 }
85 53
54 makeInfoBox() {
55 if (infoBox != null) {
56 return;
57 }
58 infoBox = new DivElement();
59 infoBox.classes.add('infoBox');
60 infoBox.classes.add('shadow');
61 infoBox.style.display = 'none';
62 infoBox.onClick.listen((e) => e.stopPropagation());
63 }
64
65 makeInfoButton() {
66 infoButton = new SpanElement();
67 infoButton.style.marginLeft = 'auto';
68 infoButton.style.marginRight = '1em';
69 infoButton.children.add(new Element.tag('icon-info-outline'));
70 infoButton.onClick.listen((event) {
71 event.stopPropagation();
72 toggleInfoBox();
73 });
74 }
75
76 static const attributes = const {
77 'optimized' : const ['O', null, 'Optimized'],
78 'unoptimized' : const ['U', null, 'Unoptimized'],
79 'inlined' : const ['I', null, 'Inlined'],
80 'dart' : const ['D', null, 'Dart'],
81 'tag' : const ['T', null, 'Tag'],
82 'native' : const ['N', null, 'Native'],
83 'stub': const ['S', null, 'Stub'],
84 'synthetic' : const ['?', null, 'Synthetic'],
85 };
86
87 HtmlElement newAttributeBox(String attribute) {
88 List attributeDetails = attributes[attribute];
89 if (attributeDetails == null) {
90 print('could not find attribute $attribute');
91 return null;
92 }
93 var element = new SpanElement();
94 element.style.border = 'solid 2px #ECECEC';
95 element.style.height = '100%';
96 element.style.display = 'inline-block';
97 element.style.textAlign = 'center';
98 element.style.minWidth = '1.5em';
99 element.style.fontWeight = 'bold';
100 if (attributeDetails[1] != null) {
101 element.style.backgroundColor = attributeDetails[1];
102 }
103 element.text = attributeDetails[0];
104 element.title = attributeDetails[2];
105 return element;
106 }
107
108 onHide() {
109 super.onHide();
110 infoBox = null;
111 infoButton = null;
112 }
113
114 showInfoBox() {
115 if ((infoButton == null) || (infoBox == null)) {
116 return;
117 }
118 _infoBoxShown = true;
119 infoBox.style.display = 'block';
120 infoButton.children.clear();
121 infoButton.children.add(new Element.tag('icon-info'));
122 }
123
124 hideInfoBox() {
125 _infoBoxShown = false;
126 if ((infoButton == null) || (infoBox == null)) {
127 return;
128 }
129 infoBox.style.display = 'none';
130 infoButton.children.clear();
131 infoButton.children.add(new Element.tag('icon-info-outline'));
132 }
133
134 toggleInfoBox() {
135 if (_infoBoxShown) {
136 hideInfoBox();
137 } else {
138 showInfoBox();
139 }
140 }
141
142 hideAllInfoBoxes() {
143 final List<ProfileTreeRow> rows = tree.rows;
144 for (var row in rows) {
145 row.hideInfoBox();
146 }
147 }
148
149 onClick(MouseEvent e) {
150 e.stopPropagation();
151 if (e.altKey) {
152 bool show = !_infoBoxShown;
153 hideAllInfoBoxes();
154 if (show) {
155 showInfoBox();
156 }
157 return;
158 }
159 super.onClick(e);
160 }
161
162 HtmlElement newCodeRef(ProfileCode code) {
163 var codeRef = new Element.tag('code-ref');
164 codeRef.ref = code.code;
165 return codeRef;
166 }
167
168 HtmlElement newFunctionRef(ProfileFunction function) {
169 var ref = new Element.tag('function-ref');
170 ref.ref = function.function;
171 return ref;
172 }
173
174 HtmlElement hr() {
175 var element = new HRElement();
176 return element;
177 }
178
179 HtmlElement div(String text) {
180 var element = new DivElement();
181 element.text = text;
182 return element;
183 }
184
185 HtmlElement br() {
186 return new BRElement();
187 }
188
189 HtmlElement span(String text) {
190 var element = new SpanElement();
191 element.style.minWidth = '1em';
192 element.text = text;
193 return element;
194 }
195 }
196
197 class CodeProfileTreeRow extends ProfileTreeRow<CodeCallTreeNode> {
198 CodeProfileTreeRow(TableTree tree, CodeProfileTreeRow parent,
199 CpuProfile profile, CodeCallTreeNode node)
200 : super(tree, parent, profile, node,
201 node.profileCode.normalizedExclusiveTicks,
202 node.percentage) {
203 // fill out attributes.
204 }
205
206 bool hasChildren() => node.children.length > 0;
207
86 void onShow() { 208 void onShow() {
87 super.onShow(); 209 super.onShow();
210
88 if (children.length == 0) { 211 if (children.length == 0) {
89 var threshold = profile.displayThreshold;
90 for (var childNode in node.children) { 212 for (var childNode in node.children) {
91 if (!shouldDisplayChild(childNode, threshold)) { 213 var row = new CodeProfileTreeRow(tree, this, profile, childNode);
92 continue;
93 }
94 var row =
95 new ProfileCodeTrieNodeTreeRow(profile, root, childNode, tree, this);
96 children.add(row); 214 children.add(row);
97 } 215 }
98 } 216 }
99 217
100 var methodCell = tableColumns[0]; 218 // Fill in method column.
101 // Enable expansion by clicking anywhere on the method column. 219 var methodColumn = flexColumns[0];
102 methodCell.onClick.listen(onClick); 220 methodColumn.style.justifyContent = 'flex-start';
103 221 methodColumn.style.position = 'relative';
104 // Grab the flex-row Div inside the methodCell. 222
105 methodCell = methodCell.children[0]; 223 // Percent.
106 224 var percentNode = new DivElement();
107 // Insert the parent percentage 225 percentNode.text = percent;
108 var parentPercent = new DivElement(); 226 percentNode.style.minWidth = '5em';
109 parentPercent.text = tipParent; 227 percentNode.style.textAlign = 'right';
110 methodCell.children.add(parentPercent); 228 percentNode.title = 'Self: $selfPercent';
111 229 methodColumn.children.add(percentNode);
230
231 // Gap.
112 var gap = new SpanElement(); 232 var gap = new SpanElement();
113 gap.style.minWidth = '1em'; 233 gap.style.minWidth = '1em';
114 methodCell.children.add(gap); 234 methodColumn.children.add(gap);
115 235
116 var codeRef = new Element.tag('code-ref'); 236 // Code link.
117 codeRef.ref = code; 237 var codeRef = newCodeRef(node.profileCode);
118 methodCell.children.add(codeRef); 238 codeRef.style.alignSelf = 'center';
119 239 methodColumn.children.add(codeRef);
120 var selfCell = tableColumns[1]; 240
121 selfCell.style.position = 'relative'; 241 gap = new SpanElement();
122 selfCell.text = tipExclusive; 242 gap.style.minWidth = '1em';
123 243 methodColumn.children.add(gap);
124 var tooltipDiv = new DivElement(); 244
125 tooltipDiv.classes.add('tooltip'); 245 for (var attribute in sorted(node.attributes)) {
126 246 methodColumn.children.add(newAttributeBox(attribute));
127 var memberListDiv = new DivElement(); 247 }
128 memberListDiv.classes.add('memberList'); 248
129 tooltipDiv.children.add(memberListDiv); 249 makeInfoBox();
130 _buildTooltip(memberListDiv, { 250 methodColumn.children.add(infoBox);
131 'Kind' : tipKind, 251
132 'Percent of Parent' : tipParent, 252 infoBox.children.add(span('Code '));
133 'Sample Count' : tipTicks, 253 infoBox.children.add(newCodeRef(node.profileCode));
134 'Approximate Execution Time': tipTime, 254 infoBox.children.add(span(' '));
255 for (var attribute in sorted(node.profileCode.attributes)) {
256 infoBox.children.add(newAttributeBox(attribute));
257 }
258 infoBox.children.add(br());
259 infoBox.children.add(br());
260 var memberList = new DivElement();
261 memberList.classes.add('memberList');
262 infoBox.children.add(br());
263 infoBox.children.add(memberList);
264 ProfileTreeRow._addToMemberList(memberList, {
265 'Exclusive ticks' : node.profileCode.formattedExclusiveTicks,
266 'Cpu time' : node.profileCode.formattedCpuTime,
267 'Inclusive ticks' : node.profileCode.formattedInclusiveTicks,
268 'Call stack time' : node.profileCode.formattedOnStackTime,
135 }); 269 });
136 selfCell.children.add(tooltipDiv); 270
137 } 271 makeInfoButton();
138 272 methodColumn.children.add(infoButton);
139 bool hasChildren() { 273
140 return node.children.length > 0; 274 // Fill in self column.
275 var selfColumn = flexColumns[1];
276 selfColumn.style.position = 'relative';
277 selfColumn.style.alignItems = 'center';
278 selfColumn.text = selfPercent;
141 } 279 }
142 } 280 }
143 281
144 class ProfileFunctionTrieNodeTreeRow extends TableTreeRow { 282 class FunctionProfileTreeRow extends ProfileTreeRow<FunctionCallTreeNode> {
145 final CpuProfile profile; 283 FunctionProfileTreeRow(TableTree tree, FunctionProfileTreeRow parent,
146 @reflectable final FunctionTrieNode root; 284 CpuProfile profile, FunctionCallTreeNode node)
147 @reflectable final FunctionTrieNode node; 285 : super(tree, parent, profile, node,
148 ProfileFunction get profileFunction => node.profileFunction; 286 node.profileFunction.normalizedExclusiveTicks,
149 @reflectable ServiceFunction get function => node.profileFunction.function; 287 node.percentage) {
150 @reflectable String tipKind = ''; 288 // fill out attributes.
151 @reflectable String tipParent = ''; 289 }
152 @reflectable String tipExclusive = ''; 290
153 @reflectable String tipTime = ''; 291 bool hasChildren() => node.children.length > 0;
154 @reflectable String tipTicks = ''; 292
155 293 onShow() {
156 String tipOptimized = '';
157
158 ProfileFunctionTrieNodeTreeRow(this.profile, this.root, this.node,
159 TableTree tree,
160 ProfileFunctionTrieNodeTreeRow parent)
161 : super(tree, parent) {
162 assert(root != null);
163 assert(node != null);
164 tipTicks = '${node.count}';
165 var seconds = profile.approximateSecondsForCount(node.count);
166 tipTime = Utils.formatTimePrecise(seconds);
167 if (parent == null) {
168 tipParent = Utils.formatPercent(node.count, root.count);
169 } else {
170 tipParent = Utils.formatPercent(node.count, parent.node.count);
171 }
172 if (function.kind == FunctionKind.kTag) {
173 tipExclusive = Utils.formatPercent(node.count, root.count);
174 } else {
175 tipExclusive =
176 Utils.formatPercent(node.profileFunction.exclusiveTicks, root.count);
177 }
178
179 if (function.kind == FunctionKind.kTag) {
180 tipKind = 'Tag (category)';
181 } else if (function.kind == FunctionKind.kCollected) {
182 tipKind = 'Garbage Collected Code';
183 } else {
184 tipKind = '${function.kind} (Function)';
185 }
186 }
187
188 bool hasChildren() {
189 return node.children.length > 0;
190 }
191
192 void _buildTooltip(DivElement memberList, Map<String, String> items) {
193 items.forEach((k, v) {
194 var item = new DivElement();
195 item.classes.add('memberItem');
196 var name = new DivElement();
197 name.classes.add('memberName');
198 name.classes.add('white');
199 name.text = k;
200 var value = new DivElement();
201 value.classes.add('memberValue');
202 value.classes.add('white');
203 value.text = v;
204 item.children.add(name);
205 item.children.add(value);
206 memberList.children.add(item);
207 });
208 }
209
210 void onShow() {
211 super.onShow(); 294 super.onShow();
212 if (children.length == 0) { 295 if (children.length == 0) {
213 for (var childNode in node.children) { 296 for (var childNode in node.children) {
214 var row = new ProfileFunctionTrieNodeTreeRow(profile, 297 var row = new FunctionProfileTreeRow(tree, this, profile, childNode);
215 root,
216 childNode, tree, this);
217 children.add(row); 298 children.add(row);
218 } 299 }
219 } 300 }
220 301
221 var selfCell = tableColumns[1]; 302 var methodColumn = flexColumns[0];
222 selfCell.style.position = 'relative'; 303 methodColumn.style.justifyContent = 'flex-start';
223 selfCell.text = tipExclusive; 304
224 305 var codeAndFunctionColumn = new DivElement();
225 var methodCell = tableColumns[0]; 306 codeAndFunctionColumn.classes.add('flex-column');
226 // Enable expansion by clicking anywhere on the method column. 307 codeAndFunctionColumn.style.justifyContent = 'center';
227 methodCell.onClick.listen(onClick); 308 codeAndFunctionColumn.style.width = '100%';
228 309 methodColumn.children.add(codeAndFunctionColumn);
229 // Grab the flex-row Div inside the methodCell. 310
230 methodCell = methodCell.children[0]; 311 var functionRow = new DivElement();
312 functionRow.classes.add('flex-row');
313 functionRow.style.position = 'relative';
314 functionRow.style.justifyContent = 'flex-start';
315 codeAndFunctionColumn.children.add(functionRow);
231 316
232 // Insert the parent percentage 317 // Insert the parent percentage
233 var parentPercent = new DivElement(); 318 var parentPercent = new SpanElement();
234 parentPercent.text = tipParent; 319 parentPercent.text = percent;
235 methodCell.children.add(parentPercent); 320 parentPercent.style.minWidth = '4em';
236 321 parentPercent.style.alignSelf = 'center';
322 parentPercent.style.textAlign = 'right';
323 parentPercent.title = 'Self: $selfPercent';
324 functionRow.children.add(parentPercent);
325
326 // Gap.
237 var gap = new SpanElement(); 327 var gap = new SpanElement();
238 gap.style.minWidth = '1em'; 328 gap.style.minWidth = '1em';
239 methodCell.children.add(gap); 329 gap.text = ' ';
240 330 functionRow.children.add(gap);
241 var functionAndCodeContainer = new DivElement();
242 methodCell.children.add(functionAndCodeContainer);
243 331
244 var functionRef = new Element.tag('function-ref'); 332 var functionRef = new Element.tag('function-ref');
245 functionRef.ref = function; 333 functionRef.ref = node.profileFunction.function;
246 functionAndCodeContainer.children.add(functionRef); 334 functionRef.style.alignSelf = 'center';
247 335 functionRow.children.add(functionRef);
248 var codeRow = new DivElement(); 336
249 codeRow.style.paddingTop = '1em'; 337 gap = new SpanElement();
250 functionAndCodeContainer.children.add(codeRow); 338 gap.style.minWidth = '1em';
251 if (!function.kind.isSynthetic()) { 339 gap.text = ' ';
252 340 functionRow.children.add(gap);
341
342 for (var attribute in sorted(node.attributes)) {
343 functionRow.children.add(newAttributeBox(attribute));
344 }
345
346 makeInfoBox();
347 functionRow.children.add(infoBox);
348
349 if (node.profileFunction.function.kind.hasDartCode()) {
350 infoBox.children.add(div('Hot code for current node'));
351 infoBox.children.add(br());
253 var totalTicks = node.totalCodesTicks; 352 var totalTicks = node.totalCodesTicks;
254 var numCodes = node.codes.length; 353 var numCodes = node.codes.length;
255 var label = new SpanElement();
256 label.text = 'Compiled into:\n';
257 codeRow.children.add(label);
258 var curlyBlock = new Element.tag('curly-block');
259 codeRow.children.add(curlyBlock);
260 for (var i = 0; i < numCodes; i++) { 354 for (var i = 0; i < numCodes; i++) {
261 var codeRowSpan = new DivElement(); 355 var codeRowSpan = new DivElement();
262 codeRowSpan.style.paddingLeft = '1em'; 356 codeRowSpan.style.paddingLeft = '1em';
263 curlyBlock.children.add(codeRowSpan); 357 infoBox.children.add(codeRowSpan);
264 var nodeCode = node.codes[i]; 358 var nodeCode = node.codes[i];
265 var ticks = nodeCode.ticks; 359 var ticks = nodeCode.ticks;
266 var percentage = Utils.formatPercent(ticks, totalTicks); 360 var percentage = Utils.formatPercent(ticks, totalTicks);
267 var percentageSpan = new SpanElement(); 361 var percentageSpan = new SpanElement();
268 percentageSpan.text = '($percentage) '; 362 percentageSpan.style.display = 'inline-block';
363 percentageSpan.text = '$percentage';
364 percentageSpan.style.minWidth = '5em';
365 percentageSpan.style.textAlign = 'right';
269 codeRowSpan.children.add(percentageSpan); 366 codeRowSpan.children.add(percentageSpan);
270 var codeRef = new Element.tag('code-ref'); 367 var codeRef = new Element.tag('code-ref');
271 codeRef.ref = nodeCode.code.code; 368 codeRef.ref = nodeCode.code.code;
369 codeRef.style.marginLeft = '1em';
370 codeRef.style.marginRight = 'auto';
371 codeRef.style.width = '100%';
272 codeRowSpan.children.add(codeRef); 372 codeRowSpan.children.add(codeRef);
273 } 373 }
274 } 374 infoBox.children.add(hr());
275 375 }
276 var tooltipDiv = new DivElement(); 376 infoBox.children.add(span('Function '));
277 tooltipDiv.classes.add('tooltip'); 377 infoBox.children.add(newFunctionRef(node.profileFunction));
278 378 infoBox.children.add(span(' '));
279 var memberListDiv = new DivElement(); 379 for (var attribute in sorted(node.profileFunction.attributes)) {
280 memberListDiv.classes.add('memberList'); 380 infoBox.children.add(newAttributeBox(attribute));
281 tooltipDiv.children.add(memberListDiv); 381 }
282 _buildTooltip(memberListDiv, { 382 var memberList = new DivElement();
283 'Kind' : tipKind, 383 memberList.classes.add('memberList');
284 'Percent of Parent' : tipParent, 384 infoBox.children.add(br());
285 'Sample Count' : tipTicks, 385 infoBox.children.add(br());
286 'Approximate Execution Time': tipTime, 386 infoBox.children.add(memberList);
387 infoBox.children.add(br());
388 ProfileTreeRow._addToMemberList(memberList, {
389 'Exclusive ticks' : node.profileFunction.formattedExclusiveTicks,
390 'Cpu time' : node.profileFunction.formattedCpuTime,
391 'Inclusive ticks' : node.profileFunction.formattedInclusiveTicks,
392 'Call stack time' : node.profileFunction.formattedOnStackTime,
287 }); 393 });
288 selfCell.children.add(tooltipDiv); 394
395 if (node.profileFunction.function.kind.hasDartCode()) {
396 infoBox.children.add(div('Hot code containing function'));
397 infoBox.children.add(br());
398 var totalTicks = profile.sampleCount;
399 var codes = node.profileFunction.profileCodes;
400 var numCodes = codes.length;
401 for (var i = 0; i < numCodes; i++) {
402 var codeRowSpan = new DivElement();
403 codeRowSpan.style.paddingLeft = '1em';
404 infoBox.children.add(codeRowSpan);
405 var profileCode = codes[i];
406 var code = profileCode.code;
407 var ticks = profileCode.inclusiveTicks;
408 var percentage = Utils.formatPercent(ticks, totalTicks);
409 var percentageSpan = new SpanElement();
410 percentageSpan.style.display = 'inline-block';
411 percentageSpan.text = '$percentage';
412 percentageSpan.style.minWidth = '5em';
413 percentageSpan.style.textAlign = 'right';
414 percentageSpan.title = 'Inclusive ticks';
415 codeRowSpan.children.add(percentageSpan);
416 var codeRef = new Element.tag('code-ref');
417 codeRef.ref = code;
418 codeRef.style.marginLeft = '1em';
419 codeRef.style.marginRight = 'auto';
420 codeRef.style.width = '100%';
421 codeRowSpan.children.add(codeRef);
422 }
423 }
424
425 makeInfoButton();
426 methodColumn.children.add(infoButton);
427
428 // Fill in self column.
429 var selfColumn = flexColumns[1];
430 selfColumn.style.position = 'relative';
431 selfColumn.style.alignItems = 'center';
432 selfColumn.text = selfPercent;
289 } 433 }
290 } 434 }
291 435
292 /// Displays a CpuProfile 436 /// Displays a CpuProfile
293 @CustomTag('cpu-profile') 437 @CustomTag('cpu-profile')
294 class CpuProfileElement extends ObservatoryElement { 438 class CpuProfileElement extends ObservatoryElement {
295 static const MICROSECONDS_PER_SECOND = 1000000.0; 439 static const MICROSECONDS_PER_SECOND = 1000000.0;
296 440
297 @published Isolate isolate; 441 @published Isolate isolate;
298 @observable String sampleCount = ''; 442 @observable String sampleCount = '';
299 @observable String refreshTime = ''; 443 @observable String refreshTime = '';
300 @observable String sampleRate = ''; 444 @observable String sampleRate = '';
301 @observable String stackDepth = ''; 445 @observable String stackDepth = '';
302 @observable String displayCutoff = '';
303 @observable String timeSpan = ''; 446 @observable String timeSpan = '';
304 447 @observable String fetchTime = '';
448 @observable String loadTime = '';
305 @observable String tagSelector = 'UserVM'; 449 @observable String tagSelector = 'UserVM';
306 @observable String modeSelector = 'Function'; 450 @observable String modeSelector = 'Function';
451 @observable String directionSelector = 'Up';
452
453 @observable String state = 'Requested';
454 @observable var exception;
455 @observable var stackTrace;
456
457 final Stopwatch _sw = new Stopwatch();
307 458
308 final CpuProfile profile = new CpuProfile(); 459 final CpuProfile profile = new CpuProfile();
309 460
310 CpuProfileElement.created() : super.created(); 461 CpuProfileElement.created() : super.created();
311 462
312 @override 463 @override
313 void attached() { 464 void attached() {
314 super.attached(); 465 super.attached();
315 } 466 }
316 467
317 void isolateChanged(oldValue) { 468 void isolateChanged(oldValue) {
318 _getCpuProfile(); 469 _getCpuProfile();
319 } 470 }
320 471
321 void tagSelectorChanged(oldValue) { 472 void tagSelectorChanged(oldValue) {
322 _getCpuProfile(); 473 _getCpuProfile();
323 } 474 }
324 475
325 void modeSelectorChanged(oldValue) { 476 void modeSelectorChanged(oldValue) {
326 _updateView(); 477 _updateView();
327 } 478 }
328 479
480 void directionSelectorChanged(oldValue) {
481 _updateView();
482 }
483
329 void clear(var done) { 484 void clear(var done) {
330 _clearCpuProfile().whenComplete(done); 485 _clearCpuProfile().whenComplete(done);
331 } 486 }
332 487
333 Future _clearCpuProfile() { 488 Future _clearCpuProfile() {
334 profile.clear(); 489 profile.clear();
335 if (isolate == null) { 490 if (isolate == null) {
336 return new Future.value(null); 491 return new Future.value(null);
337 } 492 }
338 return isolate.invokeRpc('clearCpuProfile', { }) 493 return isolate.invokeRpc('clearCpuProfile', { })
339 .then((ServiceMap response) { 494 .then((ServiceMap response) {
340 _updateView(); 495 _updateView();
341 }); 496 });
342 } 497 }
343 498
344 void refresh(var done) { 499 void refresh(var done) {
345 _getCpuProfile().whenComplete(done); 500 _getCpuProfile().whenComplete(done);
346 } 501 }
347 502
348 Future _getCpuProfile() { 503 _onFetchStarted() {
504 _sw.reset();
505 _sw.start();
506 state = 'Requested';
507 }
508
509 _onFetchFinished() {
510 _sw.stop();
511 fetchTime = formatTimeMilliseconds(_sw.elapsedMilliseconds);
512 }
513
514 _onLoadStarted() {
515 _sw.reset();
516 _sw.start();
517 state = 'Loading';
518 }
519
520 _onLoadFinished() {
521 _sw.stop();
522 loadTime = formatTimeMilliseconds(_sw.elapsedMilliseconds);
523 state = 'Loaded';
524 }
525
526 Future _getCpuProfile() async {
349 profile.clear(); 527 profile.clear();
528 if (functionTree != null) {
529 functionTree.clear();
530 }
531 if (codeTree != null) {
532 codeTree.clear();
533 }
350 if (isolate == null) { 534 if (isolate == null) {
351 return new Future.value(null); 535 return new Future.value(null);
352 } 536 }
353 return isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector }) 537 _onFetchStarted();
354 .then((ServiceMap response) { 538 var response =
355 profile.load(isolate, response); 539 await isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector });
356 _updateView(); 540 _onFetchFinished();
357 }); 541 _onLoadStarted();
542 await window.animationFrame;
543 try {
544 profile.load(isolate, response);
545 _onLoadFinished();
546 _updateView();
547 } catch (e, st) {
548 state = 'Exception';
549 exception = e;
550 stackTrace = st;
551 }
358 } 552 }
359 553
360 void _updateView() { 554 void _updateView() {
361 sampleCount = profile.sampleCount.toString(); 555 sampleCount = profile.sampleCount.toString();
362 refreshTime = new DateTime.now().toString(); 556 refreshTime = new DateTime.now().toString();
363 stackDepth = profile.stackDepth.toString(); 557 stackDepth = profile.stackDepth.toString();
364 sampleRate = profile.sampleRate.toStringAsFixed(0); 558 sampleRate = profile.sampleRate.toStringAsFixed(0);
365 timeSpan = formatTime(profile.timeSpan); 559 timeSpan = formatTime(profile.timeSpan);
366 displayCutoff = '${(profile.displayThreshold * 100.0).toString()}%';
367 if (functionTree != null) { 560 if (functionTree != null) {
368 functionTree.clear(); 561 functionTree.clear();
369 } 562 }
370 if (codeTree != null) { 563 if (codeTree != null) {
371 codeTree.clear(); 564 codeTree.clear();
372 } 565 }
566 bool exclusive = directionSelector == 'Up';
373 if (modeSelector == 'Code') { 567 if (modeSelector == 'Code') {
374 _buildCodeTree(); 568 _buildCodeTree(exclusive);
375 } else { 569 } else {
376 _buildFunctionTree(); 570 _buildFunctionTree(exclusive);
377 } 571 }
378 } 572 }
379 573
380 TableTree codeTree; 574 TableTree codeTree;
381 TableTree functionTree; 575 TableTree functionTree;
382 576
383 void _buildFunctionTree() { 577 void _buildFunctionTree(bool exclusive) {
384 if (functionTree == null) { 578 if (functionTree == null) {
385 var tableBody = shadowRoot.querySelector('#treeBody'); 579 var tableBody = shadowRoot.querySelector('#treeBody');
386 assert(tableBody != null); 580 assert(tableBody != null);
387 functionTree = new TableTree(tableBody, 2); 581 functionTree = new TableTree(tableBody, 2);
388 } 582 }
389 var root = profile.functionTrieRoot; 583 var tree = profile.functionTrees[exclusive ? 'exclusive' : 'inclusive'];
390 if (root == null) { 584 if (tree == null) {
391 return; 585 return;
392 } 586 }
393 try { 587 var rootRow =
394 functionTree.initialize( 588 new FunctionProfileTreeRow(functionTree, null, profile, tree.root);
395 new ProfileFunctionTrieNodeTreeRow(profile, 589 functionTree.initialize(rootRow);
396 root, root, functionTree, null));
397 } catch (e, stackTrace) {
398 print(e);
399 print(stackTrace);
400 Logger.root.warning('_buildFunctionTree', e, stackTrace);
401 }
402 // Check if we only have one node at the root and expand it.
403 if (functionTree.rows.length == 1) {
404 functionTree.toggle(functionTree.rows[0]);
405 }
406 } 590 }
407 591
408 void _buildCodeTree() { 592 void _buildCodeTree(bool exclusive) {
409 if (codeTree == null) { 593 if (codeTree == null) {
410 var tableBody = shadowRoot.querySelector('#treeBody'); 594 var tableBody = shadowRoot.querySelector('#treeBody');
411 assert(tableBody != null); 595 assert(tableBody != null);
412 codeTree = new TableTree(tableBody, 2); 596 codeTree = new TableTree(tableBody, 2);
413 } 597 }
414 var root = profile.codeTrieRoot; 598 var tree = profile.codeTrees[exclusive ? 'exclusive' : 'inclusive'];
415 if (root == null) { 599 if (tree == null) {
416 return; 600 return;
417 } 601 }
418 try { 602 var rootRow = new CodeProfileTreeRow(codeTree, null, profile, tree.root);
419 codeTree.initialize( 603 codeTree.initialize(rootRow);
420 new ProfileCodeTrieNodeTreeRow(profile, root, root, codeTree, null));
421 } catch (e, stackTrace) {
422 print(e);
423 print(stackTrace);
424 Logger.root.warning('_buildCodeTree', e, stackTrace);
425 }
426 // Check if we only have one node at the root and expand it.
427 if (codeTree.rows.length == 1) {
428 codeTree.toggle(codeTree.rows[0]);
429 }
430 } 604 }
431 } 605 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/elements/class_tree.html ('k') | runtime/observatory/lib/src/elements/cpu_profile.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698