| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 service; | 5 part of service; |
| 6 | 6 |
| 7 /// A [ServiceObject] represents a persistent object within the vm. | 7 /// A [ServiceObject] represents a persistent object within the vm. |
| 8 abstract class ServiceObject extends Observable { | 8 abstract class ServiceObject extends Observable { |
| 9 static int LexicalSortName(ServiceObject o1, ServiceObject o2) { | 9 static int LexicalSortName(ServiceObject o1, ServiceObject o2) { |
| 10 return o1.name.compareTo(o2.name); | 10 return o1.name.compareTo(o2.name); |
| (...skipping 559 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 570 }, test: (e) => e is ServiceError).catchError((exception) { | 570 }, test: (e) => e is ServiceError).catchError((exception) { |
| 571 | 571 |
| 572 // ServiceException, forward to VM's ServiceException stream. | 572 // ServiceException, forward to VM's ServiceException stream. |
| 573 exceptions.add(exception); | 573 exceptions.add(exception); |
| 574 return new Future.error(exception); | 574 return new Future.error(exception); |
| 575 }, test: (e) => e is ServiceException); | 575 }, test: (e) => e is ServiceException); |
| 576 } | 576 } |
| 577 | 577 |
| 578 Future<ServiceObject> invokeRpc(String method, Map params) { | 578 Future<ServiceObject> invokeRpc(String method, Map params) { |
| 579 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { | 579 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { |
| 580 » var obj = new ServiceObject._fromMap(this, response); | 580 var obj = new ServiceObject._fromMap(this, response); |
| 581 if (obj.canCache) { | 581 if ((obj != null) && obj.canCache) { |
| 582 _cache.putIfAbsent(id, () => obj); | 582 String objId = obj.id; |
| 583 } | 583 _cache.putIfAbsent(objId, () => obj); |
| 584 return obj; | 584 } |
| 585 return obj; |
| 585 }); | 586 }); |
| 586 } | 587 } |
| 587 | 588 |
| 588 Future<ObservableMap> _fetchDirect() { | 589 Future<ObservableMap> _fetchDirect() { |
| 589 return invokeRpcNoUpgrade('getVM', {}); | 590 return invokeRpcNoUpgrade('getVM', {}); |
| 590 } | 591 } |
| 591 | 592 |
| 592 Future<ServiceObject> getFlagList() { | 593 Future<ServiceObject> getFlagList() { |
| 593 return invokeRpc('getFlagList', {}); | 594 return invokeRpc('getFlagList', {}); |
| 594 } | 595 } |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 770 @observable bool loading = true; | 771 @observable bool loading = true; |
| 771 @observable bool ioEnabled = false; | 772 @observable bool ioEnabled = false; |
| 772 | 773 |
| 773 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); | 774 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); |
| 774 final TagProfile tagProfile = new TagProfile(20); | 775 final TagProfile tagProfile = new TagProfile(20); |
| 775 | 776 |
| 776 Isolate._empty(ServiceObjectOwner owner) : super._empty(owner) { | 777 Isolate._empty(ServiceObjectOwner owner) : super._empty(owner) { |
| 777 assert(owner is VM); | 778 assert(owner is VM); |
| 778 } | 779 } |
| 779 | 780 |
| 780 static const TAG_ROOT_ID = 'code/tag-0'; | 781 void resetCachedProfileData() { |
| 781 | |
| 782 /// Returns the Code object for the root tag. | |
| 783 Code tagRoot() { | |
| 784 // TODO(turnidge): Use get() here instead? | |
| 785 return _cache[TAG_ROOT_ID]; | |
| 786 } | |
| 787 | |
| 788 void processProfile(ServiceMap profile) { | |
| 789 assert(profile.type == 'CpuProfile'); | |
| 790 var codeTable = new List<Code>(); | |
| 791 var codeRegions = profile['codes']; | |
| 792 for (var codeRegion in codeRegions) { | |
| 793 Code code = codeRegion['code']; | |
| 794 assert(code != null); | |
| 795 codeTable.add(code); | |
| 796 } | |
| 797 _resetProfileData(); | |
| 798 _updateProfileData(profile, codeTable); | |
| 799 var exclusiveTrie = profile['exclusive_trie']; | |
| 800 if (exclusiveTrie != null) { | |
| 801 profileTrieRoot = _processProfileTrie(exclusiveTrie, codeTable); | |
| 802 } | |
| 803 } | |
| 804 | |
| 805 void _resetProfileData() { | |
| 806 _cache.values.forEach((value) { | 782 _cache.values.forEach((value) { |
| 807 if (value is Code) { | 783 if (value is Code) { |
| 808 Code code = value; | 784 Code code = value; |
| 809 code.resetProfileData(); | 785 code.profile = null; |
| 810 } | 786 } else if (value is ServiceFunction) { |
| 811 }); | 787 ServiceFunction function = value; |
| 812 } | 788 function.profile = null; |
| 813 | 789 } |
| 814 void _updateProfileData(ServiceMap profile, List<Code> codeTable) { | 790 }); |
| 815 var codeRegions = profile['codes']; | |
| 816 var sampleCount = profile['samples']; | |
| 817 for (var codeRegion in codeRegions) { | |
| 818 Code code = codeRegion['code']; | |
| 819 code.updateProfileData(codeRegion, codeTable, sampleCount); | |
| 820 } | |
| 821 } | 791 } |
| 822 | 792 |
| 823 /// Fetches and builds the class hierarchy for this isolate. Returns the | 793 /// Fetches and builds the class hierarchy for this isolate. Returns the |
| 824 /// Object class object. | 794 /// Object class object. |
| 825 Future<Class> getClassHierarchy() { | 795 Future<Class> getClassHierarchy() { |
| 826 return invokeRpc('getClassList', {}) | 796 return invokeRpc('getClassList', {}) |
| 827 .then(_loadClasses) | 797 .then(_loadClasses) |
| 828 .then(_buildClassHierarchy); | 798 .then(_buildClassHierarchy); |
| 829 } | 799 } |
| 830 | 800 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 854 } | 824 } |
| 855 } | 825 } |
| 856 assert(objectClass != null); | 826 assert(objectClass != null); |
| 857 return new Future.value(objectClass); | 827 return new Future.value(objectClass); |
| 858 } | 828 } |
| 859 | 829 |
| 860 ServiceObject getFromMap(ObservableMap map) { | 830 ServiceObject getFromMap(ObservableMap map) { |
| 861 if (map == null) { | 831 if (map == null) { |
| 862 return null; | 832 return null; |
| 863 } | 833 } |
| 864 String id = map['id']; | 834 String mapId = map['id']; |
| 865 var obj = _cache[id]; | 835 var obj = (mapId != null) ? _cache[mapId] : null; |
| 866 if (obj != null) { | 836 if (obj != null) { |
| 867 // Consider calling update when map is not a reference. | 837 // Consider calling update when map is not a reference. |
| 868 return obj; | 838 return obj; |
| 869 } | 839 } |
| 870 // Build the object from the map directly. | 840 // Build the object from the map directly. |
| 871 obj = new ServiceObject._fromMap(this, map); | 841 obj = new ServiceObject._fromMap(this, map); |
| 872 if (obj != null && obj.canCache) { | 842 if ((obj != null) && obj.canCache) { |
| 873 _cache[id] = obj; | 843 _cache[mapId] = obj; |
| 874 } | 844 } |
| 875 return obj; | 845 return obj; |
| 876 } | 846 } |
| 877 | 847 |
| 878 Future<ObservableMap> invokeRpcNoUpgrade(String method, Map params) { | 848 Future<ObservableMap> invokeRpcNoUpgrade(String method, Map params) { |
| 879 params['isolateId'] = id; | 849 params['isolateId'] = id; |
| 880 return vm.invokeRpcNoUpgrade(method, params); | 850 return vm.invokeRpcNoUpgrade(method, params); |
| 881 } | 851 } |
| 882 | 852 |
| 883 Future<ServiceObject> invokeRpc(String method, Map params) { | 853 Future<ServiceObject> invokeRpc(String method, Map params) { |
| 884 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { | 854 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { |
| 885 var obj = new ServiceObject._fromMap(this, response); | 855 var obj = new ServiceObject._fromMap(this, response); |
| 886 if (obj.canCache) { | 856 if ((obj != null) && obj.canCache) { |
| 887 _cache.putIfAbsent(id, () => obj); | 857 String objId = obj.id; |
| 888 } | 858 _cache.putIfAbsent(objId, () => obj); |
| 889 » return obj; | 859 } |
| 860 return obj; |
| 890 }); | 861 }); |
| 891 } | 862 } |
| 892 | 863 |
| 893 Future<ServiceObject> getObject(String objectId) { | 864 Future<ServiceObject> getObject(String objectId) { |
| 894 assert(objectId != null && objectId != ''); | 865 assert(objectId != null && objectId != ''); |
| 895 var obj = _cache[objectId]; | 866 var obj = _cache[objectId]; |
| 896 if (obj != null) { | 867 if (obj != null) { |
| 897 return obj.reload(); | 868 return obj.reload(); |
| 898 } | 869 } |
| 899 Map params = { | 870 Map params = { |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1035 | 1006 |
| 1036 Future<TagProfile> updateTagProfile() { | 1007 Future<TagProfile> updateTagProfile() { |
| 1037 return isolate.invokeRpcNoUpgrade('getTagProfile', {}).then( | 1008 return isolate.invokeRpcNoUpgrade('getTagProfile', {}).then( |
| 1038 (ObservableMap map) { | 1009 (ObservableMap map) { |
| 1039 var seconds = new DateTime.now().millisecondsSinceEpoch / 1000.0; | 1010 var seconds = new DateTime.now().millisecondsSinceEpoch / 1000.0; |
| 1040 tagProfile._processTagProfile(seconds, map); | 1011 tagProfile._processTagProfile(seconds, map); |
| 1041 return tagProfile; | 1012 return tagProfile; |
| 1042 }); | 1013 }); |
| 1043 } | 1014 } |
| 1044 | 1015 |
| 1045 @reflectable CodeTrieNode profileTrieRoot; | |
| 1046 // The profile trie is serialized as a list of integers. Each node | |
| 1047 // is recreated by consuming some portion of the list. The format is as | |
| 1048 // follows: | |
| 1049 // [0] index into codeTable of code object. | |
| 1050 // [1] tick count (number of times this stack frame occured). | |
| 1051 // [2] child node count | |
| 1052 // Reading the trie is done by recursively reading the tree depth-first | |
| 1053 // pre-order. | |
| 1054 CodeTrieNode _processProfileTrie(List<int> data, List<Code> codeTable) { | |
| 1055 // Setup state shared across calls to _readTrieNode. | |
| 1056 _trieDataCursor = 0; | |
| 1057 _trieData = data; | |
| 1058 if (_trieData == null) { | |
| 1059 return null; | |
| 1060 } | |
| 1061 if (_trieData.length < 3) { | |
| 1062 // Not enough integers for 1 node. | |
| 1063 return null; | |
| 1064 } | |
| 1065 // Read the tree, returns the root node. | |
| 1066 return _readTrieNode(codeTable); | |
| 1067 } | |
| 1068 int _trieDataCursor; | |
| 1069 List<int> _trieData; | |
| 1070 CodeTrieNode _readTrieNode(List<Code> codeTable) { | |
| 1071 // Read index into code table. | |
| 1072 var index = _trieData[_trieDataCursor++]; | |
| 1073 // Lookup code object. | |
| 1074 var code = codeTable[index]; | |
| 1075 // Frame counter. | |
| 1076 var count = _trieData[_trieDataCursor++]; | |
| 1077 // Create node. | |
| 1078 var node = new CodeTrieNode(code, count); | |
| 1079 // Number of children. | |
| 1080 var children = _trieData[_trieDataCursor++]; | |
| 1081 // Recursively read child nodes. | |
| 1082 for (var i = 0; i < children; i++) { | |
| 1083 var child = _readTrieNode(codeTable); | |
| 1084 node.children.add(child); | |
| 1085 node.summedChildCount += child.count; | |
| 1086 } | |
| 1087 return node; | |
| 1088 } | |
| 1089 | |
| 1090 ObservableList<Breakpoint> breakpoints = new ObservableList(); | 1016 ObservableList<Breakpoint> breakpoints = new ObservableList(); |
| 1091 | 1017 |
| 1092 void _removeBreakpoint(Breakpoint bpt) { | 1018 void _removeBreakpoint(Breakpoint bpt) { |
| 1093 var script = bpt.script; | 1019 var script = bpt.script; |
| 1094 var tokenPos = bpt.tokenPos; | 1020 var tokenPos = bpt.tokenPos; |
| 1095 assert(tokenPos != null); | 1021 assert(tokenPos != null); |
| 1096 if (script.loaded) { | 1022 if (script.loaded) { |
| 1097 var line = script.tokenToLine(tokenPos); | 1023 var line = script.tokenToLine(tokenPos); |
| 1098 assert(line != null); | 1024 assert(line != null); |
| 1099 if (script.lines[line - 1] != null) { | 1025 if (script.lines[line - 1] != null) { |
| (...skipping 761 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1861 | 1787 |
| 1862 String toString() => 'Context($length)'; | 1788 String toString() => 'Context($length)'; |
| 1863 } | 1789 } |
| 1864 | 1790 |
| 1865 | 1791 |
| 1866 // TODO(koda): Sync this with VM. | 1792 // TODO(koda): Sync this with VM. |
| 1867 class FunctionKind { | 1793 class FunctionKind { |
| 1868 final String _strValue; | 1794 final String _strValue; |
| 1869 FunctionKind._internal(this._strValue); | 1795 FunctionKind._internal(this._strValue); |
| 1870 toString() => _strValue; | 1796 toString() => _strValue; |
| 1871 bool isFake() => [kCollected, kNative, kTag, kReused].contains(this); | 1797 bool isSynthetic() => [kCollected, kNative, kTag, kReused].contains(this); |
| 1872 | 1798 |
| 1873 static FunctionKind fromJSON(String value) { | 1799 static FunctionKind fromJSON(String value) { |
| 1874 switch(value) { | 1800 switch(value) { |
| 1875 case 'kRegularFunction': return kRegularFunction; | 1801 case 'kRegularFunction': return kRegularFunction; |
| 1876 case 'kClosureFunction': return kClosureFunction; | 1802 case 'kClosureFunction': return kClosureFunction; |
| 1877 case 'kGetterFunction': return kGetterFunction; | 1803 case 'kGetterFunction': return kGetterFunction; |
| 1878 case 'kSetterFunction': return kSetterFunction; | 1804 case 'kSetterFunction': return kSetterFunction; |
| 1879 case 'kConstructor': return kConstructor; | 1805 case 'kConstructor': return kConstructor; |
| 1880 case 'kImplicitGetter': return kImplicitGetterFunction; | 1806 case 'kImplicitGetter': return kImplicitGetterFunction; |
| 1881 case 'kImplicitSetter': return kImplicitSetterFunction; | 1807 case 'kImplicitSetter': return kImplicitSetterFunction; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1920 @observable int endTokenPos; | 1846 @observable int endTokenPos; |
| 1921 @observable Code code; | 1847 @observable Code code; |
| 1922 @observable Code unoptimizedCode; | 1848 @observable Code unoptimizedCode; |
| 1923 @observable bool isOptimizable; | 1849 @observable bool isOptimizable; |
| 1924 @observable bool isInlinable; | 1850 @observable bool isInlinable; |
| 1925 @observable FunctionKind kind; | 1851 @observable FunctionKind kind; |
| 1926 @observable int deoptimizations; | 1852 @observable int deoptimizations; |
| 1927 @observable String qualifiedName; | 1853 @observable String qualifiedName; |
| 1928 @observable int usageCounter; | 1854 @observable int usageCounter; |
| 1929 @observable bool isDart; | 1855 @observable bool isDart; |
| 1856 @observable ProfileFunction profile; |
| 1857 |
| 1858 bool get canCache => true; |
| 1859 bool get immutable => false; |
| 1930 | 1860 |
| 1931 ServiceFunction._empty(ServiceObject owner) : super._empty(owner); | 1861 ServiceFunction._empty(ServiceObject owner) : super._empty(owner); |
| 1932 | 1862 |
| 1933 void _update(ObservableMap map, bool mapIsRef) { | 1863 void _update(ObservableMap map, bool mapIsRef) { |
| 1934 name = map['name']; | 1864 name = map['name']; |
| 1935 vmName = (map.containsKey('vmName') ? map['vmName'] : name); | 1865 vmName = (map.containsKey('vmName') ? map['vmName'] : name); |
| 1936 | 1866 |
| 1937 _upgradeCollection(map, isolate); | 1867 _upgradeCollection(map, isolate); |
| 1938 | 1868 |
| 1939 owningClass = map.containsKey('owningClass') ? map['owningClass'] : null; | 1869 owningClass = map.containsKey('owningClass') ? map['owningClass'] : null; |
| 1940 owningLibrary = map.containsKey('owningLibrary') ? map['owningLibrary'] : nu
ll; | 1870 owningLibrary = map.containsKey('owningLibrary') ? map['owningLibrary'] : nu
ll; |
| 1941 kind = FunctionKind.fromJSON(map['kind']); | 1871 kind = FunctionKind.fromJSON(map['kind']); |
| 1942 isDart = !kind.isFake(); | 1872 isDart = !kind.isSynthetic(); |
| 1943 | 1873 |
| 1944 if (parent == null) { | 1874 if (parent == null) { |
| 1945 qualifiedName = (owningClass != null) ? | 1875 qualifiedName = (owningClass != null) ? |
| 1946 "${owningClass.name}.${name}" : | 1876 "${owningClass.name}.${name}" : |
| 1947 name; | 1877 name; |
| 1948 } else { | 1878 } else { |
| 1949 qualifiedName = "${parent.qualifiedName}.${name}"; | 1879 qualifiedName = "${parent.qualifiedName}.${name}"; |
| 1950 } | 1880 } |
| 1951 | 1881 |
| 1952 if (mapIsRef) { return; } | 1882 if (mapIsRef) { |
| 1883 return; |
| 1884 } |
| 1953 | 1885 |
| 1954 isStatic = map['static']; | 1886 isStatic = map['static']; |
| 1955 isConst = map['const']; | 1887 isConst = map['const']; |
| 1956 parent = map['parent']; | 1888 parent = map['parent']; |
| 1957 script = map['script']; | 1889 script = map['script']; |
| 1958 tokenPos = map['tokenPos']; | 1890 tokenPos = map['tokenPos']; |
| 1959 endTokenPos = map['endTokenPos']; | 1891 endTokenPos = map['endTokenPos']; |
| 1960 code = _convertNull(map['code']); | 1892 code = _convertNull(map['code']); |
| 1961 unoptimizedCode = _convertNull(map['unoptimizedCode']); | 1893 unoptimizedCode = _convertNull(map['unoptimizedCode']); |
| 1962 isOptimizable = map['optimizable']; | 1894 isOptimizable = map['optimizable']; |
| 1963 isInlinable = map['inlinable']; | 1895 isInlinable = map['inlinable']; |
| 1964 deoptimizations = map['deoptimizations']; | 1896 deoptimizations = map['deoptimizations']; |
| 1965 usageCounter = map['usageCounter']; | 1897 usageCounter = map['usageCounter']; |
| 1966 | |
| 1967 } | 1898 } |
| 1968 } | 1899 } |
| 1969 | 1900 |
| 1970 | 1901 |
| 1971 class Field extends ServiceObject { | 1902 class Field extends ServiceObject { |
| 1972 @observable var /* Library or Class */ owner; | 1903 @observable var /* Library or Class */ owner; |
| 1973 @observable Instance declaredType; | 1904 @observable Instance declaredType; |
| 1974 @observable bool isStatic; | 1905 @observable bool isStatic; |
| 1975 @observable bool isFinal; | 1906 @observable bool isFinal; |
| 1976 @observable bool isConst; | 1907 @observable bool isConst; |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2199 } | 2130 } |
| 2200 | 2131 |
| 2201 void _applyHitsToLines() { | 2132 void _applyHitsToLines() { |
| 2202 for (var line in lines) { | 2133 for (var line in lines) { |
| 2203 var hits = _hits[line.line]; | 2134 var hits = _hits[line.line]; |
| 2204 line.hits = hits; | 2135 line.hits = hits; |
| 2205 } | 2136 } |
| 2206 } | 2137 } |
| 2207 } | 2138 } |
| 2208 | 2139 |
| 2209 class CodeTick { | |
| 2210 final int address; | |
| 2211 final int exclusiveTicks; | |
| 2212 final int inclusiveTicks; | |
| 2213 CodeTick(this.address, this.exclusiveTicks, this.inclusiveTicks); | |
| 2214 } | |
| 2215 | |
| 2216 class PcDescriptor extends Observable { | 2140 class PcDescriptor extends Observable { |
| 2217 final int pcOffset; | 2141 final int pcOffset; |
| 2218 @reflectable final int deoptId; | 2142 @reflectable final int deoptId; |
| 2219 @reflectable final int tokenPos; | 2143 @reflectable final int tokenPos; |
| 2220 @reflectable final int tryIndex; | 2144 @reflectable final int tryIndex; |
| 2221 @reflectable final String kind; | 2145 @reflectable final String kind; |
| 2222 @observable Script script; | 2146 @observable Script script; |
| 2223 @observable String formattedLine; | 2147 @observable String formattedLine; |
| 2224 PcDescriptor(this.pcOffset, this.deoptId, this.tokenPos, this.tryIndex, | 2148 PcDescriptor(this.pcOffset, this.deoptId, this.tokenPos, this.tryIndex, |
| 2225 this.kind); | 2149 this.kind); |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2349 | 2273 |
| 2350 class CodeInstruction extends Observable { | 2274 class CodeInstruction extends Observable { |
| 2351 @observable final int address; | 2275 @observable final int address; |
| 2352 @observable final int pcOffset; | 2276 @observable final int pcOffset; |
| 2353 @observable final String machine; | 2277 @observable final String machine; |
| 2354 @observable final String human; | 2278 @observable final String human; |
| 2355 @observable CodeInstruction jumpTarget; | 2279 @observable CodeInstruction jumpTarget; |
| 2356 @reflectable List<PcDescriptor> descriptors = | 2280 @reflectable List<PcDescriptor> descriptors = |
| 2357 new ObservableList<PcDescriptor>(); | 2281 new ObservableList<PcDescriptor>(); |
| 2358 | 2282 |
| 2359 static String formatPercent(num a, num total) { | |
| 2360 var percent = 100.0 * (a / total); | |
| 2361 return '${percent.toStringAsFixed(2)}%'; | |
| 2362 } | |
| 2363 | |
| 2364 CodeInstruction(this.address, this.pcOffset, this.machine, this.human); | 2283 CodeInstruction(this.address, this.pcOffset, this.machine, this.human); |
| 2365 | 2284 |
| 2366 @reflectable bool get isComment => address == 0; | 2285 @reflectable bool get isComment => address == 0; |
| 2367 @reflectable bool get hasDescriptors => descriptors.length > 0; | 2286 @reflectable bool get hasDescriptors => descriptors.length > 0; |
| 2368 | 2287 |
| 2369 @reflectable String formattedAddress() { | |
| 2370 if (address == 0) { | |
| 2371 return ''; | |
| 2372 } | |
| 2373 return '0x${address.toRadixString(16)}'; | |
| 2374 } | |
| 2375 | |
| 2376 @reflectable String formattedInclusive(Code code) { | |
| 2377 if (code == null) { | |
| 2378 return ''; | |
| 2379 } | |
| 2380 var tick = code.addressTicks[address]; | |
| 2381 if (tick == null) { | |
| 2382 return ''; | |
| 2383 } | |
| 2384 // Don't show inclusive ticks if they are the same as exclusive ticks. | |
| 2385 if (tick.inclusiveTicks == tick.exclusiveTicks) { | |
| 2386 return ''; | |
| 2387 } | |
| 2388 var pcent = formatPercent(tick.inclusiveTicks, code.totalSamplesInProfile); | |
| 2389 return '$pcent (${tick.inclusiveTicks})'; | |
| 2390 } | |
| 2391 | |
| 2392 @reflectable String formattedExclusive(Code code) { | |
| 2393 if (code == null) { | |
| 2394 return ''; | |
| 2395 } | |
| 2396 var tick = code.addressTicks[address]; | |
| 2397 if (tick == null) { | |
| 2398 return ''; | |
| 2399 } | |
| 2400 var pcent = formatPercent(tick.exclusiveTicks, code.totalSamplesInProfile); | |
| 2401 return '$pcent (${tick.exclusiveTicks})'; | |
| 2402 } | |
| 2403 | |
| 2404 bool _isJumpInstruction() { | 2288 bool _isJumpInstruction() { |
| 2405 return human.startsWith('j'); | 2289 return human.startsWith('j'); |
| 2406 } | 2290 } |
| 2407 | 2291 |
| 2408 int _getJumpAddress() { | 2292 int _getJumpAddress() { |
| 2409 assert(_isJumpInstruction()); | 2293 assert(_isJumpInstruction()); |
| 2410 var chunks = human.split(' '); | 2294 var chunks = human.split(' '); |
| 2411 if (chunks.length != 2) { | 2295 if (chunks.length != 2) { |
| 2412 // We expect jump instructions to be of the form 'j.. address'. | 2296 // We expect jump instructions to be of the form 'j.. address'. |
| 2413 return 0; | 2297 return 0; |
| 2414 } | 2298 } |
| 2415 var address = chunks[1]; | 2299 var address = chunks[1]; |
| 2416 if (address.startsWith('0x')) { | 2300 if (address.startsWith('0x')) { |
| 2417 // Chop off the 0x. | 2301 // Chop off the 0x. |
| 2418 address = address.substring(2); | 2302 address = address.substring(2); |
| 2419 } | 2303 } |
| 2420 try { | 2304 try { |
| 2421 return int.parse(address, radix:16); | 2305 return int.parse(address, radix:16); |
| 2422 } catch (_) { | 2306 } catch (_) { |
| 2423 return 0; | 2307 return 0; |
| 2424 } | 2308 } |
| 2425 } | 2309 } |
| 2426 | 2310 |
| 2427 void _resolveJumpTarget(List<CodeInstruction> instructions) { | 2311 void _resolveJumpTarget(List<CodeInstruction> instructions) { |
| 2428 if (!_isJumpInstruction()) { | 2312 if (!_isJumpInstruction()) { |
| 2429 return; | 2313 return; |
| 2430 } | 2314 } |
| 2431 int address = _getJumpAddress(); | 2315 int address = _getJumpAddress(); |
| 2432 if (address == 0) { | 2316 if (address == 0) { |
| 2433 // Could not determine jump address. | |
| 2434 Logger.root.severe('Could not determine jump address for $human'); | |
| 2435 return; | 2317 return; |
| 2436 } | 2318 } |
| 2437 for (var i = 0; i < instructions.length; i++) { | 2319 for (var i = 0; i < instructions.length; i++) { |
| 2438 var instruction = instructions[i]; | 2320 var instruction = instructions[i]; |
| 2439 if (instruction.address == address) { | 2321 if (instruction.address == address) { |
| 2440 jumpTarget = instruction; | 2322 jumpTarget = instruction; |
| 2441 return; | 2323 return; |
| 2442 } | 2324 } |
| 2443 } | 2325 } |
| 2444 Logger.root.severe( | |
| 2445 'Could not find instruction at ${address.toRadixString(16)}'); | |
| 2446 } | 2326 } |
| 2447 } | 2327 } |
| 2448 | 2328 |
| 2449 class CodeKind { | 2329 class CodeKind { |
| 2450 final _value; | 2330 final _value; |
| 2451 const CodeKind._internal(this._value); | 2331 const CodeKind._internal(this._value); |
| 2452 String toString() => '$_value'; | 2332 String toString() => '$_value'; |
| 2453 | 2333 |
| 2454 static CodeKind fromString(String s) { | 2334 static CodeKind fromString(String s) { |
| 2455 if (s == 'Native') { | 2335 if (s == 'Native') { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 2466 Logger.root.warning('Unknown code kind $s'); | 2346 Logger.root.warning('Unknown code kind $s'); |
| 2467 throw new FallThroughError(); | 2347 throw new FallThroughError(); |
| 2468 } | 2348 } |
| 2469 static const Native = const CodeKind._internal('Native'); | 2349 static const Native = const CodeKind._internal('Native'); |
| 2470 static const Dart = const CodeKind._internal('Dart'); | 2350 static const Dart = const CodeKind._internal('Dart'); |
| 2471 static const Collected = const CodeKind._internal('Collected'); | 2351 static const Collected = const CodeKind._internal('Collected'); |
| 2472 static const Reused = const CodeKind._internal('Reused'); | 2352 static const Reused = const CodeKind._internal('Reused'); |
| 2473 static const Tag = const CodeKind._internal('Tag'); | 2353 static const Tag = const CodeKind._internal('Tag'); |
| 2474 } | 2354 } |
| 2475 | 2355 |
| 2476 class CodeCallCount { | 2356 class CodeInlineInterval { |
| 2477 final Code code; | 2357 final int start; |
| 2478 final int count; | 2358 final int end; |
| 2479 CodeCallCount(this.code, this.count); | 2359 final List<ServiceFunction> functions = new List<ServiceFunction>(); |
| 2480 } | 2360 bool contains(int pc) => (pc >= start) && (pc < end); |
| 2481 | 2361 CodeInlineInterval(this.start, this.end); |
| 2482 class CodeTrieNode { | |
| 2483 final Code code; | |
| 2484 final int count; | |
| 2485 final children = new List<CodeTrieNode>(); | |
| 2486 int summedChildCount = 0; | |
| 2487 CodeTrieNode(this.code, this.count); | |
| 2488 } | 2362 } |
| 2489 | 2363 |
| 2490 class Code extends ServiceObject { | 2364 class Code extends ServiceObject { |
| 2491 @observable CodeKind kind; | 2365 @observable CodeKind kind; |
| 2492 @observable int totalSamplesInProfile = 0; | |
| 2493 @reflectable int exclusiveTicks = 0; | |
| 2494 @reflectable int inclusiveTicks = 0; | |
| 2495 @reflectable int startAddress = 0; | |
| 2496 @reflectable int endAddress = 0; | |
| 2497 @reflectable final callers = new List<CodeCallCount>(); | |
| 2498 @reflectable final callees = new List<CodeCallCount>(); | |
| 2499 @reflectable final instructions = new ObservableList<CodeInstruction>(); | |
| 2500 @reflectable final addressTicks = new ObservableMap<int, CodeTick>(); | |
| 2501 @observable String formattedInclusiveTicks = ''; | |
| 2502 @observable String formattedExclusiveTicks = ''; | |
| 2503 @observable Instance objectPool; | 2366 @observable Instance objectPool; |
| 2504 @observable ServiceFunction function; | 2367 @observable ServiceFunction function; |
| 2505 @observable Script script; | 2368 @observable Script script; |
| 2506 @observable bool isOptimized = false; | 2369 @observable bool isOptimized = false; |
| 2507 | 2370 @reflectable int startAddress = 0; |
| 2371 @reflectable int endAddress = 0; |
| 2372 @reflectable final instructions = new ObservableList<CodeInstruction>(); |
| 2373 @observable ProfileCode profile; |
| 2374 final List<CodeInlineInterval> inlineIntervals = |
| 2375 new List<CodeInlineInterval>(); |
| 2376 final ObservableList<ServiceFunction> inlinedFunctions = |
| 2377 new ObservableList<ServiceFunction>(); |
| 2508 bool get canCache => true; | 2378 bool get canCache => true; |
| 2509 bool get immutable => true; | 2379 bool get immutable => true; |
| 2510 | 2380 |
| 2511 Code._empty(ServiceObjectOwner owner) : super._empty(owner); | 2381 Code._empty(ServiceObjectOwner owner) : super._empty(owner); |
| 2512 | 2382 |
| 2513 // Reset all data associated with a profile. | |
| 2514 void resetProfileData() { | |
| 2515 totalSamplesInProfile = 0; | |
| 2516 exclusiveTicks = 0; | |
| 2517 inclusiveTicks = 0; | |
| 2518 formattedInclusiveTicks = ''; | |
| 2519 formattedExclusiveTicks = ''; | |
| 2520 callers.clear(); | |
| 2521 callees.clear(); | |
| 2522 addressTicks.clear(); | |
| 2523 } | |
| 2524 | |
| 2525 void _updateDescriptors(Script script) { | 2383 void _updateDescriptors(Script script) { |
| 2526 this.script = script; | 2384 this.script = script; |
| 2527 for (var instruction in instructions) { | 2385 for (var instruction in instructions) { |
| 2528 for (var descriptor in instruction.descriptors) { | 2386 for (var descriptor in instruction.descriptors) { |
| 2529 descriptor.processScript(script); | 2387 descriptor.processScript(script); |
| 2530 } | 2388 } |
| 2531 } | 2389 } |
| 2532 } | 2390 } |
| 2533 | 2391 |
| 2534 void loadScript() { | 2392 void loadScript() { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 2563 /// a [ServiceError]. | 2421 /// a [ServiceError]. |
| 2564 Future<ServiceObject> reload() { | 2422 Future<ServiceObject> reload() { |
| 2565 assert(kind != null); | 2423 assert(kind != null); |
| 2566 if (kind == CodeKind.Dart) { | 2424 if (kind == CodeKind.Dart) { |
| 2567 // We only reload Dart code. | 2425 // We only reload Dart code. |
| 2568 return super.reload(); | 2426 return super.reload(); |
| 2569 } | 2427 } |
| 2570 return new Future.value(this); | 2428 return new Future.value(this); |
| 2571 } | 2429 } |
| 2572 | 2430 |
| 2573 void _resolveCalls(List<CodeCallCount> calls, List data, List<Code> codes) { | |
| 2574 // Assert that this has been cleared. | |
| 2575 assert(calls.length == 0); | |
| 2576 // Resolve. | |
| 2577 for (var i = 0; i < data.length; i += 2) { | |
| 2578 var index = int.parse(data[i]); | |
| 2579 var count = int.parse(data[i + 1]); | |
| 2580 assert(index >= 0); | |
| 2581 assert(index < codes.length); | |
| 2582 calls.add(new CodeCallCount(codes[index], count)); | |
| 2583 } | |
| 2584 // Sort to descending count order. | |
| 2585 calls.sort((a, b) => b.count - a.count); | |
| 2586 } | |
| 2587 | |
| 2588 | |
| 2589 static String formatPercent(num a, num total) { | |
| 2590 var percent = 100.0 * (a / total); | |
| 2591 return '${percent.toStringAsFixed(2)}%'; | |
| 2592 } | |
| 2593 | |
| 2594 void updateProfileData(Map profileData, | |
| 2595 List<Code> codeTable, | |
| 2596 int sampleCount) { | |
| 2597 // Assert we are handed profile data for this code object. | |
| 2598 assert(profileData['code'] == this); | |
| 2599 totalSamplesInProfile = sampleCount; | |
| 2600 inclusiveTicks = int.parse(profileData['inclusive_ticks']); | |
| 2601 exclusiveTicks = int.parse(profileData['exclusive_ticks']); | |
| 2602 _resolveCalls(callers, profileData['callers'], codeTable); | |
| 2603 _resolveCalls(callees, profileData['callees'], codeTable); | |
| 2604 var ticks = profileData['ticks']; | |
| 2605 if (ticks != null) { | |
| 2606 _processTicks(ticks); | |
| 2607 } | |
| 2608 formattedInclusiveTicks = | |
| 2609 '${formatPercent(inclusiveTicks, totalSamplesInProfile)} ' | |
| 2610 '($inclusiveTicks)'; | |
| 2611 formattedExclusiveTicks = | |
| 2612 '${formatPercent(exclusiveTicks, totalSamplesInProfile)} ' | |
| 2613 '($exclusiveTicks)'; | |
| 2614 } | |
| 2615 | |
| 2616 void _update(ObservableMap m, bool mapIsRef) { | 2431 void _update(ObservableMap m, bool mapIsRef) { |
| 2617 name = m['name']; | 2432 name = m['name']; |
| 2618 vmName = (m.containsKey('vmName') ? m['vmName'] : name); | 2433 vmName = (m.containsKey('vmName') ? m['vmName'] : name); |
| 2619 isOptimized = m['optimized'] != null ? m['optimized'] : false; | 2434 isOptimized = m['optimized'] != null ? m['optimized'] : false; |
| 2620 kind = CodeKind.fromString(m['kind']); | 2435 kind = CodeKind.fromString(m['kind']); |
| 2621 startAddress = int.parse(m['start'], radix:16); | 2436 startAddress = int.parse(m['start'], radix:16); |
| 2622 endAddress = int.parse(m['end'], radix:16); | 2437 endAddress = int.parse(m['end'], radix:16); |
| 2623 function = isolate.getFromMap(m['function']); | 2438 function = isolate.getFromMap(m['function']); |
| 2439 if (mapIsRef) { |
| 2440 return; |
| 2441 } |
| 2442 _loaded = true; |
| 2624 objectPool = isolate.getFromMap(m['objectPool']); | 2443 objectPool = isolate.getFromMap(m['objectPool']); |
| 2625 var disassembly = m['disassembly']; | 2444 var disassembly = m['disassembly']; |
| 2626 if (disassembly != null) { | 2445 if (disassembly != null) { |
| 2627 _processDisassembly(disassembly); | 2446 _processDisassembly(disassembly); |
| 2628 } | 2447 } |
| 2629 var descriptors = m['descriptors']; | 2448 var descriptors = m['descriptors']; |
| 2630 if (descriptors != null) { | 2449 if (descriptors != null) { |
| 2631 descriptors = descriptors['members']; | 2450 descriptors = descriptors['members']; |
| 2632 _processDescriptors(descriptors); | 2451 _processDescriptors(descriptors); |
| 2633 } | 2452 } |
| 2634 // We are loaded if we have instructions or are not Dart code. | |
| 2635 _loaded = (instructions.length != 0) || (kind != CodeKind.Dart); | |
| 2636 hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart); | 2453 hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart); |
| 2454 inlinedFunctions.clear(); |
| 2455 var inlinedFunctionsTable = m['inlinedFunctions']; |
| 2456 var inlinedIntervals = m['inlinedIntervals']; |
| 2457 if (inlinedFunctionsTable != null) { |
| 2458 // Iterate and upgrade each ServiceFunction. |
| 2459 for (var i = 0; i < inlinedFunctionsTable.length; i++) { |
| 2460 // Upgrade each function and set it back in the list. |
| 2461 var func = isolate.getFromMap(inlinedFunctionsTable[i]); |
| 2462 inlinedFunctionsTable[i] = func; |
| 2463 if (!inlinedFunctions.contains(func)) { |
| 2464 inlinedFunctions.add(func); |
| 2465 } |
| 2466 } |
| 2467 } |
| 2468 if ((inlinedIntervals == null) || (inlinedFunctionsTable == null)) { |
| 2469 // No inline information. |
| 2470 inlineIntervals.clear(); |
| 2471 return; |
| 2472 } |
| 2473 _processInline(inlinedFunctionsTable, inlinedIntervals); |
| 2474 } |
| 2475 |
| 2476 CodeInlineInterval findInterval(int pc) { |
| 2477 for (var i = 0; i < inlineIntervals.length; i++) { |
| 2478 var interval = inlineIntervals[i]; |
| 2479 if (interval.contains(pc)) { |
| 2480 return interval; |
| 2481 } |
| 2482 } |
| 2483 return null; |
| 2484 } |
| 2485 |
| 2486 void _processInline(List<ServiceFunction> inlinedFunctionsTable, |
| 2487 List<List<int>> inlinedIntervals) { |
| 2488 for (var i = 0; i < inlinedIntervals.length; i++) { |
| 2489 var inlinedInterval = inlinedIntervals[i]; |
| 2490 var start = inlinedInterval[0] + startAddress; |
| 2491 var end = inlinedInterval[1] + startAddress; |
| 2492 var codeInlineInterval = new CodeInlineInterval(start, end); |
| 2493 for (var i = 2; i < inlinedInterval.length - 1; i++) { |
| 2494 var inline_id = inlinedInterval[i]; |
| 2495 if (inline_id < 0) { |
| 2496 continue; |
| 2497 } |
| 2498 var function = inlinedFunctionsTable[inline_id]; |
| 2499 codeInlineInterval.functions.add(function); |
| 2500 } |
| 2501 inlineIntervals.add(codeInlineInterval); |
| 2502 } |
| 2637 } | 2503 } |
| 2638 | 2504 |
| 2639 @observable bool hasDisassembly = false; | 2505 @observable bool hasDisassembly = false; |
| 2640 | 2506 |
| 2641 void _processDisassembly(List<String> disassembly){ | 2507 void _processDisassembly(List<String> disassembly){ |
| 2642 assert(disassembly != null); | 2508 assert(disassembly != null); |
| 2643 instructions.clear(); | 2509 instructions.clear(); |
| 2644 assert((disassembly.length % 3) == 0); | 2510 assert((disassembly.length % 3) == 0); |
| 2645 for (var i = 0; i < disassembly.length; i += 3) { | 2511 for (var i = 0; i < disassembly.length; i += 3) { |
| 2646 var address = 0; // Assume code comment. | 2512 var address = 0; // Assume code comment. |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2680 Logger.root.warning( | 2546 Logger.root.warning( |
| 2681 'Could not find instruction with pc descriptor address: $address'); | 2547 'Could not find instruction with pc descriptor address: $address'); |
| 2682 } | 2548 } |
| 2683 | 2549 |
| 2684 void _processDescriptors(List<Map> descriptors) { | 2550 void _processDescriptors(List<Map> descriptors) { |
| 2685 for (Map descriptor in descriptors) { | 2551 for (Map descriptor in descriptors) { |
| 2686 _processDescriptor(descriptor); | 2552 _processDescriptor(descriptor); |
| 2687 } | 2553 } |
| 2688 } | 2554 } |
| 2689 | 2555 |
| 2690 void _processTicks(List<String> profileTicks) { | |
| 2691 assert(profileTicks != null); | |
| 2692 assert((profileTicks.length % 3) == 0); | |
| 2693 for (var i = 0; i < profileTicks.length; i += 3) { | |
| 2694 var address = int.parse(profileTicks[i], radix:16); | |
| 2695 var exclusive = int.parse(profileTicks[i + 1]); | |
| 2696 var inclusive = int.parse(profileTicks[i + 2]); | |
| 2697 var tick = new CodeTick(address, exclusive, inclusive); | |
| 2698 addressTicks[address] = tick; | |
| 2699 } | |
| 2700 } | |
| 2701 | |
| 2702 /// Returns true if [address] is contained inside [this]. | 2556 /// Returns true if [address] is contained inside [this]. |
| 2703 bool contains(int address) { | 2557 bool contains(int address) { |
| 2704 return (address >= startAddress) && (address < endAddress); | 2558 return (address >= startAddress) && (address < endAddress); |
| 2705 } | 2559 } |
| 2706 | 2560 |
| 2707 /// Sum all caller counts. | |
| 2708 int sumCallersCount() => _sumCallCount(callers); | |
| 2709 /// Specific caller count. | |
| 2710 int callersCount(Code code) => _callCount(callers, code); | |
| 2711 /// Sum of callees count. | |
| 2712 int sumCalleesCount() => _sumCallCount(callees); | |
| 2713 /// Specific callee count. | |
| 2714 int calleesCount(Code code) => _callCount(callees, code); | |
| 2715 | |
| 2716 int _sumCallCount(List<CodeCallCount> calls) { | |
| 2717 var sum = 0; | |
| 2718 for (CodeCallCount caller in calls) { | |
| 2719 sum += caller.count; | |
| 2720 } | |
| 2721 return sum; | |
| 2722 } | |
| 2723 | |
| 2724 int _callCount(List<CodeCallCount> calls, Code code) { | |
| 2725 for (CodeCallCount caller in calls) { | |
| 2726 if (caller.code == code) { | |
| 2727 return caller.count; | |
| 2728 } | |
| 2729 } | |
| 2730 return 0; | |
| 2731 } | |
| 2732 | |
| 2733 @reflectable bool get isDartCode => kind == CodeKind.Dart; | 2561 @reflectable bool get isDartCode => kind == CodeKind.Dart; |
| 2734 } | 2562 } |
| 2735 | 2563 |
| 2736 | 2564 |
| 2737 class SocketKind { | 2565 class SocketKind { |
| 2738 final _value; | 2566 final _value; |
| 2739 const SocketKind._internal(this._value); | 2567 const SocketKind._internal(this._value); |
| 2740 String toString() => '$_value'; | 2568 String toString() => '$_value'; |
| 2741 | 2569 |
| 2742 static SocketKind fromString(String s) { | 2570 static SocketKind fromString(String s) { |
| (...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2981 var v = list[i]; | 2809 var v = list[i]; |
| 2982 if ((v is ObservableMap) && _isServiceMap(v)) { | 2810 if ((v is ObservableMap) && _isServiceMap(v)) { |
| 2983 list[i] = owner.getFromMap(v); | 2811 list[i] = owner.getFromMap(v); |
| 2984 } else if (v is ObservableList) { | 2812 } else if (v is ObservableList) { |
| 2985 _upgradeObservableList(v, owner); | 2813 _upgradeObservableList(v, owner); |
| 2986 } else if (v is ObservableMap) { | 2814 } else if (v is ObservableMap) { |
| 2987 _upgradeObservableMap(v, owner); | 2815 _upgradeObservableMap(v, owner); |
| 2988 } | 2816 } |
| 2989 } | 2817 } |
| 2990 } | 2818 } |
| OLD | NEW |