Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 abstract class CallTreeNode { | 7 abstract class CallTreeNode<NodeT extends M.CallTreeNode> |
| 8 final List<CallTreeNode> children; | 8 implements M.CallTreeNode { |
| 9 final List<NodeT> children; | |
| 9 final int count; | 10 final int count; |
| 10 double get percentage => _percentage; | 11 double get percentage => _percentage; |
| 11 double _percentage = 0.0; | 12 double _percentage = 0.0; |
| 12 final Set<String> attributes = new Set<String>(); | 13 final Set<String> attributes = new Set<String>(); |
| 13 | 14 |
| 14 // Either a ProfileCode or a ProfileFunction. | 15 // Either a ProfileCode or a ProfileFunction. |
| 15 Object get profileData; | 16 Object get profileData; |
| 16 String get name; | 17 String get name; |
| 17 | 18 |
| 18 CallTreeNode(this.children, this.count); | 19 CallTreeNode(this.children, this.count); |
| 19 } | 20 } |
| 20 | 21 |
| 21 class CodeCallTreeNode extends CallTreeNode { | 22 class CodeCallTreeNode extends CallTreeNode<CodeCallTreeNode> |
| 23 implements M.CodeCallTreeNode { | |
| 22 final ProfileCode profileCode; | 24 final ProfileCode profileCode; |
| 23 | 25 |
| 24 Object get profileData => profileCode; | 26 Object get profileData => profileCode; |
| 25 | 27 |
| 26 String get name => profileCode.code.name; | 28 String get name => profileCode.code.name; |
| 27 | 29 |
| 28 final Set<String> attributes = new Set<String>(); | 30 final Set<String> attributes = new Set<String>(); |
| 29 CodeCallTreeNode(this.profileCode, int count) | 31 CodeCallTreeNode(this.profileCode, int count) |
| 30 : super(new List<CodeCallTreeNode>(), count) { | 32 : super(new List<CodeCallTreeNode>(), count) { |
| 31 attributes.addAll(profileCode.attributes); | 33 attributes.addAll(profileCode.attributes); |
| 32 } | 34 } |
| 33 } | 35 } |
| 34 | 36 |
| 35 class CallTree { | 37 class CallTree<NodeT extends CallTreeNode> { |
| 36 final bool inclusive; | 38 final bool inclusive; |
| 37 final CallTreeNode root; | 39 final NodeT root; |
| 38 | 40 |
| 39 CallTree(this.inclusive, this.root); | 41 CallTree(this.inclusive, this.root); |
| 40 } | 42 } |
| 41 | 43 |
| 42 class CodeCallTree extends CallTree { | 44 class CodeCallTree extends CallTree<CodeCallTreeNode> |
| 45 implements M.CodeCallTree { | |
| 43 CodeCallTree(bool inclusive, CodeCallTreeNode root) | 46 CodeCallTree(bool inclusive, CodeCallTreeNode root) |
| 44 : super(inclusive, root) { | 47 : super(inclusive, root) { |
| 45 _setCodePercentage(null, root); | 48 _setCodePercentage(null, root); |
| 46 } | 49 } |
| 47 | 50 |
| 48 CodeCallTree filtered(CallTreeNodeFilter filter) { | 51 CodeCallTree filtered(CallTreeNodeFilter filter) { |
| 49 var treeFilter = new _FilteredCodeCallTreeBuilder(filter, this); | 52 var treeFilter = new _FilteredCodeCallTreeBuilder(filter, this); |
| 50 treeFilter.build(); | 53 treeFilter.build(); |
| 51 _setCodePercentage(null, treeFilter.filtered.root); | 54 _setCodePercentage(null, treeFilter.filtered.root); |
| 52 return treeFilter.filtered; | 55 return treeFilter.filtered; |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 298 : super(filter, tree, | 301 : super(filter, tree, |
| 299 new CodeCallTree(tree.inclusive, | 302 new CodeCallTree(tree.inclusive, |
| 300 new CodeCallTreeNode(tree.root.profileData, | 303 new CodeCallTreeNode(tree.root.profileData, |
| 301 tree.root.count))); | 304 tree.root.count))); |
| 302 | 305 |
| 303 _copyNode(CodeCallTreeNode node) { | 306 _copyNode(CodeCallTreeNode node) { |
| 304 return new CodeCallTreeNode(node.profileData, node.count); | 307 return new CodeCallTreeNode(node.profileData, node.count); |
| 305 } | 308 } |
| 306 } | 309 } |
| 307 | 310 |
| 308 class FunctionCallTree extends CallTree { | 311 class FunctionCallTree extends CallTree implements M.FunctionCallTree { |
| 309 FunctionCallTree(bool inclusive, FunctionCallTreeNode root) | 312 FunctionCallTree(bool inclusive, FunctionCallTreeNode root) |
| 310 : super(inclusive, root) { | 313 : super(inclusive, root) { |
| 311 _setFunctionPercentage(null, root); | 314 _setFunctionPercentage(null, root); |
| 312 } | 315 } |
| 313 | 316 |
| 314 FunctionCallTree filtered(CallTreeNodeFilter filter) { | 317 FunctionCallTree filtered(CallTreeNodeFilter filter) { |
| 315 var treeFilter = new _FilteredFunctionCallTreeBuilder(filter, this); | 318 var treeFilter = new _FilteredFunctionCallTreeBuilder(filter, this); |
| 316 treeFilter.build(); | 319 treeFilter.build(); |
| 317 _setFunctionPercentage(null, treeFilter.filtered.root); | 320 _setFunctionPercentage(null, treeFilter.filtered.root); |
| 318 return treeFilter.filtered; | 321 return treeFilter.filtered; |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 363 | 366 |
| 364 class InlineIntervalTick { | 367 class InlineIntervalTick { |
| 365 final int startAddress; | 368 final int startAddress; |
| 366 int _inclusiveTicks = 0; | 369 int _inclusiveTicks = 0; |
| 367 int get inclusiveTicks => _inclusiveTicks; | 370 int get inclusiveTicks => _inclusiveTicks; |
| 368 int _exclusiveTicks = 0; | 371 int _exclusiveTicks = 0; |
| 369 int get exclusiveTicks => _exclusiveTicks; | 372 int get exclusiveTicks => _exclusiveTicks; |
| 370 InlineIntervalTick(this.startAddress); | 373 InlineIntervalTick(this.startAddress); |
| 371 } | 374 } |
| 372 | 375 |
| 373 class ProfileCode { | 376 class ProfileCode implements M.ProfileCode { |
| 374 final CpuProfile profile; | 377 final CpuProfile profile; |
| 375 final Code code; | 378 final Code code; |
| 376 int exclusiveTicks; | 379 int exclusiveTicks; |
| 377 int inclusiveTicks; | 380 int inclusiveTicks; |
| 378 double normalizedExclusiveTicks = 0.0; | 381 double normalizedExclusiveTicks = 0.0; |
| 379 double normalizedInclusiveTicks = 0.0; | 382 double normalizedInclusiveTicks = 0.0; |
| 380 final addressTicks = new Map<int, CodeTick>(); | 383 final addressTicks = new Map<int, CodeTick>(); |
| 381 final intervalTicks = new Map<int, InlineIntervalTick>(); | 384 final intervalTicks = new Map<int, InlineIntervalTick>(); |
| 382 String formattedInclusiveTicks = ''; | 385 String formattedInclusiveTicks = ''; |
| 383 String formattedExclusiveTicks = ''; | 386 String formattedExclusiveTicks = ''; |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 481 | 484 |
| 482 _recordCallee(ProfileCode callee, int count) { | 485 _recordCallee(ProfileCode callee, int count) { |
| 483 var r = callees[callee]; | 486 var r = callees[callee]; |
| 484 if (r == null) { | 487 if (r == null) { |
| 485 r = 0; | 488 r = 0; |
| 486 } | 489 } |
| 487 callees[callee] = r + count; | 490 callees[callee] = r + count; |
| 488 } | 491 } |
| 489 } | 492 } |
| 490 | 493 |
| 491 class ProfileFunction { | 494 class ProfileFunction implements M.ProfileFunction { |
| 492 final CpuProfile profile; | 495 final CpuProfile profile; |
| 493 final ServiceFunction function; | 496 final ServiceFunction function; |
| 494 // List of compiled code objects containing this function. | 497 // List of compiled code objects containing this function. |
| 495 final List<ProfileCode> profileCodes = new List<ProfileCode>(); | 498 final List<ProfileCode> profileCodes = new List<ProfileCode>(); |
| 496 final Map<ProfileFunction, int> callers = new Map<ProfileFunction, int>(); | 499 final Map<ProfileFunction, int> callers = new Map<ProfileFunction, int>(); |
| 497 final Map<ProfileFunction, int> callees = new Map<ProfileFunction, int>(); | 500 final Map<ProfileFunction, int> callees = new Map<ProfileFunction, int>(); |
| 498 | 501 |
| 499 // Absolute ticks: | 502 // Absolute ticks: |
| 500 int exclusiveTicks = 0; | 503 int exclusiveTicks = 0; |
| 501 int inclusiveTicks = 0; | 504 int inclusiveTicks = 0; |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 633 var r = callees[callee]; | 636 var r = callees[callee]; |
| 634 if (r == null) { | 637 if (r == null) { |
| 635 r = 0; | 638 r = 0; |
| 636 } | 639 } |
| 637 callees[callee] = r + count; | 640 callees[callee] = r + count; |
| 638 } | 641 } |
| 639 } | 642 } |
| 640 | 643 |
| 641 | 644 |
| 642 // TODO(johnmccutchan): Rename to SampleProfile | 645 // TODO(johnmccutchan): Rename to SampleProfile |
| 643 class CpuProfile { | 646 class CpuProfile extends M.SampleProfile { |
| 644 final double MICROSECONDS_PER_SECOND = 1000000.0; | 647 static const double MICROSECONDS_PER_SECOND = 1000000.0; |
|
Cutch
2016/08/04 15:35:52
Please switch this to use the constants defined on
cbernaschina
2016/08/04 19:45:08
Done.
| |
| 645 final double displayThreshold = 0.0002; // 0.02%. | 648 static const double MICROSECONDS_PER_MILLISECOND = 1000.0; |
| 649 static const double displayThreshold = 0.0002; // 0.02%. | |
|
Cutch
2016/08/04 15:35:52
please remove the displayThreshold (it's unnecessa
cbernaschina
2016/08/04 19:45:08
Done.
| |
| 646 | 650 |
| 647 Isolate isolate; | 651 Isolate isolate; |
| 648 | 652 |
| 649 int sampleCount = 0; | 653 int sampleCount = 0; |
| 650 int samplePeriod = 0; | 654 int samplePeriod = 0; |
| 651 double sampleRate = 0.0; | 655 double sampleRate = 0.0; |
| 652 | 656 |
| 653 int stackDepth = 0; | 657 int stackDepth = 0; |
| 654 | 658 |
| 655 double timeSpan = 0.0; | 659 double timeSpan = 0.0; |
| 656 | 660 |
| 657 final Map<String, List> tries = <String, List>{}; | 661 final Map<String, List> tries = <String, List>{}; |
| 658 final List<ProfileCode> codes = new List<ProfileCode>(); | 662 final List<ProfileCode> codes = new List<ProfileCode>(); |
| 659 bool _builtCodeCalls = false; | 663 bool _builtCodeCalls = false; |
| 660 final List<ProfileFunction> functions = new List<ProfileFunction>(); | 664 final List<ProfileFunction> functions = new List<ProfileFunction>(); |
| 661 bool _builtFunctionCalls = false; | 665 bool _builtFunctionCalls = false; |
| 662 | 666 |
| 663 CodeCallTree loadCodeTree(String name) { | 667 CodeCallTree loadCodeTree(M.ProfileTreeDirection direction) { |
| 664 if (name == 'inclusive') { | 668 switch (direction) { |
| 665 return _loadCodeTree(true, tries['inclusiveCodeTrie']); | 669 case M.ProfileTreeDirection.inclusive: |
| 666 } else { | 670 return _loadCodeTree(true, tries['inclusiveCodeTrie']); |
| 667 return _loadCodeTree(false, tries['exclusiveCodeTrie']); | 671 case M.ProfileTreeDirection.exclusive: |
| 672 return _loadCodeTree(false, tries['exclusiveCodeTrie']); | |
| 668 } | 673 } |
| 674 throw new Exception('Unknown ProfileTreeDirection'); | |
| 669 } | 675 } |
| 670 | 676 |
| 671 FunctionCallTree loadFunctionTree(String name) { | 677 FunctionCallTree loadFunctionTree(M.ProfileTreeDirection direction) { |
| 672 if (name == 'inclusive') { | 678 switch (direction) { |
| 673 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']); | 679 case M.ProfileTreeDirection.inclusive: |
| 674 } else { | 680 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']); |
| 675 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']); | 681 case M.ProfileTreeDirection.exclusive: |
| 682 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']); | |
| 676 } | 683 } |
| 684 throw new Exception('Unknown ProfileTreeDirection'); | |
| 677 } | 685 } |
| 678 | 686 |
| 679 buildCodeCallerAndCallees() { | 687 buildCodeCallerAndCallees() { |
| 680 if (_builtCodeCalls) { | 688 if (_builtCodeCalls) { |
| 681 return; | 689 return; |
| 682 } | 690 } |
| 683 _builtCodeCalls = true; | 691 _builtCodeCalls = true; |
| 684 var tree = loadCodeTree('inclusive'); | 692 var tree = loadCodeTree(M.ProfileTreeDirection.inclusive); |
| 685 tree._recordCallerAndCallees(); | 693 tree._recordCallerAndCallees(); |
| 686 } | 694 } |
| 687 | 695 |
| 688 buildFunctionCallerAndCallees() { | 696 buildFunctionCallerAndCallees() { |
| 689 if (_builtFunctionCalls) { | 697 if (_builtFunctionCalls) { |
| 690 return; | 698 return; |
| 691 } | 699 } |
| 692 _builtFunctionCalls = true; | 700 _builtFunctionCalls = true; |
| 693 var tree = loadFunctionTree('inclusive'); | 701 var tree = loadFunctionTree(M.ProfileTreeDirection.inclusive); |
| 694 tree._markFunctionCalls(); | 702 tree._markFunctionCalls(); |
| 695 } | 703 } |
| 696 | 704 |
| 697 clear() { | 705 clear() { |
| 698 sampleCount = 0; | 706 sampleCount = 0; |
| 699 samplePeriod = 0; | 707 samplePeriod = 0; |
| 700 sampleRate = 0.0; | 708 sampleRate = 0.0; |
| 701 stackDepth = 0; | 709 stackDepth = 0; |
| 702 timeSpan = 0.0; | 710 timeSpan = 0.0; |
| 703 codes.clear(); | 711 codes.clear(); |
| 704 functions.clear(); | 712 functions.clear(); |
| 705 tries.clear(); | 713 tries.clear(); |
| 706 _builtCodeCalls = false; | 714 _builtCodeCalls = false; |
| 707 _builtFunctionCalls = false; | 715 _builtFunctionCalls = false; |
| 708 } | 716 } |
| 709 | 717 |
| 710 load(Isolate isolate, ServiceMap profile) { | 718 Future load(Isolate isolate, ServiceMap profile) async { |
| 711 clear(); | 719 await loadProgress(isolate, profile).last; |
| 712 if ((isolate == null) || (profile == null)) { | 720 } |
| 713 return; | |
| 714 } | |
| 715 | 721 |
| 716 this.isolate = isolate; | 722 static Future sleep([Duration duration = const Duration(microseconds: 0)]) { |
| 717 isolate.resetCachedProfileData(); | 723 final Completer completer = new Completer(); |
| 724 new Timer(duration, () => completer.complete() ); | |
| 725 return completer.future; | |
| 726 } | |
| 718 | 727 |
| 719 sampleCount = profile['sampleCount']; | 728 Stream<double> loadProgress(Isolate isolate, ServiceMap profile) { |
| 720 samplePeriod = profile['samplePeriod']; | 729 var progress = new StreamController<double>.broadcast(); |
| 721 sampleRate = (MICROSECONDS_PER_SECOND / samplePeriod); | |
| 722 stackDepth = profile['stackDepth']; | |
| 723 timeSpan = profile['timeSpan']; | |
| 724 | 730 |
| 725 // Process code table. | 731 (() async { |
| 726 for (var codeRegion in profile['codes']) { | 732 final Stopwatch watch = new Stopwatch(); |
| 727 Code code = codeRegion['code']; | 733 watch.start(); |
| 728 assert(code != null); | 734 int count = 0; |
| 729 codes.add(new ProfileCode.fromMap(this, code, codeRegion)); | 735 var needToUpdate = () { |
| 730 } | 736 count++; |
| 737 if (count % 256 == 0 && watch.elapsedMilliseconds > 16) { | |
| 738 watch.reset(); | |
| 739 return true; | |
| 740 } | |
| 741 return false; | |
| 742 }; | |
| 743 var signal = (double p) { | |
| 744 progress.add(p); | |
| 745 return sleep(); | |
| 746 }; | |
| 747 try { | |
| 748 clear(); | |
| 749 progress.add(0.0); | |
| 750 if ((isolate == null) || (profile == null)) { | |
| 751 return; | |
| 752 } | |
| 731 | 753 |
| 732 // Process function table. | 754 this.isolate = isolate; |
| 733 for (var profileFunction in profile['functions']) { | 755 isolate.resetCachedProfileData(); |
| 734 ServiceFunction function = profileFunction['function']; | |
| 735 assert(function != null); | |
| 736 functions.add( | |
| 737 new ProfileFunction.fromMap(this, function, profileFunction)); | |
| 738 } | |
| 739 | 756 |
| 740 tries['exclusiveCodeTrie'] = | 757 sampleCount = profile['sampleCount']; |
| 741 new Uint32List.fromList(profile['exclusiveCodeTrie']); | 758 samplePeriod = profile['samplePeriod']; |
| 742 tries['inclusiveCodeTrie'] = | 759 sampleRate = (MICROSECONDS_PER_SECOND / samplePeriod); |
| 743 new Uint32List.fromList(profile['inclusiveCodeTrie']); | 760 stackDepth = profile['stackDepth']; |
| 744 tries['exclusiveFunctionTrie'] = | 761 timeSpan = profile['timeSpan']; |
| 745 new Uint32List.fromList(profile['exclusiveFunctionTrie']); | 762 |
| 746 tries['inclusiveFunctionTrie'] = | 763 num length = profile['codes'].length + |
| 747 new Uint32List.fromList(profile['inclusiveFunctionTrie']); | 764 profile['functions'].length; |
| 765 | |
| 766 // Process code table. | |
| 767 for (var codeRegion in profile['codes']) { | |
| 768 if (needToUpdate()) { await signal(count * 100.0 / length); } | |
| 769 Code code = codeRegion['code']; | |
| 770 assert(code != null); | |
| 771 codes.add(new ProfileCode.fromMap(this, code, codeRegion)); | |
| 772 } | |
| 773 // Process function table. | |
| 774 for (var profileFunction in profile['functions']) { | |
| 775 if (needToUpdate()) { await signal(count * 100 / length); } | |
| 776 ServiceFunction function = profileFunction['function']; | |
| 777 assert(function != null); | |
| 778 functions.add( | |
| 779 new ProfileFunction.fromMap(this, function, profileFunction)); | |
| 780 } | |
| 781 | |
| 782 tries['exclusiveCodeTrie'] = | |
| 783 new Uint32List.fromList(profile['exclusiveCodeTrie']); | |
| 784 tries['inclusiveCodeTrie'] = | |
| 785 new Uint32List.fromList(profile['inclusiveCodeTrie']); | |
| 786 tries['exclusiveFunctionTrie'] = | |
| 787 new Uint32List.fromList(profile['exclusiveFunctionTrie']); | |
| 788 tries['inclusiveFunctionTrie'] = | |
| 789 new Uint32List.fromList(profile['inclusiveFunctionTrie']); | |
| 790 } finally { | |
| 791 progress.close(); | |
| 792 } | |
| 793 }()); | |
| 794 return progress.stream; | |
| 748 } | 795 } |
| 749 | 796 |
| 750 // Data shared across calls to _read*TrieNode. | 797 // Data shared across calls to _read*TrieNode. |
| 751 int _dataCursor = 0; | 798 int _dataCursor = 0; |
| 752 | 799 |
| 753 // The code trie is serialized as a list of integers. Each node | 800 // The code trie is serialized as a list of integers. Each node |
| 754 // is recreated by consuming some portion of the list. The format is as | 801 // is recreated by consuming some portion of the list. The format is as |
| 755 // follows: | 802 // follows: |
| 756 // [0] index into codeTable of code object. | 803 // [0] index into codeTable of code object. |
| 757 // [1] tick count (number of times this stack frame occured). | 804 // [1] tick count (number of times this stack frame occured). |
| (...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 911 if (node.children.length > 0) { | 958 if (node.children.length > 0) { |
| 912 nodeStack.add(node); | 959 nodeStack.add(node); |
| 913 childIndexStack.add(0); | 960 childIndexStack.add(0); |
| 914 } | 961 } |
| 915 } | 962 } |
| 916 | 963 |
| 917 return root; | 964 return root; |
| 918 } | 965 } |
| 919 | 966 |
| 920 int approximateMillisecondsForCount(count) { | 967 int approximateMillisecondsForCount(count) { |
| 921 var MICROSECONDS_PER_MILLISECOND = 1000.0; | |
| 922 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; | 968 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; |
| 923 } | 969 } |
| 924 | 970 |
| 925 double approximateSecondsForCount(count) { | 971 double approximateSecondsForCount(count) { |
| 926 var MICROSECONDS_PER_SECOND = 1000000.0; | |
| 927 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; | 972 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; |
| 928 } | 973 } |
| 929 } | 974 } |
| OLD | NEW |