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

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

Issue 1013563002: CPU profile displayed in three tables with a tree (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 8 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) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, 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 part of cpu_profiler; 5 part of cpu_profiler;
6 6
7 class CodeCallTreeNode { 7 class CodeCallTreeNode {
8 final ProfileCode profileCode; 8 final ProfileCode profileCode;
9 final int count; 9 final int count;
10 double get percentage => _percentage; 10 double get percentage => _percentage;
(...skipping 23 matching lines...) Expand all
34 } 34 }
35 if (inclusive) { 35 if (inclusive) {
36 node._percentage = parentPercentage * (node.count / parentCount); 36 node._percentage = parentPercentage * (node.count / parentCount);
37 } else { 37 } else {
38 node._percentage = (node.count / parentCount); 38 node._percentage = (node.count / parentCount);
39 } 39 }
40 for (var child in node.children) { 40 for (var child in node.children) {
41 _setCodePercentage(node, child); 41 _setCodePercentage(node, child);
42 } 42 }
43 } 43 }
44
45 _recordCallerAndCalleesInner(CodeCallTreeNode caller,
46 CodeCallTreeNode callee) {
47 if (caller != null) {
48 caller.profileCode._recordCallee(callee.profileCode, callee.count);
49 callee.profileCode._recordCaller(caller.profileCode, callee.count);
50 }
51
52 for (var child in callee.children) {
53 _recordCallerAndCalleesInner(callee, child);
54 }
55 }
56
57 _recordCallerAndCallees() {
58 for (var child in root.children) {
59 _recordCallerAndCalleesInner(null, child);
60 }
61 }
44 } 62 }
45 63
46 class FunctionCallTreeNodeCode { 64 class FunctionCallTreeNodeCode {
47 final ProfileCode code; 65 final ProfileCode code;
48 final int ticks; 66 final int ticks;
49 FunctionCallTreeNodeCode(this.code, this.ticks); 67 FunctionCallTreeNodeCode(this.code, this.ticks);
50 } 68 }
51 69
52 class FunctionCallTreeNode { 70 class FunctionCallTreeNode {
53 final ProfileFunction profileFunction; 71 final ProfileFunction profileFunction;
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 } 140 }
123 if (hasUnoptimizedCode()) { 141 if (hasUnoptimizedCode()) {
124 attributes.add('unoptimized'); 142 attributes.add('unoptimized');
125 } 143 }
126 if (isInlined()) { 144 if (isInlined()) {
127 attributes.add('inlined'); 145 attributes.add('inlined');
128 } 146 }
129 } 147 }
130 } 148 }
131 149
150 /// Predicate filter function. Returns true if path from root to [node] and all
151 /// of [node]'s children should be added to the filtered tree.
152 typedef bool FunctionCallTreeNodeFilter(FunctionCallTreeNode node);
153
154 /// Build a filter version of a FunctionCallTree.
155 class _FilteredFunctionCallTreeBuilder {
156 /// The filter.
157 final FunctionCallTreeNodeFilter filter;
158 /// The unfiltered tree.
159 final FunctionCallTree _unfilteredTree;
160 /// The filtered tree (construct by [build]).
161 final FunctionCallTree filtered;
162 final List _currentPath = [];
163
164 /// Construct a filtered tree builder using [filter] and [tree].
165 _FilteredFunctionCallTreeBuilder(this.filter, FunctionCallTree tree)
166 : _unfilteredTree = tree,
167 filtered =
168 new FunctionCallTree(
169 tree.inclusive,
170 new FunctionCallTreeNode(
171 tree.root.profileFunction,
172 tree.root.count));
173
174 /// Build the filtered tree.
175 build() {
176 assert(filtered != null);
177 assert(filter != null);
178 assert(_unfilteredTree != null);
179 _descend(_unfilteredTree.root);
180 }
181
182 FunctionCallTreeNode _findFunctionInChildren(FunctionCallTreeNode current,
183 FunctionCallTreeNode needle) {
184 for (var child in current.children) {
185 if (child.profileFunction == needle.profileFunction) {
186 return child;
187 }
188 }
189 return null;
190 }
191
192 /// Add all nodes in [_currentPath].
193 FunctionCallTreeNode _addCurrentPath() {
194 FunctionCallTreeNode current = filtered.root;
195 // Tree root is always the first element of the current path.
196 assert(_unfilteredTree.root == _currentPath[0]);
197 // Assert that unfiltered tree's root and filtered tree's root are different .
198 assert(_unfilteredTree.root != current);
199 for (var i = 1; i < _currentPath.length; i++) {
200 // toAdd is from the unfiltered tree.
201 var toAdd = _currentPath[i];
202 // See if we already have a node for toAdd in the filtered tree.
203 var child = _findFunctionInChildren(current, toAdd);
204 if (child == null) {
205 // New node.
206 child = new FunctionCallTreeNode(toAdd.profileFunction, toAdd.count);
207 current.children.add(child);
208 }
209 current = child;
210 assert(current.count == toAdd.count);
211 }
212 return current;
213 }
214
215 /// Starting at [current] append [next] and all of [next]'s sub-trees
216 _appendTree(FunctionCallTreeNode current, FunctionCallTreeNode next) {
217 if (next == null) {
218 return;
219 }
220 var child = _findFunctionInChildren(current, next);
221 if (child == null) {
222 child = new FunctionCallTreeNode(next.profileFunction, next.count);
223 current.children.add(child);
224 }
225 current = child;
226 for (var nextChild in next.children) {
227 _appendTree(current, nextChild);
228 }
229 }
230
231 /// Add path from root to [child], [child], and all of [child]'s sub-trees
232 /// to filtered tree.
233 _addTree(FunctionCallTreeNode child) {
234 var current = _addCurrentPath();
235 _appendTree(current, child);
236 }
237
238 /// Descend further into the tree. [current] is from the unfiltered tree.
239 _descend(FunctionCallTreeNode current) {
240 if (current == null) {
241 return;
242 }
243 _currentPath.add(current);
244
245 if (filter(current)) {
246 // Filter matched.
247 if (current.children.length == 0) {
248 // Have no children. Add this path.
249 _addTree(null);
250 } else {
251 // Add all child trees.
252 for (var child in current.children) {
253 _addTree(child);
254 }
255 }
256 } else {
257 // Did not match, descend to each child.
258 for (var child in current.children) {
259 _descend(child);
260 }
261 }
262
263 var last = _currentPath.removeLast();
264 assert(current == last);
265 }
266 }
267
132 class FunctionCallTree { 268 class FunctionCallTree {
133 final bool inclusive; 269 final bool inclusive;
134 final FunctionCallTreeNode root; 270 final FunctionCallTreeNode root;
135 FunctionCallTree(this.inclusive, this.root) { 271 FunctionCallTree(this.inclusive, this.root) {
136 _setFunctionPercentage(null, root); 272 _setFunctionPercentage(null, root);
137 } 273 }
138 274
275 FunctionCallTree filtered(FunctionCallTreeNodeFilter filter) {
276 var treeFilter = new _FilteredFunctionCallTreeBuilder(filter, this);
277 treeFilter.build();
278 _setFunctionPercentage(null, treeFilter.filtered.root);
279 return treeFilter.filtered;
280 }
281
139 void _setFunctionPercentage(FunctionCallTreeNode parent, 282 void _setFunctionPercentage(FunctionCallTreeNode parent,
140 FunctionCallTreeNode node) { 283 FunctionCallTreeNode node) {
141 assert(node != null); 284 assert(node != null);
142 var parentPercentage = 1.0; 285 var parentPercentage = 1.0;
143 var parentCount = node.count; 286 var parentCount = node.count;
144 if (parent != null) { 287 if (parent != null) {
145 parentPercentage = parent._percentage; 288 parentPercentage = parent._percentage;
146 parentCount = parent.count; 289 parentCount = parent.count;
147 } 290 }
148 if (inclusive) { 291 if (inclusive) {
149 node._percentage = parentPercentage * (node.count / parentCount); 292 node._percentage = parentPercentage * (node.count / parentCount);
150 } else { 293 } else {
151 node._percentage = (node.count / parentCount); 294 node._percentage = (node.count / parentCount);
152 } 295 }
153 for (var child in node.children) { 296 for (var child in node.children) {
154 _setFunctionPercentage(node, child); 297 _setFunctionPercentage(node, child);
155 } 298 }
156 } 299 }
300
301 _markFunctionCallsInner(FunctionCallTreeNode caller,
302 FunctionCallTreeNode callee) {
303 if (caller != null) {
304 caller.profileFunction._recordCallee(callee.profileFunction, callee.count) ;
305 callee.profileFunction._recordCaller(caller.profileFunction, callee.count) ;
306 }
307 for (var child in callee.children) {
308 _markFunctionCallsInner(callee, child);
309 }
310 }
311
312 _markFunctionCalls() {
313 for (var child in root.children) {
314 _markFunctionCallsInner(null, child);
315 }
316 }
157 } 317 }
158 318
159 class CodeTick { 319 class CodeTick {
160 final int exclusiveTicks; 320 final int exclusiveTicks;
161 final int inclusiveTicks; 321 final int inclusiveTicks;
162 CodeTick(this.exclusiveTicks, this.inclusiveTicks); 322 CodeTick(this.exclusiveTicks, this.inclusiveTicks);
163 } 323 }
164 324
165 class InlineIntervalTick { 325 class InlineIntervalTick {
166 final int startAddress; 326 final int startAddress;
(...skipping 12 matching lines...) Expand all
179 double normalizedExclusiveTicks = 0.0; 339 double normalizedExclusiveTicks = 0.0;
180 double normalizedInclusiveTicks = 0.0; 340 double normalizedInclusiveTicks = 0.0;
181 final addressTicks = new Map<int, CodeTick>(); 341 final addressTicks = new Map<int, CodeTick>();
182 final intervalTicks = new Map<int, InlineIntervalTick>(); 342 final intervalTicks = new Map<int, InlineIntervalTick>();
183 String formattedInclusiveTicks = ''; 343 String formattedInclusiveTicks = '';
184 String formattedExclusiveTicks = ''; 344 String formattedExclusiveTicks = '';
185 String formattedExclusivePercent = ''; 345 String formattedExclusivePercent = '';
186 String formattedCpuTime = ''; 346 String formattedCpuTime = '';
187 String formattedOnStackTime = ''; 347 String formattedOnStackTime = '';
188 final Set<String> attributes = new Set<String>(); 348 final Set<String> attributes = new Set<String>();
349 final Map<ProfileCode, int> callers = new Map<ProfileCode, int>();
350 final Map<ProfileCode, int> callees = new Map<ProfileCode, int>();
189 351
190 void _processTicks(List<String> profileTicks) { 352 void _processTicks(List<String> profileTicks) {
191 assert(profileTicks != null); 353 assert(profileTicks != null);
192 assert((profileTicks.length % 3) == 0); 354 assert((profileTicks.length % 3) == 0);
193 for (var i = 0; i < profileTicks.length; i += 3) { 355 for (var i = 0; i < profileTicks.length; i += 3) {
194 var address = int.parse(profileTicks[i], radix:16); 356 var address = int.parse(profileTicks[i], radix:16);
195 var exclusive = int.parse(profileTicks[i + 1]); 357 var exclusive = int.parse(profileTicks[i + 1]);
196 var inclusive = int.parse(profileTicks[i + 2]); 358 var inclusive = int.parse(profileTicks[i + 2]);
197 var tick = new CodeTick(exclusive, inclusive); 359 var tick = new CodeTick(exclusive, inclusive);
198 addressTicks[address] = tick; 360 addressTicks[address] = tick;
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
255 profile.approximateMillisecondsForCount(inclusiveTicks)); 417 profile.approximateMillisecondsForCount(inclusiveTicks));
256 418
257 formattedInclusiveTicks = 419 formattedInclusiveTicks =
258 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' 420 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
259 '($inclusiveTicks)'; 421 '($inclusiveTicks)';
260 422
261 formattedExclusiveTicks = 423 formattedExclusiveTicks =
262 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' 424 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
263 '($exclusiveTicks)'; 425 '($exclusiveTicks)';
264 } 426 }
427
428 _recordCaller(ProfileCode caller, int count) {
429 var r = callers[caller];
430 if (r == null) {
431 r = 0;
432 }
433 callers[caller] = r + count;
434 }
435
436 _recordCallee(ProfileCode callee, int count) {
437 var r = callees[callee];
438 if (r == null) {
439 r = 0;
440 }
441 callees[callee] = r + count;
442 }
265 } 443 }
266 444
267 class ProfileFunction { 445 class ProfileFunction {
268 final CpuProfile profile; 446 final CpuProfile profile;
269 final ServiceFunction function; 447 final ServiceFunction function;
270 // List of compiled code objects containing this function. 448 // List of compiled code objects containing this function.
271 final List<ProfileCode> profileCodes = new List<ProfileCode>(); 449 final List<ProfileCode> profileCodes = new List<ProfileCode>();
450 final Map<ProfileFunction, int> callers = new Map<ProfileFunction, int>();
451 final Map<ProfileFunction, int> callees = new Map<ProfileFunction, int>();
452
272 // Absolute ticks: 453 // Absolute ticks:
273 int exclusiveTicks = 0; 454 int exclusiveTicks = 0;
274 int inclusiveTicks = 0; 455 int inclusiveTicks = 0;
275 456
276 // Global percentages: 457 // Global percentages:
277 double normalizedExclusiveTicks = 0.0; 458 double normalizedExclusiveTicks = 0.0;
278 double normalizedInclusiveTicks = 0.0; 459 double normalizedInclusiveTicks = 0.0;
279 460
280 String formattedInclusiveTicks = ''; 461 String formattedInclusiveTicks = '';
281 String formattedExclusiveTicks = ''; 462 String formattedExclusiveTicks = '';
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
349 } else if (function.kind == FunctionKind.kNative) { 530 } else if (function.kind == FunctionKind.kNative) {
350 attribs.add('native'); 531 attribs.add('native');
351 } else if (function.kind.isSynthetic()) { 532 } else if (function.kind.isSynthetic()) {
352 attribs.add('synthetic'); 533 attribs.add('synthetic');
353 } else { 534 } else {
354 attribs.add('dart'); 535 attribs.add('dart');
355 } 536 }
356 } 537 }
357 538
358 ProfileFunction.fromMap(this.profile, this.function, Map data) { 539 ProfileFunction.fromMap(this.profile, this.function, Map data) {
540 function.profile = this;
359 for (var codeIndex in data['codes']) { 541 for (var codeIndex in data['codes']) {
360 var profileCode = profile.codes[codeIndex]; 542 var profileCode = profile.codes[codeIndex];
361 profileCodes.add(profileCode); 543 profileCodes.add(profileCode);
362 } 544 }
363 profileCodes.sort(_sortCodes); 545 profileCodes.sort(_sortCodes);
364 546
365 if (hasOptimizedCode()) { 547 if (hasOptimizedCode()) {
366 attributes.add('optimized'); 548 attributes.add('optimized');
367 } 549 }
368 if (hasUnoptimizedCode()) { 550 if (hasUnoptimizedCode()) {
(...skipping 21 matching lines...) Expand all
390 profile.approximateMillisecondsForCount(inclusiveTicks)); 572 profile.approximateMillisecondsForCount(inclusiveTicks));
391 573
392 formattedInclusiveTicks = 574 formattedInclusiveTicks =
393 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' 575 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
394 '($inclusiveTicks)'; 576 '($inclusiveTicks)';
395 577
396 formattedExclusiveTicks = 578 formattedExclusiveTicks =
397 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' 579 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
398 '($exclusiveTicks)'; 580 '($exclusiveTicks)';
399 } 581 }
582
583 _recordCaller(ProfileFunction caller, int count) {
584 var r = callers[caller];
585 if (r == null) {
586 r = 0;
587 }
588 callers[caller] = r + count;
589 }
590
591 _recordCallee(ProfileFunction callee, int count) {
592 var r = callees[callee];
593 if (r == null) {
594 r = 0;
595 }
596 callees[callee] = r + count;
597 }
400 } 598 }
401 599
402 600
403 class CpuProfile { 601 class CpuProfile {
404 final double MICROSECONDS_PER_SECOND = 1000000.0; 602 final double MICROSECONDS_PER_SECOND = 1000000.0;
405 final double displayThreshold = 0.0002; // 0.02%. 603 final double displayThreshold = 0.0002; // 0.02%.
406 604
407 Isolate isolate; 605 Isolate isolate;
408 606
409 int sampleCount = 0; 607 int sampleCount = 0;
410 int samplePeriod = 0; 608 int samplePeriod = 0;
411 double sampleRate = 0.0; 609 double sampleRate = 0.0;
412 610
413 int stackDepth = 0; 611 int stackDepth = 0;
414 612
415 double timeSpan = 0.0; 613 double timeSpan = 0.0;
416 614
417 final Map<String, List> tries = <String, List>{}; 615 final Map<String, List> tries = <String, List>{};
418 final List<ProfileCode> codes = new List<ProfileCode>(); 616 final List<ProfileCode> codes = new List<ProfileCode>();
617 bool _builtCodeCalls = false;
419 final List<ProfileFunction> functions = new List<ProfileFunction>(); 618 final List<ProfileFunction> functions = new List<ProfileFunction>();
619 bool _builtFunctionCalls = false;
420 620
421 CodeCallTree loadCodeTree(String name) { 621 CodeCallTree loadCodeTree(String name) {
422 if (name == 'inclusive') { 622 if (name == 'inclusive') {
423 return _loadCodeTree(true, tries['inclusiveCodeTrie']); 623 return _loadCodeTree(true, tries['inclusiveCodeTrie']);
424 } else { 624 } else {
425 return _loadCodeTree(false, tries['exclusiveCodeTrie']); 625 return _loadCodeTree(false, tries['exclusiveCodeTrie']);
426 } 626 }
427 } 627 }
428 628
429 FunctionCallTree loadFunctionTree(String name) { 629 FunctionCallTree loadFunctionTree(String name) {
430 if (name == 'inclusive') { 630 if (name == 'inclusive') {
431 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']); 631 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']);
432 } else { 632 } else {
433 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']); 633 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']);
434 } 634 }
435 } 635 }
436 636
437 void clear() { 637 buildCodeCallerAndCallees() {
638 if (_builtCodeCalls) {
639 return;
640 }
641 _builtCodeCalls = true;
642 var tree = loadCodeTree('inclusive');
643 tree._recordCallerAndCallees();
644 }
645
646 buildFunctionCallerAndCallees() {
647 if (_builtFunctionCalls) {
648 return;
649 }
650 _builtFunctionCalls = true;
651 var tree = loadFunctionTree('inclusive');
652 tree._markFunctionCalls();
653 }
654
655 clear() {
438 sampleCount = 0; 656 sampleCount = 0;
439 samplePeriod = 0; 657 samplePeriod = 0;
440 sampleRate = 0.0; 658 sampleRate = 0.0;
441 stackDepth = 0; 659 stackDepth = 0;
442 timeSpan = 0.0; 660 timeSpan = 0.0;
443 codes.clear(); 661 codes.clear();
444 functions.clear(); 662 functions.clear();
445 tries.clear(); 663 tries.clear();
664 _builtCodeCalls = false;
665 _builtFunctionCalls = false;
446 } 666 }
447 667
448 void load(Isolate isolate, ServiceMap profile) { 668 load(Isolate isolate, ServiceMap profile) {
449 clear(); 669 clear();
450 if ((isolate == null) || (profile == null)) { 670 if ((isolate == null) || (profile == null)) {
451 return; 671 return;
452 } 672 }
453 673
454 this.isolate = isolate; 674 this.isolate = isolate;
455 isolate.resetCachedProfileData(); 675 isolate.resetCachedProfileData();
456 676
457 sampleCount = profile['sampleCount']; 677 sampleCount = profile['sampleCount'];
458 samplePeriod = profile['samplePeriod']; 678 samplePeriod = profile['samplePeriod'];
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after
657 int approximateMillisecondsForCount(count) { 877 int approximateMillisecondsForCount(count) {
658 var MICROSECONDS_PER_MILLISECOND = 1000.0; 878 var MICROSECONDS_PER_MILLISECOND = 1000.0;
659 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; 879 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND;
660 } 880 }
661 881
662 double approximateSecondsForCount(count) { 882 double approximateSecondsForCount(count) {
663 var MICROSECONDS_PER_SECOND = 1000000.0; 883 var MICROSECONDS_PER_SECOND = 1000000.0;
664 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; 884 return (count * samplePeriod) / MICROSECONDS_PER_SECOND;
665 } 885 }
666 } 886 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/src/app/page.dart ('k') | runtime/observatory/lib/src/elements/class_tree.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698