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

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 }
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();
212 if (children.length == 0) {
213 for (var childNode in node.children) {
214 var row = new ProfileFunctionTrieNodeTreeRow(profile,
215 root,
216 childNode, tree, this);
217 children.add(row);
218 }
219 }
220
221 var selfCell = tableColumns[1];
222 selfCell.style.position = 'relative';
223 selfCell.text = tipExclusive;
224
225 var methodCell = tableColumns[0];
226 // Enable expansion by clicking anywhere on the method column.
227 methodCell.onClick.listen(onClick);
228
229 // Grab the flex-row Div inside the methodCell.
230 methodCell = methodCell.children[0];
231
232 // Insert the parent percentage
233 var parentPercent = new DivElement();
234 parentPercent.text = tipParent;
235 methodCell.children.add(parentPercent);
236
237 var gap = new SpanElement();
238 gap.style.minWidth = '1em';
239 methodCell.children.add(gap);
240
241 var functionAndCodeContainer = new DivElement();
242 methodCell.children.add(functionAndCodeContainer);
243
244 var functionRef = new Element.tag('function-ref');
245 functionRef.ref = function;
246 functionAndCodeContainer.children.add(functionRef);
247
248 var codeRow = new DivElement();
249 codeRow.style.paddingTop = '1em';
250 functionAndCodeContainer.children.add(codeRow);
251 if (!function.kind.isSynthetic()) {
252
253 var totalTicks = node.totalCodesTicks;
254 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++) {
261 var codeRowSpan = new DivElement();
262 codeRowSpan.style.paddingLeft = '1em';
263 curlyBlock.children.add(codeRowSpan);
264 var nodeCode = node.codes[i];
265 var ticks = nodeCode.ticks;
266 var percentage = Utils.formatPercent(ticks, totalTicks);
267 var percentageSpan = new SpanElement();
268 percentageSpan.text = '($percentage) ';
269 codeRowSpan.children.add(percentageSpan);
270 var codeRef = new Element.tag('code-ref');
271 codeRef.ref = nodeCode.code.code;
272 codeRowSpan.children.add(codeRef);
273 }
274 }
275
276 var tooltipDiv = new DivElement();
277 tooltipDiv.classes.add('tooltip');
278
279 var memberListDiv = new DivElement();
280 memberListDiv.classes.add('memberList');
281 tooltipDiv.children.add(memberListDiv);
282 _buildTooltip(memberListDiv, {
283 'Kind' : tipKind,
284 'Percent of Parent' : tipParent,
285 'Sample Count' : tipTicks,
286 'Approximate Execution Time': tipTime,
287 });
288 selfCell.children.add(tooltipDiv);
289 }
290 }
291
139 /// Displays a CpuProfile 292 /// Displays a CpuProfile
140 @CustomTag('cpu-profile') 293 @CustomTag('cpu-profile')
141 class CpuProfileElement extends ObservatoryElement { 294 class CpuProfileElement extends ObservatoryElement {
142 CpuProfileElement.created() : super.created(); 295 static const MICROSECONDS_PER_SECOND = 1000000.0;
296
143 @published Isolate isolate; 297 @published Isolate isolate;
144
145 @observable ServiceMap profile;
146 @observable bool hideTagsChecked;
147 @observable String sampleCount = ''; 298 @observable String sampleCount = '';
148 @observable String refreshTime = ''; 299 @observable String refreshTime = '';
149 @observable String sampleRate = ''; 300 @observable String sampleRate = '';
150 @observable String sampleDepth = ''; 301 @observable String stackDepth = '';
151 @observable String displayCutoff = ''; 302 @observable String displayCutoff = '';
152 @observable String timeSpan = ''; 303 @observable String timeSpan = '';
153 @reflectable double displayThreshold = 0.0002; // 0.02%.
154 304
155 @observable String tagSelector = 'UserVM'; 305 @observable String tagSelector = 'UserVM';
156 306 @observable String modeSelector = 'Function';
157 final _id = '#tableTree'; 307
158 TableTree tree; 308 final CpuProfile profile = new CpuProfile();
159 309
160 static const MICROSECONDS_PER_SECOND = 1000000.0; 310 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 311
177 @override 312 @override
178 void attached() { 313 void attached() {
179 super.attached(); 314 super.attached();
180 var tableBody = shadowRoot.querySelector('#tableTreeBody'); 315 }
181 assert(tableBody != null); 316
182 tree = new TableTree(tableBody, 2); 317 void isolateChanged(oldValue) {
183 _update(); 318 _getCpuProfile();
184 } 319 }
185 320
186 void tagSelectorChanged(oldValue) { 321 void tagSelectorChanged(oldValue) {
187 isolateChanged(null); 322 _getCpuProfile();
323 }
324
325 void modeSelectorChanged(oldValue) {
326 _updateView();
327 }
328
329 void clear(var done) {
330 _clearCpuProfile().whenComplete(done);
331 }
332
333 Future _clearCpuProfile() {
334 profile.clear();
335 if (isolate == null) {
336 return new Future.value(null);
337 }
338 return isolate.invokeRpc('clearCpuProfile', { })
339 .then((ServiceMap response) {
340 _updateView();
341 });
188 } 342 }
189 343
190 void refresh(var done) { 344 void refresh(var done) {
191 isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector }) 345 _getCpuProfile().whenComplete(done);
192 .then((ServiceObject obj) { 346 }
193 // Assert we got back the a profile. 347
194 assert(obj.type == 'CpuProfile'); 348 Future _getCpuProfile() {
195 profile = obj; 349 profile.clear();
196 _update(); 350 if (isolate == null) {
197 }).whenComplete(done); 351 return new Future.value(null);
198 } 352 }
199 353 return isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector })
200 void _update() { 354 .then((ServiceMap response) {
201 if (profile == null) { 355 profile.load(isolate, response);
202 return; 356 _updateView();
203 } 357 });
204 var totalSamples = profile['samples']; 358 }
205 var now = new DateTime.now(); 359
206 sampleCount = totalSamples.toString(); 360 void _updateView() {
207 refreshTime = now.toString(); 361 sampleCount = profile.sampleCount.toString();
208 sampleDepth = profile['depth'].toString(); 362 refreshTime = new DateTime.now().toString();
209 var period = profile['period']; 363 stackDepth = profile.stackDepth.toString();
210 sampleRate = (MICROSECONDS_PER_SECOND / period).toStringAsFixed(0); 364 sampleRate = profile.sampleRate.toStringAsFixed(0);
211 timeSpan = formatTime(profile['timeSpan']); 365 timeSpan = formatTime(profile.timeSpan);
212 displayCutoff = '${(displayThreshold * 100.0).toString()}%'; 366 displayCutoff = '${(profile.displayThreshold * 100.0).toString()}%';
213 profile.isolate.processProfile(profile); 367 if (functionTree != null) {
214 profile['threshold'] = displayThreshold; 368 functionTree.clear();
215 _buildTree(); 369 }
216 } 370 if (codeTree != null) {
217 371 codeTree.clear();
218 void _buildStackTree() { 372 }
219 var root = profile.isolate.profileTrieRoot; 373 if (modeSelector == 'Code') {
374 _buildCodeTree();
375 } else {
376 _buildFunctionTree();
377 }
378 }
379
380 TableTree codeTree;
381 TableTree functionTree;
382
383 void _buildFunctionTree() {
384 if (functionTree == null) {
385 var tableBody = shadowRoot.querySelector('#treeBody');
386 assert(tableBody != null);
387 functionTree = new TableTree(tableBody, 2);
388 }
389 var root = profile.functionTrieRoot;
220 if (root == null) { 390 if (root == null) {
221 return; 391 return;
222 } 392 }
223 try { 393 try {
224 tree.initialize( 394 functionTree.initialize(
225 new ProfileCodeTrieNodeTreeRow(profile, root, root, tree, null)); 395 new ProfileFunctionTrieNodeTreeRow(profile,
396 root, root, functionTree, null));
226 } catch (e, stackTrace) { 397 } catch (e, stackTrace) {
227 print(e); 398 print(e);
228 print(stackTrace); 399 print(stackTrace);
229 Logger.root.warning('_buildStackTree', e, stackTrace); 400 Logger.root.warning('_buildFunctionTree', e, stackTrace);
230 } 401 }
231 // Check if we only have one node at the root and expand it. 402 // Check if we only have one node at the root and expand it.
232 if (tree.rows.length == 1) { 403 if (functionTree.rows.length == 1) {
233 tree.toggle(tree.rows[0]); 404 functionTree.toggle(functionTree.rows[0]);
234 } 405 }
235 notifyPropertyChange(#tree, null, tree); 406 }
236 } 407
237 408 void _buildCodeTree() {
238 void _buildTree() { 409 if (codeTree == null) {
239 _buildStackTree(); 410 var tableBody = shadowRoot.querySelector('#treeBody');
411 assert(tableBody != null);
412 codeTree = new TableTree(tableBody, 2);
413 }
414 var root = profile.codeTrieRoot;
415 if (root == null) {
416 return;
417 }
418 try {
419 codeTree.initialize(
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 }
240 } 430 }
241 } 431 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/elements/code_view.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