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

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

Issue 2999763002: Clear analyzer warning on Observatory (Closed)
Patch Set: Created 3 years, 4 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
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 part of cpu_profiler;
6
7 abstract class CallTreeNode<NodeT extends M.CallTreeNode>
8 implements M.CallTreeNode {
9 final List<NodeT> children;
10 final int count;
11 final int inclusiveNativeAllocations;
12 final int exclusiveNativeAllocations;
13 double get percentage => _percentage;
14 double _percentage = 0.0;
15 final Set<String> attributes = new Set<String>();
16
17 // Either a ProfileCode or a ProfileFunction.
18 Object get profileData;
19 String get name;
20
21 CallTreeNode(this.children, this.count, this.inclusiveNativeAllocations,
22 this.exclusiveNativeAllocations);
23 }
24
25 class CodeCallTreeNode extends CallTreeNode<CodeCallTreeNode>
26 implements M.CodeCallTreeNode {
27 final ProfileCode profileCode;
28
29 Object get profileData => profileCode;
30
31 String get name => profileCode.code.name;
32
33 final Set<String> attributes = new Set<String>();
34 CodeCallTreeNode(this.profileCode, int count, int inclusiveNativeAllocations,
35 int exclusiveNativeAllocations)
36 : super(new List<CodeCallTreeNode>(), count, inclusiveNativeAllocations,
37 exclusiveNativeAllocations) {
38 attributes.addAll(profileCode.attributes);
39 }
40 }
41
42 class CallTree<NodeT extends CallTreeNode> {
43 final bool inclusive;
44 final NodeT root;
45
46 CallTree(this.inclusive, this.root);
47 }
48
49 class CodeCallTree extends CallTree<CodeCallTreeNode>
50 implements M.CodeCallTree {
51 CodeCallTree(bool inclusive, CodeCallTreeNode root) : super(inclusive, root) {
52 if ((root.inclusiveNativeAllocations != null) &&
53 (root.inclusiveNativeAllocations != 0)) {
54 _setCodeMemoryPercentage(null, root);
55 } else {
56 _setCodePercentage(null, root);
57 }
58 }
59
60 CodeCallTree filtered(CallTreeNodeFilter filter) {
61 var treeFilter = new _FilteredCodeCallTreeBuilder(filter, this);
62 treeFilter.build();
63 if ((treeFilter.filtered.root.inclusiveNativeAllocations != null) &&
64 (treeFilter.filtered.root.inclusiveNativeAllocations != 0)) {
65 _setCodeMemoryPercentage(null, treeFilter.filtered.root);
66 } else {
67 _setCodePercentage(null, treeFilter.filtered.root);
68 }
69 return treeFilter.filtered;
70 }
71
72 _setCodePercentage(CodeCallTreeNode parent, CodeCallTreeNode node) {
73 assert(node != null);
74 var parentPercentage = 1.0;
75 var parentCount = node.count;
76 if (parent != null) {
77 parentPercentage = parent._percentage;
78 parentCount = parent.count;
79 }
80 if (inclusive) {
81 node._percentage = parentPercentage * (node.count / parentCount);
82 } else {
83 node._percentage = (node.count / parentCount);
84 }
85 for (var child in node.children) {
86 _setCodePercentage(node, child);
87 }
88 }
89
90 _setCodeMemoryPercentage(CodeCallTreeNode parent, CodeCallTreeNode node) {
91 assert(node != null);
92 var parentPercentage = 1.0;
93 var parentMemory = node.inclusiveNativeAllocations;
94 if (parent != null) {
95 parentPercentage = parent._percentage;
96 parentMemory = parent.inclusiveNativeAllocations;
97 }
98 if (inclusive) {
99 node._percentage =
100 parentPercentage * (node.inclusiveNativeAllocations / parentMemory);
101 } else {
102 node._percentage = (node.inclusiveNativeAllocations / parentMemory);
103 }
104 for (var child in node.children) {
105 _setCodeMemoryPercentage(node, child);
106 }
107 node.children.sort((a, b) {
108 return b.inclusiveNativeAllocations - a.inclusiveNativeAllocations;
109 });
110 }
111
112 _recordCallerAndCalleesInner(
113 CodeCallTreeNode caller, CodeCallTreeNode callee) {
114 if (caller != null) {
115 caller.profileCode._recordCallee(callee.profileCode, callee.count);
116 callee.profileCode._recordCaller(caller.profileCode, caller.count);
117 }
118
119 for (var child in callee.children) {
120 _recordCallerAndCalleesInner(callee, child);
121 }
122 }
123
124 _recordCallerAndCallees() {
125 for (var child in root.children) {
126 _recordCallerAndCalleesInner(null, child);
127 }
128 }
129 }
130
131 class FunctionCallTreeNodeCode {
132 final ProfileCode code;
133 final int ticks;
134 FunctionCallTreeNodeCode(this.code, this.ticks);
135 }
136
137 class FunctionCallTreeNode extends CallTreeNode {
138 final ProfileFunction profileFunction;
139 final codes = new List<FunctionCallTreeNodeCode>();
140 int _totalCodeTicks = 0;
141 int get totalCodesTicks => _totalCodeTicks;
142
143 String get name => M.getFunctionFullName(profileFunction.function);
144 Object get profileData => profileFunction;
145
146 FunctionCallTreeNode(this.profileFunction, int count,
147 inclusiveNativeAllocations, exclusiveNativeAllocations)
148 : super(new List<FunctionCallTreeNode>(), count,
149 inclusiveNativeAllocations, exclusiveNativeAllocations) {
150 profileFunction._addKindBasedAttributes(attributes);
151 }
152
153 // Does this function have an optimized version of itself?
154 bool hasOptimizedCode() {
155 for (var nodeCode in codes) {
156 var profileCode = nodeCode.code;
157 if (!profileCode.code.isDartCode) {
158 continue;
159 }
160 if (profileCode.code.function != profileFunction.function) {
161 continue;
162 }
163 if (profileCode.code.isOptimized) {
164 return true;
165 }
166 }
167 return false;
168 }
169
170 // Does this function have an unoptimized version of itself?
171 bool hasUnoptimizedCode() {
172 for (var nodeCode in codes) {
173 var profileCode = nodeCode.code;
174 if (!profileCode.code.isDartCode) {
175 continue;
176 }
177 if (profileCode.code.kind == M.CodeKind.stub) {
178 continue;
179 }
180 if (!profileCode.code.isOptimized) {
181 return true;
182 }
183 }
184 return false;
185 }
186
187 // Has this function been inlined in another function?
188 bool isInlined() {
189 for (var nodeCode in codes) {
190 var profileCode = nodeCode.code;
191 if (!profileCode.code.isDartCode) {
192 continue;
193 }
194 if (profileCode.code.kind == M.CodeKind.stub) {
195 continue;
196 }
197 // If the code's function isn't this function.
198 if (profileCode.code.function != profileFunction.function) {
199 return true;
200 }
201 }
202 return false;
203 }
204
205 setCodeAttributes() {}
206 }
207
208 /// Predicate filter function. Returns true if path from root to [node] and all
209 /// of [node]'s children should be added to the filtered tree.
210 typedef bool CallTreeNodeFilter(CallTreeNode node);
211
212 /// Build a filter version of a FunctionCallTree.
213 abstract class _FilteredCallTreeBuilder {
214 /// The filter.
215 final CallTreeNodeFilter filter;
216
217 /// The unfiltered tree.
218 final CallTree _unfilteredTree;
219
220 /// The filtered tree (construct by [build]).
221 final CallTree filtered;
222 final List _currentPath = [];
223
224 /// Construct a filtered tree builder using [filter] and [tree].
225 _FilteredCallTreeBuilder(this.filter, CallTree tree, this.filtered)
226 : _unfilteredTree = tree;
227
228 /// Build the filtered tree.
229 build() {
230 assert(filtered != null);
231 assert(filter != null);
232 assert(_unfilteredTree != null);
233 _descend(_unfilteredTree.root);
234 }
235
236 CallTreeNode _findInChildren(CallTreeNode current, CallTreeNode needle) {
237 for (var child in current.children) {
238 if (child.profileData == needle.profileData) {
239 return child;
240 }
241 }
242 return null;
243 }
244
245 CallTreeNode _copyNode(CallTreeNode node);
246
247 /// Add all nodes in [_currentPath].
248 FunctionCallTreeNode _addCurrentPath() {
249 FunctionCallTreeNode current = filtered.root;
250 // Tree root is always the first element of the current path.
251 assert(_unfilteredTree.root == _currentPath[0]);
252 // Assert that unfiltered tree's root and filtered tree's root are different .
253 assert(_unfilteredTree.root != current);
254 for (var i = 1; i < _currentPath.length; i++) {
255 // toAdd is from the unfiltered tree.
256 var toAdd = _currentPath[i];
257 // See if we already have a node for toAdd in the filtered tree.
258 var child = _findInChildren(current, toAdd);
259 if (child == null) {
260 // New node.
261 child = _copyNode(toAdd);
262 current.children.add(child);
263 }
264 current = child;
265 assert(current.count == toAdd.count);
266 }
267 return current;
268 }
269
270 /// Starting at [current] append [next] and all of [next]'s sub-trees
271 _appendTree(CallTreeNode current, CallTreeNode next) {
272 if (next == null) {
273 return;
274 }
275 var child = _findInChildren(current, next);
276 if (child == null) {
277 child = _copyNode(next);
278 current.children.add(child);
279 }
280 current = child;
281 for (var nextChild in next.children) {
282 _appendTree(current, nextChild);
283 }
284 }
285
286 /// Add path from root to [child], [child], and all of [child]'s sub-trees
287 /// to filtered tree.
288 _addTree(CallTreeNode child) {
289 var current = _addCurrentPath();
290 _appendTree(current, child);
291 }
292
293 /// Descend further into the tree. [current] is from the unfiltered tree.
294 _descend(CallTreeNode current) {
295 if (current == null) {
296 return;
297 }
298 _currentPath.add(current);
299
300 if (filter(current)) {
301 // Filter matched.
302 if (current.children.length == 0) {
303 // Have no children. Add this path.
304 _addTree(null);
305 } else {
306 // Add all child trees.
307 for (var child in current.children) {
308 _addTree(child);
309 }
310 }
311 } else {
312 // Did not match, descend to each child.
313 for (var child in current.children) {
314 _descend(child);
315 }
316 }
317
318 var last = _currentPath.removeLast();
319 assert(current == last);
320 }
321 }
322
323 class _FilteredFunctionCallTreeBuilder extends _FilteredCallTreeBuilder {
324 _FilteredFunctionCallTreeBuilder(
325 CallTreeNodeFilter filter, FunctionCallTree tree)
326 : super(
327 filter,
328 tree,
329 new FunctionCallTree(
330 tree.inclusive,
331 new FunctionCallTreeNode(
332 tree.root.profileData,
333 tree.root.count,
334 tree.root.inclusiveNativeAllocations,
335 tree.root.exclusiveNativeAllocations)));
336
337 _copyNode(FunctionCallTreeNode node) {
338 return new FunctionCallTreeNode(node.profileData, node.count,
339 node.inclusiveNativeAllocations, node.exclusiveNativeAllocations);
340 }
341 }
342
343 class _FilteredCodeCallTreeBuilder extends _FilteredCallTreeBuilder {
344 _FilteredCodeCallTreeBuilder(CallTreeNodeFilter filter, CodeCallTree tree)
345 : super(
346 filter,
347 tree,
348 new CodeCallTree(
349 tree.inclusive,
350 new CodeCallTreeNode(
351 tree.root.profileData,
352 tree.root.count,
353 tree.root.inclusiveNativeAllocations,
354 tree.root.exclusiveNativeAllocations)));
355
356 _copyNode(CodeCallTreeNode node) {
357 return new CodeCallTreeNode(node.profileData, node.count,
358 node.inclusiveNativeAllocations, node.exclusiveNativeAllocations);
359 }
360 }
361
362 class FunctionCallTree extends CallTree implements M.FunctionCallTree {
363 FunctionCallTree(bool inclusive, FunctionCallTreeNode root)
364 : super(inclusive, root) {
365 if ((root.inclusiveNativeAllocations != null) &&
366 (root.inclusiveNativeAllocations != 0)) {
367 _setFunctionMemoryPercentage(null, root);
368 } else {
369 _setFunctionPercentage(null, root);
370 }
371 }
372
373 FunctionCallTree filtered(CallTreeNodeFilter filter) {
374 var treeFilter = new _FilteredFunctionCallTreeBuilder(filter, this);
375 treeFilter.build();
376 if ((treeFilter.filtered.root.inclusiveNativeAllocations != null) &&
377 (treeFilter.filtered.root.inclusiveNativeAllocations != 0)) {
378 _setFunctionMemoryPercentage(null, treeFilter.filtered.root);
379 } else {
380 _setFunctionPercentage(null, treeFilter.filtered.root);
381 }
382 return treeFilter.filtered;
383 }
384
385 void _setFunctionPercentage(
386 FunctionCallTreeNode parent, FunctionCallTreeNode node) {
387 assert(node != null);
388 var parentPercentage = 1.0;
389 var parentCount = node.count;
390 if (parent != null) {
391 parentPercentage = parent._percentage;
392 parentCount = parent.count;
393 }
394 if (inclusive) {
395 node._percentage = parentPercentage * (node.count / parentCount);
396 } else {
397 node._percentage = (node.count / parentCount);
398 }
399 for (var child in node.children) {
400 _setFunctionPercentage(node, child);
401 }
402 }
403
404 void _setFunctionMemoryPercentage(
405 FunctionCallTreeNode parent, FunctionCallTreeNode node) {
406 assert(node != null);
407 var parentPercentage = 1.0;
408 var parentMemory = node.inclusiveNativeAllocations;
409 if (parent != null) {
410 parentPercentage = parent._percentage;
411 parentMemory = parent.inclusiveNativeAllocations;
412 }
413 if (inclusive) {
414 node._percentage =
415 parentPercentage * (node.inclusiveNativeAllocations / parentMemory);
416 } else {
417 node._percentage = (node.inclusiveNativeAllocations / parentMemory);
418 }
419 for (var child in node.children) {
420 _setFunctionMemoryPercentage(node, child);
421 }
422 node.children.sort((a, b) {
423 return b.inclusiveNativeAllocations - a.inclusiveNativeAllocations;
424 });
425 }
426
427 _markFunctionCallsInner(
428 FunctionCallTreeNode caller, FunctionCallTreeNode callee) {
429 if (caller != null) {
430 caller.profileFunction
431 ._recordCallee(callee.profileFunction, callee.count);
432 callee.profileFunction
433 ._recordCaller(caller.profileFunction, caller.count);
434 }
435 for (var child in callee.children) {
436 _markFunctionCallsInner(callee, child);
437 }
438 }
439
440 _markFunctionCalls() {
441 for (var child in root.children) {
442 _markFunctionCallsInner(null, child);
443 }
444 }
445 }
446
447 class CodeTick {
448 final int exclusiveTicks;
449 final int inclusiveTicks;
450 CodeTick(this.exclusiveTicks, this.inclusiveTicks);
451 }
452
453 class InlineIntervalTick {
454 final int startAddress;
455 int _inclusiveTicks = 0;
456 int get inclusiveTicks => _inclusiveTicks;
457 int _exclusiveTicks = 0;
458 int get exclusiveTicks => _exclusiveTicks;
459 InlineIntervalTick(this.startAddress);
460 }
461
462 class ProfileCode implements M.ProfileCode {
463 final CpuProfile profile;
464 final Code code;
465 int exclusiveTicks;
466 int inclusiveTicks;
467 int exclusiveNativeAllocations;
468 int inclusiveNativeAllocations;
469 double normalizedExclusiveTicks = 0.0;
470 double normalizedInclusiveTicks = 0.0;
471 final addressTicks = new Map<int, CodeTick>();
472 final intervalTicks = new Map<int, InlineIntervalTick>();
473 String formattedInclusiveTicks = '';
474 String formattedExclusiveTicks = '';
475 String formattedExclusivePercent = '';
476 String formattedCpuTime = '';
477 String formattedOnStackTime = '';
478 final Set<String> attributes = new Set<String>();
479 final Map<ProfileCode, int> callers = new Map<ProfileCode, int>();
480 final Map<ProfileCode, int> callees = new Map<ProfileCode, int>();
481
482 void _processTicks(List<dynamic> profileTicks) {
483 assert(profileTicks != null);
484 assert((profileTicks.length % 3) == 0);
485 for (var i = 0; i < profileTicks.length; i += 3) {
486 // TODO(observatory): Address is not necessarily representable as a JS
487 // integer.
488 var address = int.parse(profileTicks[i] as String, radix: 16);
489 var exclusive = profileTicks[i + 1] as int;
490 var inclusive = profileTicks[i + 2] as int;
491 var tick = new CodeTick(exclusive, inclusive);
492 addressTicks[address] = tick;
493
494 var interval = code.findInterval(address);
495 if (interval != null) {
496 var intervalTick = intervalTicks[interval.start];
497 if (intervalTick == null) {
498 // Insert into map.
499 intervalTick = new InlineIntervalTick(interval.start);
500 intervalTicks[interval.start] = intervalTick;
501 }
502 intervalTick._inclusiveTicks += inclusive;
503 intervalTick._exclusiveTicks += exclusive;
504 }
505 }
506 }
507
508 ProfileCode.fromMap(this.profile, this.code, Map data) {
509 assert(profile != null);
510 assert(code != null);
511
512 code.profile = this;
513
514 if (code.kind == M.CodeKind.stub) {
515 attributes.add('stub');
516 } else if (code.kind == M.CodeKind.dart) {
517 if (code.isNative) {
518 attributes.add('ffi'); // Not to be confused with a C function.
519 } else {
520 attributes.add('dart');
521 }
522 if (code.hasIntrinsic) {
523 attributes.add('intrinsic');
524 }
525 if (code.isOptimized) {
526 attributes.add('optimized');
527 } else {
528 attributes.add('unoptimized');
529 }
530 } else if (code.kind == M.CodeKind.tag) {
531 attributes.add('tag');
532 } else if (code.kind == M.CodeKind.native) {
533 attributes.add('native');
534 }
535 inclusiveTicks = data['inclusiveTicks'];
536 exclusiveTicks = data['exclusiveTicks'];
537
538 normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
539
540 normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
541
542 var ticks = data['ticks'];
543 if (ticks != null) {
544 _processTicks(ticks);
545 }
546
547 if (data.containsKey('exclusiveNativeAllocations') &&
548 data.containsKey('inclusiveNativeAllocations')) {
549 exclusiveNativeAllocations =
550 int.parse(data['exclusiveNativeAllocations']);
551 inclusiveNativeAllocations =
552 int.parse(data['inclusiveNativeAllocations']);
553 }
554
555 formattedExclusivePercent =
556 Utils.formatPercent(exclusiveTicks, profile.sampleCount);
557
558 formattedCpuTime = Utils.formatTimeMilliseconds(
559 profile.approximateMillisecondsForCount(exclusiveTicks));
560
561 formattedOnStackTime = Utils.formatTimeMilliseconds(
562 profile.approximateMillisecondsForCount(inclusiveTicks));
563
564 formattedInclusiveTicks =
565 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
566 '($inclusiveTicks)';
567
568 formattedExclusiveTicks =
569 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
570 '($exclusiveTicks)';
571 }
572
573 _recordCaller(ProfileCode caller, int count) {
574 var r = callers[caller];
575 if (r == null) {
576 r = 0;
577 }
578 callers[caller] = r + count;
579 }
580
581 _recordCallee(ProfileCode callee, int count) {
582 var r = callees[callee];
583 if (r == null) {
584 r = 0;
585 }
586 callees[callee] = r + count;
587 }
588 }
589
590 class ProfileFunction implements M.ProfileFunction {
591 final CpuProfile profile;
592 final ServiceFunction function;
593 // List of compiled code objects containing this function.
594 final List<ProfileCode> profileCodes = new List<ProfileCode>();
595 final Map<ProfileFunction, int> callers = new Map<ProfileFunction, int>();
596 final Map<ProfileFunction, int> callees = new Map<ProfileFunction, int>();
597
598 // Absolute ticks:
599 int exclusiveTicks = 0;
600 int inclusiveTicks = 0;
601
602 // Global percentages:
603 double normalizedExclusiveTicks = 0.0;
604 double normalizedInclusiveTicks = 0.0;
605
606 // Native allocations:
607 int exclusiveNativeAllocations = 0;
608 int inclusiveNativeAllocations = 0;
609
610 String formattedInclusiveTicks = '';
611 String formattedExclusiveTicks = '';
612 String formattedExclusivePercent = '';
613 String formattedCpuTime = '';
614 String formattedOnStackTime = '';
615 final Set<String> attributes = new Set<String>();
616
617 int _sortCodes(ProfileCode a, ProfileCode b) {
618 if (a.code.isOptimized == b.code.isOptimized) {
619 return b.code.profile.exclusiveTicks - a.code.profile.exclusiveTicks;
620 }
621 if (a.code.isOptimized) {
622 return -1;
623 }
624 return 1;
625 }
626
627 // Does this function have an optimized version of itself?
628 bool hasOptimizedCode() {
629 for (var profileCode in profileCodes) {
630 if (profileCode.code.function != function) {
631 continue;
632 }
633 if (profileCode.code.isOptimized) {
634 return true;
635 }
636 }
637 return false;
638 }
639
640 // Does this function have an unoptimized version of itself?
641 bool hasUnoptimizedCode() {
642 for (var profileCode in profileCodes) {
643 if (profileCode.code.kind == M.CodeKind.stub) {
644 continue;
645 }
646 if (!profileCode.code.isDartCode) {
647 continue;
648 }
649 if (!profileCode.code.isOptimized) {
650 return true;
651 }
652 }
653 return false;
654 }
655
656 // Has this function been inlined in another function?
657 bool isInlined() {
658 for (var profileCode in profileCodes) {
659 if (profileCode.code.kind == M.CodeKind.stub) {
660 continue;
661 }
662 if (!profileCode.code.isDartCode) {
663 continue;
664 }
665 // If the code's function isn't this function.
666 if (profileCode.code.function != function) {
667 return true;
668 }
669 }
670 return false;
671 }
672
673 void _addKindBasedAttributes(Set<String> attribs) {
674 if (function.kind == M.FunctionKind.tag) {
675 attribs.add('tag');
676 } else if (function.kind == M.FunctionKind.stub) {
677 attribs.add('stub');
678 } else if (function.kind == M.FunctionKind.native) {
679 attribs.add('native');
680 } else if (M.isSyntheticFunction(function.kind)) {
681 attribs.add('synthetic');
682 } else if (function.isNative) {
683 attribs.add('ffi'); // Not to be confused with a C function.
684 } else {
685 attribs.add('dart');
686 }
687 if (function.hasIntrinsic == true) {
688 attribs.add('intrinsic');
689 }
690 }
691
692 ProfileFunction.fromMap(this.profile, this.function, Map data) {
693 function.profile = this;
694 for (var codeIndex in data['codes']) {
695 var profileCode = profile.codes[codeIndex];
696 profileCodes.add(profileCode);
697 }
698 profileCodes.sort(_sortCodes);
699
700 _addKindBasedAttributes(attributes);
701 exclusiveTicks = data['exclusiveTicks'];
702 inclusiveTicks = data['inclusiveTicks'];
703
704 normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
705 normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
706
707 if (data.containsKey('exclusiveNativeAllocations') &&
708 data.containsKey('inclusiveNativeAllocations')) {
709 exclusiveNativeAllocations =
710 int.parse(data['exclusiveNativeAllocations']);
711 inclusiveNativeAllocations =
712 int.parse(data['inclusiveNativeAllocations']);
713 }
714
715 formattedExclusivePercent =
716 Utils.formatPercent(exclusiveTicks, profile.sampleCount);
717
718 formattedCpuTime = Utils.formatTimeMilliseconds(
719 profile.approximateMillisecondsForCount(exclusiveTicks));
720
721 formattedOnStackTime = Utils.formatTimeMilliseconds(
722 profile.approximateMillisecondsForCount(inclusiveTicks));
723
724 formattedInclusiveTicks =
725 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
726 '($inclusiveTicks)';
727
728 formattedExclusiveTicks =
729 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
730 '($exclusiveTicks)';
731 }
732
733 _recordCaller(ProfileFunction caller, int count) {
734 var r = callers[caller];
735 if (r == null) {
736 r = 0;
737 }
738 callers[caller] = r + count;
739 }
740
741 _recordCallee(ProfileFunction callee, int count) {
742 var r = callees[callee];
743 if (r == null) {
744 r = 0;
745 }
746 callees[callee] = r + count;
747 }
748 }
749
750 // TODO(johnmccutchan): Rename to SampleProfile
751 class CpuProfile extends M.SampleProfile {
752 Isolate isolate;
753
754 int sampleCount = 0;
755 int samplePeriod = 0;
756 double sampleRate = 0.0;
757
758 int stackDepth = 0;
759
760 double timeSpan = 0.0;
761
762 final Map<String, List> tries = <String, List>{};
763 final List<ProfileCode> codes = new List<ProfileCode>();
764 bool _builtCodeCalls = false;
765 final List<ProfileFunction> functions = new List<ProfileFunction>();
766 bool _builtFunctionCalls = false;
767
768 CodeCallTree loadCodeTree(M.ProfileTreeDirection direction) {
769 switch (direction) {
770 case M.ProfileTreeDirection.inclusive:
771 return _loadCodeTree(true, tries['inclusiveCodeTrie']);
772 case M.ProfileTreeDirection.exclusive:
773 return _loadCodeTree(false, tries['exclusiveCodeTrie']);
774 }
775 throw new Exception('Unknown ProfileTreeDirection');
776 }
777
778 FunctionCallTree loadFunctionTree(M.ProfileTreeDirection direction) {
779 switch (direction) {
780 case M.ProfileTreeDirection.inclusive:
781 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']);
782 case M.ProfileTreeDirection.exclusive:
783 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']);
784 }
785 throw new Exception('Unknown ProfileTreeDirection');
786 }
787
788 buildCodeCallerAndCallees() {
789 if (_builtCodeCalls) {
790 return;
791 }
792 _builtCodeCalls = true;
793 var tree = loadCodeTree(M.ProfileTreeDirection.inclusive);
794 tree._recordCallerAndCallees();
795 }
796
797 buildFunctionCallerAndCallees() {
798 if (_builtFunctionCalls) {
799 return;
800 }
801 _builtFunctionCalls = true;
802 var tree = loadFunctionTree(M.ProfileTreeDirection.inclusive);
803 tree._markFunctionCalls();
804 }
805
806 clear() {
807 sampleCount = 0;
808 samplePeriod = 0;
809 sampleRate = 0.0;
810 stackDepth = 0;
811 timeSpan = 0.0;
812 codes.clear();
813 functions.clear();
814 tries.clear();
815 _builtCodeCalls = false;
816 _builtFunctionCalls = false;
817 }
818
819 Future load(ServiceObjectOwner owner, ServiceMap profile) async {
820 await loadProgress(owner, profile).last;
821 }
822
823 static Future sleep([Duration duration = const Duration(microseconds: 0)]) {
824 final Completer completer = new Completer();
825 new Timer(duration, () => completer.complete());
826 return completer.future;
827 }
828
829 Stream<double> loadProgress(ServiceObjectOwner owner, ServiceMap profile) {
830 var progress = new StreamController<double>.broadcast();
831
832 (() async {
833 final Stopwatch watch = new Stopwatch();
834 watch.start();
835 int count = 0;
836 var needToUpdate = () {
837 count++;
838 if (((count % 256) == 0) && (watch.elapsedMilliseconds > 16)) {
839 watch.reset();
840 return true;
841 }
842 return false;
843 };
844 var signal = (double p) {
845 progress.add(p);
846 return sleep();
847 };
848 try {
849 clear();
850 progress.add(0.0);
851 if (profile == null) {
852 return;
853 }
854
855 if ((owner != null) && (owner is Isolate)) {
856 isolate = owner;
857 isolate.resetCachedProfileData();
858 }
859
860 sampleCount = profile['sampleCount'];
861 samplePeriod = profile['samplePeriod'];
862 sampleRate = (Duration.MICROSECONDS_PER_SECOND / samplePeriod);
863 stackDepth = profile['stackDepth'];
864 timeSpan = profile['timeSpan'];
865
866 num length = profile['codes'].length + profile['functions'].length;
867
868 // Process code table.
869 for (var codeRegion in profile['codes']) {
870 if (needToUpdate()) {
871 await signal(count * 100.0 / length);
872 }
873 Code code = codeRegion['code'];
874 assert(code != null);
875 codes.add(new ProfileCode.fromMap(this, code, codeRegion));
876 }
877 // Process function table.
878 for (var profileFunction in profile['functions']) {
879 if (needToUpdate()) {
880 await signal(count * 100 / length);
881 }
882 ServiceFunction function = profileFunction['function'];
883 assert(function != null);
884 functions.add(
885 new ProfileFunction.fromMap(this, function, profileFunction));
886 }
887
888 tries['exclusiveCodeTrie'] =
889 new Uint32List.fromList(profile['exclusiveCodeTrie']);
890 tries['inclusiveCodeTrie'] =
891 new Uint32List.fromList(profile['inclusiveCodeTrie']);
892 tries['exclusiveFunctionTrie'] =
893 new Uint32List.fromList(profile['exclusiveFunctionTrie']);
894 tries['inclusiveFunctionTrie'] =
895 new Uint32List.fromList(profile['inclusiveFunctionTrie']);
896 } finally {
897 progress.close();
898 }
899 }());
900 return progress.stream;
901 }
902
903 // Data shared across calls to _read*TrieNode.
904 int _dataCursor = 0;
905
906 // The code trie is serialized as a list of integers. Each node
907 // is recreated by consuming some portion of the list. The format is as
908 // follows:
909 // [0] index into codeTable of code object.
910 // [1] tick count (number of times this stack frame occured).
911 // [2] child node count
912 // Reading the trie is done by recursively reading the tree depth-first
913 // pre-order.
914 CodeCallTree _loadCodeTree(bool inclusive, List<int> data) {
915 if (data == null) {
916 return null;
917 }
918 if (data.length < 3) {
919 // Not enough for root node.
920 return null;
921 }
922 // Read the tree, returns the root node.
923 var root = _readCodeTrie(data);
924 return new CodeCallTree(inclusive, root);
925 }
926
927 CodeCallTreeNode _readCodeTrieNode(List<int> data) {
928 // Lookup code object.
929 var codeIndex = data[_dataCursor++];
930 var code = codes[codeIndex];
931 // Node tick counter.
932 var count = data[_dataCursor++];
933 // Child node count.
934 var children = data[_dataCursor++];
935 // Inclusive native allocations.
936 var inclusiveNativeAllocations = data[_dataCursor++];
937 // Exclusive native allocations.
938 var exclusiveNativeAllocations = data[_dataCursor++];
939 // Create node.
940 var node = new CodeCallTreeNode(
941 code, count, inclusiveNativeAllocations, exclusiveNativeAllocations);
942 node.children.length = children;
943 return node;
944 }
945
946 CodeCallTreeNode _readCodeTrie(List<int> data) {
947 final nodeStack = new List<CodeCallTreeNode>();
948 final childIndexStack = new List<int>();
949
950 _dataCursor = 0;
951 // Read root.
952 var root = _readCodeTrieNode(data);
953
954 // Push root onto stack.
955 if (root.children.length > 0) {
956 nodeStack.add(root);
957 childIndexStack.add(0);
958 }
959
960 while (nodeStack.length > 0) {
961 var lastIndex = nodeStack.length - 1;
962 // Pop parent from stack.
963 var parent = nodeStack[lastIndex];
964 var childIndex = childIndexStack[lastIndex];
965
966 // Read child node.
967 assert(childIndex < parent.children.length);
968 var node = _readCodeTrieNode(data);
969 parent.children[childIndex++] = node;
970
971 // If parent still has children, update child index.
972 if (childIndex < parent.children.length) {
973 childIndexStack[lastIndex] = childIndex;
974 } else {
975 // Finished processing parent node.
976 nodeStack.removeLast();
977 childIndexStack.removeLast();
978 }
979
980 // If node has children, push onto stack.
981 if (node.children.length > 0) {
982 nodeStack.add(node);
983 childIndexStack.add(0);
984 }
985 }
986
987 return root;
988 }
989
990 FunctionCallTree _loadFunctionTree(bool inclusive, List<int> data) {
991 if (data == null) {
992 return null;
993 }
994 if (data.length < 3) {
995 // Not enough integers for 1 node.
996 return null;
997 }
998 // Read the tree, returns the root node.
999 var root = _readFunctionTrie(data);
1000 return new FunctionCallTree(inclusive, root);
1001 }
1002
1003 FunctionCallTreeNode _readFunctionTrieNode(List<int> data) {
1004 // Read index into function table.
1005 var index = data[_dataCursor++];
1006 // Lookup function object.
1007 var function = functions[index];
1008 // Counter.
1009 var count = data[_dataCursor++];
1010 // Inclusive native allocations.
1011 var inclusiveNativeAllocations = data[_dataCursor++];
1012 // Exclusive native allocations.
1013 var exclusiveNativeAllocations = data[_dataCursor++];
1014 // Create node.
1015 var node = new FunctionCallTreeNode(function, count,
1016 inclusiveNativeAllocations, exclusiveNativeAllocations);
1017 // Number of code index / count pairs.
1018 var codeCount = data[_dataCursor++];
1019 node.codes.length = codeCount;
1020 var totalCodeTicks = 0;
1021 for (var i = 0; i < codeCount; i++) {
1022 var codeIndex = data[_dataCursor++];
1023 var code = codes[codeIndex];
1024 assert(code != null);
1025 var codeTicks = data[_dataCursor++];
1026 totalCodeTicks += codeTicks;
1027 var nodeCode = new FunctionCallTreeNodeCode(code, codeTicks);
1028 node.codes[i] = nodeCode;
1029 }
1030 node.setCodeAttributes();
1031 node._totalCodeTicks = totalCodeTicks;
1032 // Number of children.
1033 var childCount = data[_dataCursor++];
1034 node.children.length = childCount;
1035 return node;
1036 }
1037
1038 FunctionCallTreeNode _readFunctionTrie(List<int> data) {
1039 final nodeStack = new List<FunctionCallTreeNode>();
1040 final childIndexStack = new List<int>();
1041
1042 _dataCursor = 0;
1043
1044 // Read root.
1045 var root = _readFunctionTrieNode(data);
1046
1047 // Push root onto stack.
1048 if (root.children.length > 0) {
1049 nodeStack.add(root);
1050 childIndexStack.add(0);
1051 }
1052
1053 while (nodeStack.length > 0) {
1054 var lastIndex = nodeStack.length - 1;
1055 // Pop parent from stack.
1056 var parent = nodeStack[lastIndex];
1057 var childIndex = childIndexStack[lastIndex];
1058
1059 // Read child node.
1060 assert(childIndex < parent.children.length);
1061 var node = _readFunctionTrieNode(data);
1062 parent.children[childIndex++] = node;
1063
1064 // If parent still has children, update child index.
1065 if (childIndex < parent.children.length) {
1066 childIndexStack[lastIndex] = childIndex;
1067 } else {
1068 // Finished processing parent node.
1069 nodeStack.removeLast();
1070 childIndexStack.removeLast();
1071 }
1072
1073 // If node has children, push onto stack.
1074 if (node.children.length > 0) {
1075 nodeStack.add(node);
1076 childIndexStack.add(0);
1077 }
1078 }
1079
1080 return root;
1081 }
1082
1083 int approximateMillisecondsForCount(count) {
1084 return (count * samplePeriod) ~/ Duration.MICROSECONDS_PER_MILLISECOND;
1085 }
1086
1087 double approximateSecondsForCount(count) {
1088 return (count * samplePeriod) / Duration.MICROSECONDS_PER_SECOND;
1089 }
1090 }
OLDNEW
« no previous file with comments | « runtime/observatory/lib/service.dart ('k') | runtime/observatory/lib/src/elements/class_allocation_profile.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698