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

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

Issue 928833003: Add Function based profile tree (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 10 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:html'; 8 import 'dart:html';
8 import 'observatory_element.dart'; 9 import 'observatory_element.dart';
9 import 'package:logging/logging.dart'; 10 import 'package:logging/logging.dart';
10 import 'package:observatory/service.dart'; 11 import 'package:observatory/service.dart';
11 import 'package:observatory/app.dart'; 12 import 'package:observatory/app.dart';
13 import 'package:observatory/cpu_profile.dart';
12 import 'package:observatory/elements.dart'; 14 import 'package:observatory/elements.dart';
13 import 'package:polymer/polymer.dart'; 15 import 'package:polymer/polymer.dart';
14 16
15 class ProfileCodeTrieNodeTreeRow extends TableTreeRow { 17 class ProfileCodeTrieNodeTreeRow extends TableTreeRow {
16 final ServiceMap profile; 18 final CpuProfile profile;
17 @reflectable final CodeTrieNode root; 19 @reflectable final CodeTrieNode root;
18 @reflectable final CodeTrieNode node; 20 @reflectable final CodeTrieNode node;
19 @reflectable Code get code => node.code; 21 @reflectable Code get code => node.profileCode.code;
20 22
21 @reflectable String tipKind = ''; 23 @reflectable String tipKind = '';
22 @reflectable String tipParent = ''; 24 @reflectable String tipParent = '';
23 @reflectable String tipExclusive = ''; 25 @reflectable String tipExclusive = '';
24 @reflectable String tipTicks = ''; 26 @reflectable String tipTicks = '';
25 @reflectable String tipTime = ''; 27 @reflectable String tipTime = '';
26 28
27 ProfileCodeTrieNodeTreeRow(this.profile, this.root, this.node, 29 ProfileCodeTrieNodeTreeRow(this.profile, this.root, this.node,
28 TableTree tree, 30 TableTree tree,
29 ProfileCodeTrieNodeTreeRow parent) 31 ProfileCodeTrieNodeTreeRow parent)
30 : super(tree, parent) { 32 : super(tree, parent) {
31 assert(root != null); 33 assert(root != null);
32 assert(node != null); 34 assert(node != null);
33 tipTicks = '${node.count}'; 35 tipTicks = '${node.count}';
34 var period = profile['period']; 36 var seconds = profile.approximateSecondsForCount(node.count);
35 var MICROSECONDS_PER_SECOND = 1000000.0;
36 var seconds = (period * node.count) / MICROSECONDS_PER_SECOND; // seconds
37 tipTime = Utils.formatTimePrecise(seconds); 37 tipTime = Utils.formatTimePrecise(seconds);
38 if (code.kind == CodeKind.Tag) { 38 if (code.kind == CodeKind.Tag) {
39 tipKind = 'Tag (category)'; 39 tipKind = 'Tag (category)';
40 if (parent == null) { 40 if (parent == null) {
41 tipParent = Utils.formatPercent(node.count, root.count); 41 tipParent = Utils.formatPercent(node.count, root.count);
42 } else { 42 } else {
43 tipParent = Utils.formatPercent(node.count, parent.node.count); 43 tipParent = Utils.formatPercent(node.count, parent.node.count);
44 } 44 }
45 tipExclusive = Utils.formatPercent(node.count, root.count); 45 tipExclusive = Utils.formatPercent(node.count, root.count);
46 } else { 46 } else {
47 if ((code.kind == CodeKind.Collected) || 47 if ((code.kind == CodeKind.Collected) ||
48 (code.kind == CodeKind.Reused)) { 48 (code.kind == CodeKind.Reused)) {
49 tipKind = 'Garbage Collected Code'; 49 tipKind = 'Garbage Collected Code';
50 } else { 50 } else {
51 tipKind = '${code.kind} (Function)'; 51 tipKind = '${code.kind} (Function)';
52 } 52 }
53 if (parent == null) { 53 if (parent == null) {
54 tipParent = Utils.formatPercent(node.count, root.count); 54 tipParent = Utils.formatPercent(node.count, root.count);
55 } else { 55 } else {
56 tipParent = Utils.formatPercent(node.count, parent.node.count); 56 tipParent = Utils.formatPercent(node.count, parent.node.count);
57 } 57 }
58 tipExclusive = Utils.formatPercent(node.code.exclusiveTicks, root.count); 58 tipExclusive =
59 Utils.formatPercent(node.profileCode.exclusiveTicks, root.count);
59 } 60 }
60 } 61 }
61 62
62 bool shouldDisplayChild(CodeTrieNode childNode, double threshold) { 63 bool shouldDisplayChild(CodeTrieNode childNode, double threshold) {
63 return ((childNode.count / node.count) > threshold) || 64 return ((childNode.count / node.count) > threshold) ||
64 ((childNode.code.exclusiveTicks / root.count) > threshold); 65 ((childNode.profileCode.exclusiveTicks / root.count) > threshold);
65 } 66 }
66 67
67 void _buildTooltip(DivElement memberList, Map<String, String> items) { 68 void _buildTooltip(DivElement memberList, Map<String, String> items) {
68 items.forEach((k, v) { 69 items.forEach((k, v) {
69 var item = new DivElement(); 70 var item = new DivElement();
70 item.classes.add('memberItem'); 71 item.classes.add('memberItem');
71 var name = new DivElement(); 72 var name = new DivElement();
72 name.classes.add('memberName'); 73 name.classes.add('memberName');
73 name.classes.add('white'); 74 name.classes.add('white');
74 name.text = k; 75 name.text = k;
75 var value = new DivElement(); 76 var value = new DivElement();
76 value.classes.add('memberValue'); 77 value.classes.add('memberValue');
77 value.classes.add('white'); 78 value.classes.add('white');
78 value.text = v; 79 value.text = v;
79 item.children.add(name); 80 item.children.add(name);
80 item.children.add(value); 81 item.children.add(value);
81 memberList.children.add(item); 82 memberList.children.add(item);
82 }); 83 });
83 } 84 }
84 85
85 void onShow() { 86 void onShow() {
86 super.onShow(); 87 super.onShow();
87 if (children.length == 0) { 88 if (children.length == 0) {
88 var threshold = profile['threshold']; 89 var threshold = profile.displayThreshold;
89 for (var childNode in node.children) { 90 for (var childNode in node.children) {
90 if (!shouldDisplayChild(childNode, threshold)) { 91 if (!shouldDisplayChild(childNode, threshold)) {
91 continue; 92 continue;
92 } 93 }
93 var row = 94 var row =
94 new ProfileCodeTrieNodeTreeRow(profile, root, childNode, tree, this) ; 95 new ProfileCodeTrieNodeTreeRow(profile, root, childNode, tree, this);
95 children.add(row); 96 children.add(row);
96 } 97 }
97 } 98 }
98 var row = tr;
99 99
100 var methodCell = tableColumns[0]; 100 var methodCell = tableColumns[0];
101 // Enable expansion by clicking anywhere on the method column. 101 // Enable expansion by clicking anywhere on the method column.
102 methodCell.onClick.listen(onClick); 102 methodCell.onClick.listen(onClick);
103 103
104 // Grab the flex-row Div inside the methodCell.
105 methodCell = methodCell.children[0];
106
104 // Insert the parent percentage 107 // Insert the parent percentage
105 var parentPercent = new DivElement(); 108 var parentPercent = new DivElement();
106 parentPercent.style.position = 'relative';
107 parentPercent.style.display = 'inline';
108 parentPercent.text = tipParent; 109 parentPercent.text = tipParent;
109 methodCell.children.add(parentPercent); 110 methodCell.children.add(parentPercent);
110 111
112 var gap = new SpanElement();
113 gap.style.minWidth = '1em';
114 methodCell.children.add(gap);
115
111 var codeRef = new Element.tag('code-ref'); 116 var codeRef = new Element.tag('code-ref');
112 codeRef.ref = code; 117 codeRef.ref = code;
113 methodCell.children.add(codeRef); 118 methodCell.children.add(codeRef);
114 119
115 var selfCell = tableColumns[1]; 120 var selfCell = tableColumns[1];
116 selfCell.style.position = 'relative'; 121 selfCell.style.position = 'relative';
117 selfCell.text = tipExclusive; 122 selfCell.text = tipExclusive;
118 123
119 var tooltipDiv = new DivElement(); 124 var tooltipDiv = new DivElement();
120 tooltipDiv.classes.add('tooltip'); 125 tooltipDiv.classes.add('tooltip');
121 126
122 var memberListDiv = new DivElement(); 127 var memberListDiv = new DivElement();
123 memberListDiv.classes.add('memberList'); 128 memberListDiv.classes.add('memberList');
124 tooltipDiv.children.add(memberListDiv); 129 tooltipDiv.children.add(memberListDiv);
125 _buildTooltip(memberListDiv, { 130 _buildTooltip(memberListDiv, {
126 'Kind' : tipKind, 131 'Kind' : tipKind,
127 'Percent of Parent' : tipParent, 132 'Percent of Parent' : tipParent,
128 'Sample Count' : tipTicks, 133 'Sample Count' : tipTicks,
129 'Approximate Execution Time': tipTime, 134 'Approximate Execution Time': tipTime,
130 }); 135 });
131 selfCell.children.add(tooltipDiv); 136 selfCell.children.add(tooltipDiv);
132 } 137 }
133 138
134 bool hasChildren() { 139 bool hasChildren() {
135 return node.children.length > 0; 140 return node.children.length > 0;
136 } 141 }
137 } 142 }
138 143
144 class ProfileFunctionTrieNodeTreeRow extends TableTreeRow {
145 final CpuProfile profile;
146 @reflectable final FunctionTrieNode root;
147 @reflectable final FunctionTrieNode node;
148 ProfileFunction get profileFunction => node.profileFunction;
149 @reflectable ServiceFunction get function => node.profileFunction.function;
150 @reflectable String tipKind = '';
151 @reflectable String tipParent = '';
152 @reflectable String tipExclusive = '';
153 @reflectable String tipTime = '';
154 @reflectable String tipTicks = '';
155
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 if (function.kind == FunctionKind.kTag) {
187 tipOptimized = 'N/A';
188 } else {
189 tipOptimized =
190 Utils.formatPercent(node.profileFunction.inclusiveOptimizedTicks,
191 node.profileFunction.inclusiveTicks);
192 }
193 }
194
195 bool hasChildren() {
196 return node.children.length > 0;
197 }
198
199 void _buildTooltip(DivElement memberList, Map<String, String> items) {
200 items.forEach((k, v) {
201 var item = new DivElement();
202 item.classes.add('memberItem');
203 var name = new DivElement();
204 name.classes.add('memberName');
205 name.classes.add('white');
206 name.text = k;
207 var value = new DivElement();
208 value.classes.add('memberValue');
209 value.classes.add('white');
210 value.text = v;
211 item.children.add(name);
212 item.children.add(value);
213 memberList.children.add(item);
214 });
215 }
216
217 void onShow() {
218 super.onShow();
219 if (children.length == 0) {
220 for (var childNode in node.children) {
221 var row = new ProfileFunctionTrieNodeTreeRow(profile,
222 root,
223 childNode, tree, this);
224 children.add(row);
225 }
226 }
227
228 var methodCell = tableColumns[0];
229 // Enable expansion by clicking anywhere on the method column.
230 methodCell.onClick.listen(onClick);
231
232 // Grab the flex-row Div inside the methodCell.
233 methodCell = methodCell.children[0];
234
235 // Insert the parent percentage
236 var parentPercent = new DivElement();
237 parentPercent.text = tipParent;
238 methodCell.children.add(parentPercent);
239
240 var gap = new SpanElement();
241 gap.style.minWidth = '1em';
242 methodCell.children.add(gap);
243
244 var functionAndCodeContainer = new DivElement();
245 methodCell.children.add(functionAndCodeContainer);
246
247 var functionRef = new Element.tag('function-ref');
248 functionRef.ref = function;
249 functionAndCodeContainer.children.add(functionRef);
250
251 var codeRow = new DivElement();
252 codeRow.style.paddingTop = '1em';
253 functionAndCodeContainer.children.add(codeRow);
254 if (!function.kind.isSynthetic()) {
255 var totalInclusiveTicks = profileFunction.inclusiveTicks;
256 for (var code in profileFunction.profileCodes) {
257 var codeInclusiveTicks = code.inclusiveTicks;
258 var inclusivePercentage =
259 Utils.formatPercent(codeInclusiveTicks, totalInclusiveTicks);
260 var percentageSpan = new SpanElement();
261 percentageSpan.text = '($inclusivePercentage) ';
262 codeRow.children.add(percentageSpan);
263 var codeRef = new Element.tag('code-ref');
264 codeRef.ref = code.code;
265 codeRow.children.add(codeRef);
266 }
267 }
268
269 var selfCell = tableColumns[1];
270 selfCell.style.position = 'relative';
271 selfCell.text = tipExclusive;
272
273 var tooltipDiv = new DivElement();
274 tooltipDiv.classes.add('tooltip');
275
276 var memberListDiv = new DivElement();
277 memberListDiv.classes.add('memberList');
278 tooltipDiv.children.add(memberListDiv);
279 _buildTooltip(memberListDiv, {
280 'Kind' : tipKind,
281 'Percent of Parent' : tipParent,
282 'Sample Count' : tipTicks,
283 'Approximate Execution Time': tipTime,
284 'Optimized Execution Percentage': tipOptimized,
285 });
286 selfCell.children.add(tooltipDiv);
287 }
288 }
289
139 /// Displays a CpuProfile 290 /// Displays a CpuProfile
140 @CustomTag('cpu-profile') 291 @CustomTag('cpu-profile')
141 class CpuProfileElement extends ObservatoryElement { 292 class CpuProfileElement extends ObservatoryElement {
142 CpuProfileElement.created() : super.created(); 293 static const MICROSECONDS_PER_SECOND = 1000000.0;
294
143 @published Isolate isolate; 295 @published Isolate isolate;
144
145 @observable ServiceMap profile;
146 @observable bool hideTagsChecked;
147 @observable String sampleCount = ''; 296 @observable String sampleCount = '';
148 @observable String refreshTime = ''; 297 @observable String refreshTime = '';
149 @observable String sampleRate = ''; 298 @observable String sampleRate = '';
150 @observable String sampleDepth = ''; 299 @observable String stackDepth = '';
151 @observable String displayCutoff = ''; 300 @observable String displayCutoff = '';
152 @observable String timeSpan = ''; 301 @observable String timeSpan = '';
153 @reflectable double displayThreshold = 0.0002; // 0.02%.
154 302
155 @observable String tagSelector = 'UserVM'; 303 @observable String tagSelector = 'UserVM';
156 304 @observable String modeSelector = 'Function';
157 final _id = '#tableTree'; 305
158 TableTree tree; 306 final CpuProfile profile = new CpuProfile();
159 307
160 static const MICROSECONDS_PER_SECOND = 1000000.0; 308 CpuProfileElement.created() : super.created();
161
162 void isolateChanged(oldValue) {
163 if (isolate == null) {
164 profile = null;
165 return;
166 }
167 isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector })
168 .then((ServiceObject obj) {
169 print(obj);
170 // Assert we got back the a profile.
171 assert(obj.type == 'CpuProfile');
172 profile = obj;
173 _update();
174 });
175 }
176 309
177 @override 310 @override
178 void attached() { 311 void attached() {
179 super.attached(); 312 super.attached();
180 var tableBody = shadowRoot.querySelector('#tableTreeBody'); 313 }
181 assert(tableBody != null); 314
182 tree = new TableTree(tableBody, 2); 315 void isolateChanged(oldValue) {
183 _update(); 316 _getCpuProfile();
184 } 317 }
185 318
186 void tagSelectorChanged(oldValue) { 319 void tagSelectorChanged(oldValue) {
187 isolateChanged(null); 320 _getCpuProfile();
321 }
322
323 void modeSelectorChanged(oldValue) {
324 _getCpuProfile();
188 } 325 }
189 326
190 void refresh(var done) { 327 void refresh(var done) {
191 isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector }) 328 _getCpuProfile().whenComplete(done);
192 .then((ServiceObject obj) { 329 }
193 // Assert we got back the a profile. 330
194 assert(obj.type == 'CpuProfile'); 331 Future _getCpuProfile() {
195 profile = obj; 332 profile.clear();
196 _update(); 333 if (isolate == null) {
197 }).whenComplete(done); 334 return new Future.value(null);
198 } 335 }
199 336 return isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector })
200 void _update() { 337 .then((ServiceMap response) {
201 if (profile == null) { 338 profile.load(isolate, response);
202 return; 339 _updateView();
203 } 340 });
204 var totalSamples = profile['samples']; 341 }
205 var now = new DateTime.now(); 342
206 sampleCount = totalSamples.toString(); 343 void _updateView() {
207 refreshTime = now.toString(); 344 sampleCount = profile.sampleCount.toString();
208 sampleDepth = profile['depth'].toString(); 345 refreshTime = new DateTime.now().toString();
209 var period = profile['period']; 346 stackDepth = profile.stackDepth.toString();
210 sampleRate = (MICROSECONDS_PER_SECOND / period).toStringAsFixed(0); 347 sampleRate = profile.sampleRate.toStringAsFixed(0);
211 timeSpan = formatTime(profile['timeSpan']); 348 timeSpan = formatTime(profile.timeSpan);
212 displayCutoff = '${(displayThreshold * 100.0).toString()}%'; 349 displayCutoff = '${(profile.displayThreshold * 100.0).toString()}%';
213 profile.isolate.processProfile(profile); 350 if (functionTree != null) {
214 profile['threshold'] = displayThreshold; 351 functionTree.clear();
215 _buildTree(); 352 }
216 } 353 if (codeTree != null) {
217 354 codeTree.clear();
218 void _buildStackTree() { 355 }
219 var root = profile.isolate.profileTrieRoot; 356 if (modeSelector == 'Code') {
357 _buildCodeTree();
358 } else {
359 _buildFunctionTree();
360 }
361 }
362
363 TableTree codeTree;
364 TableTree functionTree;
365
366 void _buildFunctionTree() {
367 if (functionTree == null) {
368 var tableBody = shadowRoot.querySelector('#treeBody');
369 assert(tableBody != null);
370 functionTree = new TableTree(tableBody, 2);
371 }
372 var root = profile.functionTrieRoot;
220 if (root == null) { 373 if (root == null) {
221 return; 374 return;
222 } 375 }
223 try { 376 try {
224 tree.initialize( 377 functionTree.initialize(
225 new ProfileCodeTrieNodeTreeRow(profile, root, root, tree, null)); 378 new ProfileFunctionTrieNodeTreeRow(profile,
379 root, root, functionTree, null));
226 } catch (e, stackTrace) { 380 } catch (e, stackTrace) {
227 print(e); 381 print(e);
228 print(stackTrace); 382 print(stackTrace);
229 Logger.root.warning('_buildStackTree', e, stackTrace); 383 Logger.root.warning('_buildFunctionTree', e, stackTrace);
230 } 384 }
231 // Check if we only have one node at the root and expand it. 385 // Check if we only have one node at the root and expand it.
232 if (tree.rows.length == 1) { 386 if (functionTree.rows.length == 1) {
233 tree.toggle(tree.rows[0]); 387 functionTree.toggle(functionTree.rows[0]);
234 } 388 }
235 notifyPropertyChange(#tree, null, tree); 389 }
236 } 390
237 391 void _buildCodeTree() {
238 void _buildTree() { 392 if (codeTree == null) {
239 _buildStackTree(); 393 var tableBody = shadowRoot.querySelector('#treeBody');
394 assert(tableBody != null);
395 codeTree = new TableTree(tableBody, 2);
396 }
397 var root = profile.codeTrieRoot;
398 if (root == null) {
399 return;
400 }
401 try {
402 codeTree.initialize(
403 new ProfileCodeTrieNodeTreeRow(profile, root, root, codeTree, null));
404 } catch (e, stackTrace) {
405 print(e);
406 print(stackTrace);
407 Logger.root.warning('_buildCodeTree', e, stackTrace);
408 }
409 // Check if we only have one node at the root and expand it.
410 if (codeTree.rows.length == 1) {
411 codeTree.toggle(codeTree.rows[0]);
412 }
240 } 413 }
241 } 414 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698