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 366 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 // TODO(koda): Do we care about GC events in VM isolate? | 377 // TODO(koda): Do we care about GC events in VM isolate? |
378 Logger.root.severe( | 378 Logger.root.severe( |
379 'Ignoring event with unknown isolate id: $owningIsolateId'); | 379 'Ignoring event with unknown isolate id: $owningIsolateId'); |
380 } else { | 380 } else { |
381 var event = new ServiceObject._fromMap(owningIsolate, map); | 381 var event = new ServiceObject._fromMap(owningIsolate, map); |
382 events.add(event); | 382 events.add(event); |
383 } | 383 } |
384 }); | 384 }); |
385 } | 385 } |
386 | 386 |
387 static final RegExp _currentIsolateMatcher = new RegExp(r'isolates/\d+'); | |
388 static final RegExp _currentObjectMatcher = new RegExp(r'isolates/\d+/'); | |
389 static final String _isolatesPrefix = 'isolates/'; | |
390 | |
391 String _parseObjectId(String id) { | |
392 Match m = _currentObjectMatcher.matchAsPrefix(id); | |
393 if (m == null) { | |
394 return null; | |
395 } | |
396 return m.input.substring(m.end); | |
397 } | |
398 | |
399 String _parseIsolateId(String id) { | |
400 Match m = _currentIsolateMatcher.matchAsPrefix(id); | |
401 if (m == null) { | |
402 return ''; | |
403 } | |
404 return id.substring(0, m.end); | |
405 } | |
406 | |
407 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); | 387 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); |
408 Map<String,Isolate> _isolateCache = new Map<String,Isolate>(); | 388 Map<String,Isolate> _isolateCache = new Map<String,Isolate>(); |
409 | 389 |
410 ServiceObject getFromMap(ObservableMap map) { | 390 ServiceObject getFromMap(ObservableMap map) { |
411 throw new UnimplementedError(); | 391 throw new UnimplementedError(); |
412 } | 392 } |
413 | 393 |
414 // Note that this function does not reload the isolate if it found | 394 // Note that this function does not reload the isolate if it found |
415 // in the cache. | 395 // in the cache. |
416 Future<ServiceObject> getIsolate(String isolateId) { | 396 Future<ServiceObject> getIsolate(String isolateId) { |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
497 }, test: (e) => e is ServiceError).catchError((exception) { | 477 }, test: (e) => e is ServiceError).catchError((exception) { |
498 | 478 |
499 // ServiceException, forward to VM's ServiceException stream. | 479 // ServiceException, forward to VM's ServiceException stream. |
500 exceptions.add(exception); | 480 exceptions.add(exception); |
501 return new Future.error(exception); | 481 return new Future.error(exception); |
502 }, test: (e) => e is ServiceException); | 482 }, test: (e) => e is ServiceException); |
503 } | 483 } |
504 | 484 |
505 Future<ServiceObject> invokeRpc(String method, Map params) { | 485 Future<ServiceObject> invokeRpc(String method, Map params) { |
506 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { | 486 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { |
507 » var obj = new ServiceObject._fromMap(this, response); | 487 var obj = new ServiceObject._fromMap(this, response); |
508 if (obj.canCache) { | 488 if ((obj != null) && obj.canCache) { |
509 _cache.putIfAbsent(id, () => obj); | 489 String objId = obj.id; |
510 } | 490 _cache.putIfAbsent(objId, () => obj); |
511 return obj; | 491 } |
| 492 return obj; |
512 }); | 493 }); |
513 } | 494 } |
514 | 495 |
515 Future<ObservableMap> _fetchDirect() { | 496 Future<ObservableMap> _fetchDirect() { |
516 return invokeRpcNoUpgrade('getVM', {}); | 497 return invokeRpcNoUpgrade('getVM', {}); |
517 } | 498 } |
518 | 499 |
519 Future<ServiceObject> getFlagList() { | 500 Future<ServiceObject> getFlagList() { |
520 return invokeRpc('getFlagList', {}); | 501 return invokeRpc('getFlagList', {}); |
521 } | 502 } |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
707 @observable bool loading = true; | 688 @observable bool loading = true; |
708 @observable bool ioEnabled = false; | 689 @observable bool ioEnabled = false; |
709 | 690 |
710 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); | 691 Map<String,ServiceObject> _cache = new Map<String,ServiceObject>(); |
711 final TagProfile tagProfile = new TagProfile(20); | 692 final TagProfile tagProfile = new TagProfile(20); |
712 | 693 |
713 Isolate._empty(ServiceObjectOwner owner) : super._empty(owner) { | 694 Isolate._empty(ServiceObjectOwner owner) : super._empty(owner) { |
714 assert(owner is VM); | 695 assert(owner is VM); |
715 } | 696 } |
716 | 697 |
717 static const TAG_ROOT_ID = 'code/tag-0'; | 698 void resetCachedProfileData() { |
718 | |
719 /// Returns the Code object for the root tag. | |
720 Code tagRoot() { | |
721 // TODO(turnidge): Use get() here instead? | |
722 return _cache[TAG_ROOT_ID]; | |
723 } | |
724 | |
725 void processProfile(ServiceMap profile) { | |
726 assert(profile.type == 'CpuProfile'); | |
727 var codeTable = new List<Code>(); | |
728 var codeRegions = profile['codes']; | |
729 for (var codeRegion in codeRegions) { | |
730 Code code = codeRegion['code']; | |
731 assert(code != null); | |
732 codeTable.add(code); | |
733 } | |
734 _resetProfileData(); | |
735 _updateProfileData(profile, codeTable); | |
736 var exclusiveTrie = profile['exclusive_trie']; | |
737 if (exclusiveTrie != null) { | |
738 profileTrieRoot = _processProfileTrie(exclusiveTrie, codeTable); | |
739 } | |
740 } | |
741 | |
742 void _resetProfileData() { | |
743 _cache.values.forEach((value) { | 699 _cache.values.forEach((value) { |
744 if (value is Code) { | 700 if (value is Code) { |
745 Code code = value; | 701 Code code = value; |
746 code.resetProfileData(); | 702 code.profile = null; |
747 } | 703 } else if (value is ServiceFunction) { |
748 }); | 704 ServiceFunction function = value; |
749 } | 705 function.profile = null; |
750 | 706 } |
751 void _updateProfileData(ServiceMap profile, List<Code> codeTable) { | 707 }); |
752 var codeRegions = profile['codes']; | |
753 var sampleCount = profile['samples']; | |
754 for (var codeRegion in codeRegions) { | |
755 Code code = codeRegion['code']; | |
756 code.updateProfileData(codeRegion, codeTable, sampleCount); | |
757 } | |
758 } | 708 } |
759 | 709 |
760 /// Fetches and builds the class hierarchy for this isolate. Returns the | 710 /// Fetches and builds the class hierarchy for this isolate. Returns the |
761 /// Object class object. | 711 /// Object class object. |
762 Future<Class> getClassHierarchy() { | 712 Future<Class> getClassHierarchy() { |
763 return invokeRpc('getClassList', {}) | 713 return invokeRpc('getClassList', {}) |
764 .then(_loadClasses) | 714 .then(_loadClasses) |
765 .then(_buildClassHierarchy); | 715 .then(_buildClassHierarchy); |
766 } | 716 } |
767 | 717 |
(...skipping 23 matching lines...) Expand all Loading... |
791 } | 741 } |
792 } | 742 } |
793 assert(objectClass != null); | 743 assert(objectClass != null); |
794 return new Future.value(objectClass); | 744 return new Future.value(objectClass); |
795 } | 745 } |
796 | 746 |
797 ServiceObject getFromMap(ObservableMap map) { | 747 ServiceObject getFromMap(ObservableMap map) { |
798 if (map == null) { | 748 if (map == null) { |
799 return null; | 749 return null; |
800 } | 750 } |
801 String id = map['id']; | 751 String mapId = map['id']; |
802 var obj = _cache[id]; | 752 var obj = (mapId != null) ? _cache[mapId] : null; |
803 if (obj != null) { | 753 if (obj != null) { |
804 // Consider calling update when map is not a reference. | 754 // Consider calling update when map is not a reference. |
805 return obj; | 755 return obj; |
806 } | 756 } |
807 // Build the object from the map directly. | 757 // Build the object from the map directly. |
808 obj = new ServiceObject._fromMap(this, map); | 758 obj = new ServiceObject._fromMap(this, map); |
809 if (obj != null && obj.canCache) { | 759 if ((obj != null) && obj.canCache) { |
810 _cache[id] = obj; | 760 _cache[mapId] = obj; |
811 } | 761 } |
812 return obj; | 762 return obj; |
813 } | 763 } |
814 | 764 |
815 Future<ObservableMap> invokeRpcNoUpgrade(String method, Map params) { | 765 Future<ObservableMap> invokeRpcNoUpgrade(String method, Map params) { |
816 params['isolateId'] = id; | 766 params['isolateId'] = id; |
817 return vm.invokeRpcNoUpgrade(method, params); | 767 return vm.invokeRpcNoUpgrade(method, params); |
818 } | 768 } |
819 | 769 |
820 Future<ServiceObject> invokeRpc(String method, Map params) { | 770 Future<ServiceObject> invokeRpc(String method, Map params) { |
821 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { | 771 return invokeRpcNoUpgrade(method, params).then((ObservableMap response) { |
822 var obj = new ServiceObject._fromMap(this, response); | 772 var obj = new ServiceObject._fromMap(this, response); |
823 if (obj.canCache) { | 773 if ((obj != null) && obj.canCache) { |
824 _cache.putIfAbsent(id, () => obj); | 774 String objId = obj.id; |
825 } | 775 _cache.putIfAbsent(objId, () => obj); |
826 » return obj; | 776 } |
| 777 return obj; |
827 }); | 778 }); |
828 } | 779 } |
829 | 780 |
830 Future<ServiceObject> getObject(String objectId) { | 781 Future<ServiceObject> getObject(String objectId) { |
831 assert(objectId != null && objectId != ''); | 782 assert(objectId != null && objectId != ''); |
832 var obj = _cache[objectId]; | 783 var obj = _cache[objectId]; |
833 if (obj != null) { | 784 if (obj != null) { |
834 return obj.reload(); | 785 return obj.reload(); |
835 } | 786 } |
836 Map params = { | 787 Map params = { |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
972 | 923 |
973 Future<TagProfile> updateTagProfile() { | 924 Future<TagProfile> updateTagProfile() { |
974 return isolate.invokeRpcNoUpgrade('getTagProfile', {}).then( | 925 return isolate.invokeRpcNoUpgrade('getTagProfile', {}).then( |
975 (ObservableMap map) { | 926 (ObservableMap map) { |
976 var seconds = new DateTime.now().millisecondsSinceEpoch / 1000.0; | 927 var seconds = new DateTime.now().millisecondsSinceEpoch / 1000.0; |
977 tagProfile._processTagProfile(seconds, map); | 928 tagProfile._processTagProfile(seconds, map); |
978 return tagProfile; | 929 return tagProfile; |
979 }); | 930 }); |
980 } | 931 } |
981 | 932 |
982 @reflectable CodeTrieNode profileTrieRoot; | |
983 // The profile trie is serialized as a list of integers. Each node | |
984 // is recreated by consuming some portion of the list. The format is as | |
985 // follows: | |
986 // [0] index into codeTable of code object. | |
987 // [1] tick count (number of times this stack frame occured). | |
988 // [2] child node count | |
989 // Reading the trie is done by recursively reading the tree depth-first | |
990 // pre-order. | |
991 CodeTrieNode _processProfileTrie(List<int> data, List<Code> codeTable) { | |
992 // Setup state shared across calls to _readTrieNode. | |
993 _trieDataCursor = 0; | |
994 _trieData = data; | |
995 if (_trieData == null) { | |
996 return null; | |
997 } | |
998 if (_trieData.length < 3) { | |
999 // Not enough integers for 1 node. | |
1000 return null; | |
1001 } | |
1002 // Read the tree, returns the root node. | |
1003 return _readTrieNode(codeTable); | |
1004 } | |
1005 int _trieDataCursor; | |
1006 List<int> _trieData; | |
1007 CodeTrieNode _readTrieNode(List<Code> codeTable) { | |
1008 // Read index into code table. | |
1009 var index = _trieData[_trieDataCursor++]; | |
1010 // Lookup code object. | |
1011 var code = codeTable[index]; | |
1012 // Frame counter. | |
1013 var count = _trieData[_trieDataCursor++]; | |
1014 // Create node. | |
1015 var node = new CodeTrieNode(code, count); | |
1016 // Number of children. | |
1017 var children = _trieData[_trieDataCursor++]; | |
1018 // Recursively read child nodes. | |
1019 for (var i = 0; i < children; i++) { | |
1020 var child = _readTrieNode(codeTable); | |
1021 node.children.add(child); | |
1022 node.summedChildCount += child.count; | |
1023 } | |
1024 return node; | |
1025 } | |
1026 | |
1027 ObservableList<Breakpoint> breakpoints = new ObservableList(); | 933 ObservableList<Breakpoint> breakpoints = new ObservableList(); |
1028 | 934 |
1029 void _removeBreakpoint(Breakpoint bpt) { | 935 void _removeBreakpoint(Breakpoint bpt) { |
1030 var script = bpt.script; | 936 var script = bpt.script; |
1031 var tokenPos = bpt.tokenPos; | 937 var tokenPos = bpt.tokenPos; |
1032 assert(tokenPos != null); | 938 assert(tokenPos != null); |
1033 if (script.loaded) { | 939 if (script.loaded) { |
1034 var line = script.tokenToLine(tokenPos); | 940 var line = script.tokenToLine(tokenPos); |
1035 assert(line != null); | 941 assert(line != null); |
1036 if (script.lines[line - 1] != null) { | 942 if (script.lines[line - 1] != null) { |
(...skipping 761 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1798 | 1704 |
1799 String toString() => 'Context($length)'; | 1705 String toString() => 'Context($length)'; |
1800 } | 1706 } |
1801 | 1707 |
1802 | 1708 |
1803 // TODO(koda): Sync this with VM. | 1709 // TODO(koda): Sync this with VM. |
1804 class FunctionKind { | 1710 class FunctionKind { |
1805 final String _strValue; | 1711 final String _strValue; |
1806 FunctionKind._internal(this._strValue); | 1712 FunctionKind._internal(this._strValue); |
1807 toString() => _strValue; | 1713 toString() => _strValue; |
1808 bool isFake() => [kCollected, kNative, kTag, kReused].contains(this); | 1714 bool isSynthetic() => [kCollected, kNative, kTag, kReused].contains(this); |
1809 | 1715 |
1810 static FunctionKind fromJSON(String value) { | 1716 static FunctionKind fromJSON(String value) { |
1811 switch(value) { | 1717 switch(value) { |
1812 case 'kRegularFunction': return kRegularFunction; | 1718 case 'kRegularFunction': return kRegularFunction; |
1813 case 'kClosureFunction': return kClosureFunction; | 1719 case 'kClosureFunction': return kClosureFunction; |
1814 case 'kGetterFunction': return kGetterFunction; | 1720 case 'kGetterFunction': return kGetterFunction; |
1815 case 'kSetterFunction': return kSetterFunction; | 1721 case 'kSetterFunction': return kSetterFunction; |
1816 case 'kConstructor': return kConstructor; | 1722 case 'kConstructor': return kConstructor; |
1817 case 'kImplicitGetter': return kImplicitGetterFunction; | 1723 case 'kImplicitGetter': return kImplicitGetterFunction; |
1818 case 'kImplicitSetter': return kImplicitSetterFunction; | 1724 case 'kImplicitSetter': return kImplicitSetterFunction; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1857 @observable int endTokenPos; | 1763 @observable int endTokenPos; |
1858 @observable Code code; | 1764 @observable Code code; |
1859 @observable Code unoptimizedCode; | 1765 @observable Code unoptimizedCode; |
1860 @observable bool isOptimizable; | 1766 @observable bool isOptimizable; |
1861 @observable bool isInlinable; | 1767 @observable bool isInlinable; |
1862 @observable FunctionKind kind; | 1768 @observable FunctionKind kind; |
1863 @observable int deoptimizations; | 1769 @observable int deoptimizations; |
1864 @observable String qualifiedName; | 1770 @observable String qualifiedName; |
1865 @observable int usageCounter; | 1771 @observable int usageCounter; |
1866 @observable bool isDart; | 1772 @observable bool isDart; |
| 1773 @observable ProfileFunction profile; |
| 1774 |
| 1775 bool get canCache => true; |
| 1776 bool get immutable => false; |
1867 | 1777 |
1868 ServiceFunction._empty(ServiceObject owner) : super._empty(owner); | 1778 ServiceFunction._empty(ServiceObject owner) : super._empty(owner); |
1869 | 1779 |
1870 void _update(ObservableMap map, bool mapIsRef) { | 1780 void _update(ObservableMap map, bool mapIsRef) { |
1871 name = map['name']; | 1781 name = map['name']; |
1872 vmName = (map.containsKey('vmName') ? map['vmName'] : name); | 1782 vmName = (map.containsKey('vmName') ? map['vmName'] : name); |
1873 | 1783 |
1874 _upgradeCollection(map, isolate); | 1784 _upgradeCollection(map, isolate); |
1875 | 1785 |
1876 owningClass = map.containsKey('owningClass') ? map['owningClass'] : null; | 1786 owningClass = map.containsKey('owningClass') ? map['owningClass'] : null; |
1877 owningLibrary = map.containsKey('owningLibrary') ? map['owningLibrary'] : nu
ll; | 1787 owningLibrary = map.containsKey('owningLibrary') ? map['owningLibrary'] : nu
ll; |
1878 kind = FunctionKind.fromJSON(map['kind']); | 1788 kind = FunctionKind.fromJSON(map['kind']); |
1879 isDart = !kind.isFake(); | 1789 isDart = !kind.isSynthetic(); |
1880 | 1790 |
1881 if (parent == null) { | 1791 if (parent == null) { |
1882 qualifiedName = (owningClass != null) ? | 1792 qualifiedName = (owningClass != null) ? |
1883 "${owningClass.name}.${name}" : | 1793 "${owningClass.name}.${name}" : |
1884 name; | 1794 name; |
1885 } else { | 1795 } else { |
1886 qualifiedName = "${parent.qualifiedName}.${name}"; | 1796 qualifiedName = "${parent.qualifiedName}.${name}"; |
1887 } | 1797 } |
1888 | 1798 |
1889 if (mapIsRef) { return; } | 1799 if (mapIsRef) { |
| 1800 return; |
| 1801 } |
1890 | 1802 |
1891 isStatic = map['static']; | 1803 isStatic = map['static']; |
1892 isConst = map['const']; | 1804 isConst = map['const']; |
1893 parent = map['parent']; | 1805 parent = map['parent']; |
1894 script = map['script']; | 1806 script = map['script']; |
1895 tokenPos = map['tokenPos']; | 1807 tokenPos = map['tokenPos']; |
1896 endTokenPos = map['endTokenPos']; | 1808 endTokenPos = map['endTokenPos']; |
1897 code = _convertNull(map['code']); | 1809 code = _convertNull(map['code']); |
1898 unoptimizedCode = _convertNull(map['unoptimizedCode']); | 1810 unoptimizedCode = _convertNull(map['unoptimizedCode']); |
1899 isOptimizable = map['optimizable']; | 1811 isOptimizable = map['optimizable']; |
1900 isInlinable = map['inlinable']; | 1812 isInlinable = map['inlinable']; |
1901 deoptimizations = map['deoptimizations']; | 1813 deoptimizations = map['deoptimizations']; |
1902 usageCounter = map['usageCounter']; | 1814 usageCounter = map['usageCounter']; |
1903 | |
1904 } | 1815 } |
1905 } | 1816 } |
1906 | 1817 |
1907 | 1818 |
1908 class Field extends ServiceObject { | 1819 class Field extends ServiceObject { |
1909 @observable var /* Library or Class */ owner; | 1820 @observable var /* Library or Class */ owner; |
1910 @observable Instance declaredType; | 1821 @observable Instance declaredType; |
1911 @observable bool isStatic; | 1822 @observable bool isStatic; |
1912 @observable bool isFinal; | 1823 @observable bool isFinal; |
1913 @observable bool isConst; | 1824 @observable bool isConst; |
(...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2136 } | 2047 } |
2137 | 2048 |
2138 void _applyHitsToLines() { | 2049 void _applyHitsToLines() { |
2139 for (var line in lines) { | 2050 for (var line in lines) { |
2140 var hits = _hits[line.line]; | 2051 var hits = _hits[line.line]; |
2141 line.hits = hits; | 2052 line.hits = hits; |
2142 } | 2053 } |
2143 } | 2054 } |
2144 } | 2055 } |
2145 | 2056 |
2146 class CodeTick { | |
2147 final int address; | |
2148 final int exclusiveTicks; | |
2149 final int inclusiveTicks; | |
2150 CodeTick(this.address, this.exclusiveTicks, this.inclusiveTicks); | |
2151 } | |
2152 | |
2153 class PcDescriptor extends Observable { | 2057 class PcDescriptor extends Observable { |
2154 final int pcOffset; | 2058 final int pcOffset; |
2155 @reflectable final int deoptId; | 2059 @reflectable final int deoptId; |
2156 @reflectable final int tokenPos; | 2060 @reflectable final int tokenPos; |
2157 @reflectable final int tryIndex; | 2061 @reflectable final int tryIndex; |
2158 @reflectable final String kind; | 2062 @reflectable final String kind; |
2159 @observable Script script; | 2063 @observable Script script; |
2160 @observable String formattedLine; | 2064 @observable String formattedLine; |
2161 PcDescriptor(this.pcOffset, this.deoptId, this.tokenPos, this.tryIndex, | 2065 PcDescriptor(this.pcOffset, this.deoptId, this.tokenPos, this.tryIndex, |
2162 this.kind); | 2066 this.kind); |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2286 | 2190 |
2287 class CodeInstruction extends Observable { | 2191 class CodeInstruction extends Observable { |
2288 @observable final int address; | 2192 @observable final int address; |
2289 @observable final int pcOffset; | 2193 @observable final int pcOffset; |
2290 @observable final String machine; | 2194 @observable final String machine; |
2291 @observable final String human; | 2195 @observable final String human; |
2292 @observable CodeInstruction jumpTarget; | 2196 @observable CodeInstruction jumpTarget; |
2293 @reflectable List<PcDescriptor> descriptors = | 2197 @reflectable List<PcDescriptor> descriptors = |
2294 new ObservableList<PcDescriptor>(); | 2198 new ObservableList<PcDescriptor>(); |
2295 | 2199 |
2296 static String formatPercent(num a, num total) { | |
2297 var percent = 100.0 * (a / total); | |
2298 return '${percent.toStringAsFixed(2)}%'; | |
2299 } | |
2300 | |
2301 CodeInstruction(this.address, this.pcOffset, this.machine, this.human); | 2200 CodeInstruction(this.address, this.pcOffset, this.machine, this.human); |
2302 | 2201 |
2303 @reflectable bool get isComment => address == 0; | 2202 @reflectable bool get isComment => address == 0; |
2304 @reflectable bool get hasDescriptors => descriptors.length > 0; | 2203 @reflectable bool get hasDescriptors => descriptors.length > 0; |
2305 | 2204 |
2306 @reflectable String formattedAddress() { | |
2307 if (address == 0) { | |
2308 return ''; | |
2309 } | |
2310 return '0x${address.toRadixString(16)}'; | |
2311 } | |
2312 | |
2313 @reflectable String formattedInclusive(Code code) { | |
2314 if (code == null) { | |
2315 return ''; | |
2316 } | |
2317 var tick = code.addressTicks[address]; | |
2318 if (tick == null) { | |
2319 return ''; | |
2320 } | |
2321 // Don't show inclusive ticks if they are the same as exclusive ticks. | |
2322 if (tick.inclusiveTicks == tick.exclusiveTicks) { | |
2323 return ''; | |
2324 } | |
2325 var pcent = formatPercent(tick.inclusiveTicks, code.totalSamplesInProfile); | |
2326 return '$pcent (${tick.inclusiveTicks})'; | |
2327 } | |
2328 | |
2329 @reflectable String formattedExclusive(Code code) { | |
2330 if (code == null) { | |
2331 return ''; | |
2332 } | |
2333 var tick = code.addressTicks[address]; | |
2334 if (tick == null) { | |
2335 return ''; | |
2336 } | |
2337 var pcent = formatPercent(tick.exclusiveTicks, code.totalSamplesInProfile); | |
2338 return '$pcent (${tick.exclusiveTicks})'; | |
2339 } | |
2340 | |
2341 bool _isJumpInstruction() { | 2205 bool _isJumpInstruction() { |
2342 return human.startsWith('j'); | 2206 return human.startsWith('j'); |
2343 } | 2207 } |
2344 | 2208 |
2345 int _getJumpAddress() { | 2209 int _getJumpAddress() { |
2346 assert(_isJumpInstruction()); | 2210 assert(_isJumpInstruction()); |
2347 var chunks = human.split(' '); | 2211 var chunks = human.split(' '); |
2348 if (chunks.length != 2) { | 2212 if (chunks.length != 2) { |
2349 // We expect jump instructions to be of the form 'j.. address'. | 2213 // We expect jump instructions to be of the form 'j.. address'. |
2350 return 0; | 2214 return 0; |
2351 } | 2215 } |
2352 var address = chunks[1]; | 2216 var address = chunks[1]; |
2353 if (address.startsWith('0x')) { | 2217 if (address.startsWith('0x')) { |
2354 // Chop off the 0x. | 2218 // Chop off the 0x. |
2355 address = address.substring(2); | 2219 address = address.substring(2); |
2356 } | 2220 } |
2357 try { | 2221 try { |
2358 return int.parse(address, radix:16); | 2222 return int.parse(address, radix:16); |
2359 } catch (_) { | 2223 } catch (_) { |
2360 return 0; | 2224 return 0; |
2361 } | 2225 } |
2362 } | 2226 } |
2363 | 2227 |
2364 void _resolveJumpTarget(List<CodeInstruction> instructions) { | 2228 void _resolveJumpTarget(List<CodeInstruction> instructions) { |
2365 if (!_isJumpInstruction()) { | 2229 if (!_isJumpInstruction()) { |
2366 return; | 2230 return; |
2367 } | 2231 } |
2368 int address = _getJumpAddress(); | 2232 int address = _getJumpAddress(); |
2369 if (address == 0) { | 2233 if (address == 0) { |
2370 // Could not determine jump address. | |
2371 Logger.root.severe('Could not determine jump address for $human'); | |
2372 return; | 2234 return; |
2373 } | 2235 } |
2374 for (var i = 0; i < instructions.length; i++) { | 2236 for (var i = 0; i < instructions.length; i++) { |
2375 var instruction = instructions[i]; | 2237 var instruction = instructions[i]; |
2376 if (instruction.address == address) { | 2238 if (instruction.address == address) { |
2377 jumpTarget = instruction; | 2239 jumpTarget = instruction; |
2378 return; | 2240 return; |
2379 } | 2241 } |
2380 } | 2242 } |
2381 Logger.root.severe( | |
2382 'Could not find instruction at ${address.toRadixString(16)}'); | |
2383 } | 2243 } |
2384 } | 2244 } |
2385 | 2245 |
2386 class CodeKind { | 2246 class CodeKind { |
2387 final _value; | 2247 final _value; |
2388 const CodeKind._internal(this._value); | 2248 const CodeKind._internal(this._value); |
2389 String toString() => '$_value'; | 2249 String toString() => '$_value'; |
2390 | 2250 |
2391 static CodeKind fromString(String s) { | 2251 static CodeKind fromString(String s) { |
2392 if (s == 'Native') { | 2252 if (s == 'Native') { |
(...skipping 10 matching lines...) Expand all Loading... |
2403 Logger.root.warning('Unknown code kind $s'); | 2263 Logger.root.warning('Unknown code kind $s'); |
2404 throw new FallThroughError(); | 2264 throw new FallThroughError(); |
2405 } | 2265 } |
2406 static const Native = const CodeKind._internal('Native'); | 2266 static const Native = const CodeKind._internal('Native'); |
2407 static const Dart = const CodeKind._internal('Dart'); | 2267 static const Dart = const CodeKind._internal('Dart'); |
2408 static const Collected = const CodeKind._internal('Collected'); | 2268 static const Collected = const CodeKind._internal('Collected'); |
2409 static const Reused = const CodeKind._internal('Reused'); | 2269 static const Reused = const CodeKind._internal('Reused'); |
2410 static const Tag = const CodeKind._internal('Tag'); | 2270 static const Tag = const CodeKind._internal('Tag'); |
2411 } | 2271 } |
2412 | 2272 |
2413 class CodeCallCount { | 2273 class CodeInlineInterval { |
2414 final Code code; | 2274 final int start; |
2415 final int count; | 2275 final int end; |
2416 CodeCallCount(this.code, this.count); | 2276 final List<ServiceFunction> functions = new List<ServiceFunction>(); |
2417 } | 2277 bool contains(int pc) => (pc >= start) && (pc < end); |
2418 | 2278 CodeInlineInterval(this.start, this.end); |
2419 class CodeTrieNode { | |
2420 final Code code; | |
2421 final int count; | |
2422 final children = new List<CodeTrieNode>(); | |
2423 int summedChildCount = 0; | |
2424 CodeTrieNode(this.code, this.count); | |
2425 } | 2279 } |
2426 | 2280 |
2427 class Code extends ServiceObject { | 2281 class Code extends ServiceObject { |
2428 @observable CodeKind kind; | 2282 @observable CodeKind kind; |
2429 @observable int totalSamplesInProfile = 0; | |
2430 @reflectable int exclusiveTicks = 0; | |
2431 @reflectable int inclusiveTicks = 0; | |
2432 @reflectable int startAddress = 0; | |
2433 @reflectable int endAddress = 0; | |
2434 @reflectable final callers = new List<CodeCallCount>(); | |
2435 @reflectable final callees = new List<CodeCallCount>(); | |
2436 @reflectable final instructions = new ObservableList<CodeInstruction>(); | |
2437 @reflectable final addressTicks = new ObservableMap<int, CodeTick>(); | |
2438 @observable String formattedInclusiveTicks = ''; | |
2439 @observable String formattedExclusiveTicks = ''; | |
2440 @observable Instance objectPool; | 2283 @observable Instance objectPool; |
2441 @observable ServiceFunction function; | 2284 @observable ServiceFunction function; |
2442 @observable Script script; | 2285 @observable Script script; |
2443 @observable bool isOptimized = false; | 2286 @observable bool isOptimized = false; |
2444 | 2287 @reflectable int startAddress = 0; |
| 2288 @reflectable int endAddress = 0; |
| 2289 @reflectable final instructions = new ObservableList<CodeInstruction>(); |
| 2290 @observable ProfileCode profile; |
| 2291 final List<CodeInlineInterval> inlineIntervals = |
| 2292 new List<CodeInlineInterval>(); |
| 2293 final ObservableList<ServiceFunction> inlinedFunctions = |
| 2294 new ObservableList<ServiceFunction>(); |
2445 bool get canCache => true; | 2295 bool get canCache => true; |
2446 bool get immutable => true; | 2296 bool get immutable => true; |
2447 | 2297 |
2448 Code._empty(ServiceObjectOwner owner) : super._empty(owner); | 2298 Code._empty(ServiceObjectOwner owner) : super._empty(owner); |
2449 | 2299 |
2450 // Reset all data associated with a profile. | |
2451 void resetProfileData() { | |
2452 totalSamplesInProfile = 0; | |
2453 exclusiveTicks = 0; | |
2454 inclusiveTicks = 0; | |
2455 formattedInclusiveTicks = ''; | |
2456 formattedExclusiveTicks = ''; | |
2457 callers.clear(); | |
2458 callees.clear(); | |
2459 addressTicks.clear(); | |
2460 } | |
2461 | |
2462 void _updateDescriptors(Script script) { | 2300 void _updateDescriptors(Script script) { |
2463 this.script = script; | 2301 this.script = script; |
2464 for (var instruction in instructions) { | 2302 for (var instruction in instructions) { |
2465 for (var descriptor in instruction.descriptors) { | 2303 for (var descriptor in instruction.descriptors) { |
2466 descriptor.processScript(script); | 2304 descriptor.processScript(script); |
2467 } | 2305 } |
2468 } | 2306 } |
2469 } | 2307 } |
2470 | 2308 |
2471 void loadScript() { | 2309 void loadScript() { |
(...skipping 28 matching lines...) Expand all Loading... |
2500 /// a [ServiceError]. | 2338 /// a [ServiceError]. |
2501 Future<ServiceObject> reload() { | 2339 Future<ServiceObject> reload() { |
2502 assert(kind != null); | 2340 assert(kind != null); |
2503 if (kind == CodeKind.Dart) { | 2341 if (kind == CodeKind.Dart) { |
2504 // We only reload Dart code. | 2342 // We only reload Dart code. |
2505 return super.reload(); | 2343 return super.reload(); |
2506 } | 2344 } |
2507 return new Future.value(this); | 2345 return new Future.value(this); |
2508 } | 2346 } |
2509 | 2347 |
2510 void _resolveCalls(List<CodeCallCount> calls, List data, List<Code> codes) { | |
2511 // Assert that this has been cleared. | |
2512 assert(calls.length == 0); | |
2513 // Resolve. | |
2514 for (var i = 0; i < data.length; i += 2) { | |
2515 var index = int.parse(data[i]); | |
2516 var count = int.parse(data[i + 1]); | |
2517 assert(index >= 0); | |
2518 assert(index < codes.length); | |
2519 calls.add(new CodeCallCount(codes[index], count)); | |
2520 } | |
2521 // Sort to descending count order. | |
2522 calls.sort((a, b) => b.count - a.count); | |
2523 } | |
2524 | |
2525 | |
2526 static String formatPercent(num a, num total) { | |
2527 var percent = 100.0 * (a / total); | |
2528 return '${percent.toStringAsFixed(2)}%'; | |
2529 } | |
2530 | |
2531 void updateProfileData(Map profileData, | |
2532 List<Code> codeTable, | |
2533 int sampleCount) { | |
2534 // Assert we are handed profile data for this code object. | |
2535 assert(profileData['code'] == this); | |
2536 totalSamplesInProfile = sampleCount; | |
2537 inclusiveTicks = int.parse(profileData['inclusive_ticks']); | |
2538 exclusiveTicks = int.parse(profileData['exclusive_ticks']); | |
2539 _resolveCalls(callers, profileData['callers'], codeTable); | |
2540 _resolveCalls(callees, profileData['callees'], codeTable); | |
2541 var ticks = profileData['ticks']; | |
2542 if (ticks != null) { | |
2543 _processTicks(ticks); | |
2544 } | |
2545 formattedInclusiveTicks = | |
2546 '${formatPercent(inclusiveTicks, totalSamplesInProfile)} ' | |
2547 '($inclusiveTicks)'; | |
2548 formattedExclusiveTicks = | |
2549 '${formatPercent(exclusiveTicks, totalSamplesInProfile)} ' | |
2550 '($exclusiveTicks)'; | |
2551 } | |
2552 | |
2553 void _update(ObservableMap m, bool mapIsRef) { | 2348 void _update(ObservableMap m, bool mapIsRef) { |
2554 name = m['name']; | 2349 name = m['name']; |
2555 vmName = (m.containsKey('vmName') ? m['vmName'] : name); | 2350 vmName = (m.containsKey('vmName') ? m['vmName'] : name); |
2556 isOptimized = m['optimized'] != null ? m['optimized'] : false; | 2351 isOptimized = m['optimized'] != null ? m['optimized'] : false; |
2557 kind = CodeKind.fromString(m['kind']); | 2352 kind = CodeKind.fromString(m['kind']); |
2558 startAddress = int.parse(m['start'], radix:16); | 2353 startAddress = int.parse(m['start'], radix:16); |
2559 endAddress = int.parse(m['end'], radix:16); | 2354 endAddress = int.parse(m['end'], radix:16); |
2560 function = isolate.getFromMap(m['function']); | 2355 function = isolate.getFromMap(m['function']); |
| 2356 if (mapIsRef) { |
| 2357 return; |
| 2358 } |
| 2359 _loaded = true; |
2561 objectPool = isolate.getFromMap(m['objectPool']); | 2360 objectPool = isolate.getFromMap(m['objectPool']); |
2562 var disassembly = m['disassembly']; | 2361 var disassembly = m['disassembly']; |
2563 if (disassembly != null) { | 2362 if (disassembly != null) { |
2564 _processDisassembly(disassembly); | 2363 _processDisassembly(disassembly); |
2565 } | 2364 } |
2566 var descriptors = m['descriptors']; | 2365 var descriptors = m['descriptors']; |
2567 if (descriptors != null) { | 2366 if (descriptors != null) { |
2568 descriptors = descriptors['members']; | 2367 descriptors = descriptors['members']; |
2569 _processDescriptors(descriptors); | 2368 _processDescriptors(descriptors); |
2570 } | 2369 } |
2571 // We are loaded if we have instructions or are not Dart code. | |
2572 _loaded = (instructions.length != 0) || (kind != CodeKind.Dart); | |
2573 hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart); | 2370 hasDisassembly = (instructions.length != 0) && (kind == CodeKind.Dart); |
| 2371 inlinedFunctions.clear(); |
| 2372 var inlinedFunctionsTable = m['inlinedFunctions']; |
| 2373 var inlinedIntervals = m['inlinedIntervals']; |
| 2374 if (inlinedFunctionsTable != null) { |
| 2375 // Iterate and upgrade each ServiceFunction. |
| 2376 for (var i = 0; i < inlinedFunctionsTable.length; i++) { |
| 2377 // Upgrade each function and set it back in the list. |
| 2378 var func = isolate.getFromMap(inlinedFunctionsTable[i]); |
| 2379 inlinedFunctionsTable[i] = func; |
| 2380 if (!inlinedFunctions.contains(func)) { |
| 2381 inlinedFunctions.add(func); |
| 2382 } |
| 2383 } |
| 2384 } |
| 2385 if ((inlinedIntervals == null) || (inlinedFunctionsTable == null)) { |
| 2386 // No inline information. |
| 2387 inlineIntervals.clear(); |
| 2388 return; |
| 2389 } |
| 2390 _processInline(inlinedFunctionsTable, inlinedIntervals); |
| 2391 } |
| 2392 |
| 2393 CodeInlineInterval findInterval(int pc) { |
| 2394 for (var i = 0; i < inlineIntervals.length; i++) { |
| 2395 var interval = inlineIntervals[i]; |
| 2396 if (interval.contains(pc)) { |
| 2397 return interval; |
| 2398 } |
| 2399 } |
| 2400 return null; |
| 2401 } |
| 2402 |
| 2403 void _processInline(List<ServiceFunction> inlinedFunctionsTable, |
| 2404 List<List<int>> inlinedIntervals) { |
| 2405 for (var i = 0; i < inlinedIntervals.length; i++) { |
| 2406 var inlinedInterval = inlinedIntervals[i]; |
| 2407 var start = inlinedInterval[0] + startAddress; |
| 2408 var end = inlinedInterval[1] + startAddress; |
| 2409 var codeInlineInterval = new CodeInlineInterval(start, end); |
| 2410 for (var i = 2; i < inlinedInterval.length - 1; i++) { |
| 2411 var inline_id = inlinedInterval[i]; |
| 2412 if (inline_id < 0) { |
| 2413 continue; |
| 2414 } |
| 2415 var function = inlinedFunctionsTable[inline_id]; |
| 2416 codeInlineInterval.functions.add(function); |
| 2417 } |
| 2418 inlineIntervals.add(codeInlineInterval); |
| 2419 } |
2574 } | 2420 } |
2575 | 2421 |
2576 @observable bool hasDisassembly = false; | 2422 @observable bool hasDisassembly = false; |
2577 | 2423 |
2578 void _processDisassembly(List<String> disassembly){ | 2424 void _processDisassembly(List<String> disassembly){ |
2579 assert(disassembly != null); | 2425 assert(disassembly != null); |
2580 instructions.clear(); | 2426 instructions.clear(); |
2581 assert((disassembly.length % 3) == 0); | 2427 assert((disassembly.length % 3) == 0); |
2582 for (var i = 0; i < disassembly.length; i += 3) { | 2428 for (var i = 0; i < disassembly.length; i += 3) { |
2583 var address = 0; // Assume code comment. | 2429 var address = 0; // Assume code comment. |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2617 Logger.root.warning( | 2463 Logger.root.warning( |
2618 'Could not find instruction with pc descriptor address: $address'); | 2464 'Could not find instruction with pc descriptor address: $address'); |
2619 } | 2465 } |
2620 | 2466 |
2621 void _processDescriptors(List<Map> descriptors) { | 2467 void _processDescriptors(List<Map> descriptors) { |
2622 for (Map descriptor in descriptors) { | 2468 for (Map descriptor in descriptors) { |
2623 _processDescriptor(descriptor); | 2469 _processDescriptor(descriptor); |
2624 } | 2470 } |
2625 } | 2471 } |
2626 | 2472 |
2627 void _processTicks(List<String> profileTicks) { | |
2628 assert(profileTicks != null); | |
2629 assert((profileTicks.length % 3) == 0); | |
2630 for (var i = 0; i < profileTicks.length; i += 3) { | |
2631 var address = int.parse(profileTicks[i], radix:16); | |
2632 var exclusive = int.parse(profileTicks[i + 1]); | |
2633 var inclusive = int.parse(profileTicks[i + 2]); | |
2634 var tick = new CodeTick(address, exclusive, inclusive); | |
2635 addressTicks[address] = tick; | |
2636 } | |
2637 } | |
2638 | |
2639 /// Returns true if [address] is contained inside [this]. | 2473 /// Returns true if [address] is contained inside [this]. |
2640 bool contains(int address) { | 2474 bool contains(int address) { |
2641 return (address >= startAddress) && (address < endAddress); | 2475 return (address >= startAddress) && (address < endAddress); |
2642 } | 2476 } |
2643 | 2477 |
2644 /// Sum all caller counts. | |
2645 int sumCallersCount() => _sumCallCount(callers); | |
2646 /// Specific caller count. | |
2647 int callersCount(Code code) => _callCount(callers, code); | |
2648 /// Sum of callees count. | |
2649 int sumCalleesCount() => _sumCallCount(callees); | |
2650 /// Specific callee count. | |
2651 int calleesCount(Code code) => _callCount(callees, code); | |
2652 | |
2653 int _sumCallCount(List<CodeCallCount> calls) { | |
2654 var sum = 0; | |
2655 for (CodeCallCount caller in calls) { | |
2656 sum += caller.count; | |
2657 } | |
2658 return sum; | |
2659 } | |
2660 | |
2661 int _callCount(List<CodeCallCount> calls, Code code) { | |
2662 for (CodeCallCount caller in calls) { | |
2663 if (caller.code == code) { | |
2664 return caller.count; | |
2665 } | |
2666 } | |
2667 return 0; | |
2668 } | |
2669 | |
2670 @reflectable bool get isDartCode => kind == CodeKind.Dart; | 2478 @reflectable bool get isDartCode => kind == CodeKind.Dart; |
2671 } | 2479 } |
2672 | 2480 |
2673 | 2481 |
2674 class SocketKind { | 2482 class SocketKind { |
2675 final _value; | 2483 final _value; |
2676 const SocketKind._internal(this._value); | 2484 const SocketKind._internal(this._value); |
2677 String toString() => '$_value'; | 2485 String toString() => '$_value'; |
2678 | 2486 |
2679 static SocketKind fromString(String s) { | 2487 static SocketKind fromString(String s) { |
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2918 var v = list[i]; | 2726 var v = list[i]; |
2919 if ((v is ObservableMap) && _isServiceMap(v)) { | 2727 if ((v is ObservableMap) && _isServiceMap(v)) { |
2920 list[i] = owner.getFromMap(v); | 2728 list[i] = owner.getFromMap(v); |
2921 } else if (v is ObservableList) { | 2729 } else if (v is ObservableList) { |
2922 _upgradeObservableList(v, owner); | 2730 _upgradeObservableList(v, owner); |
2923 } else if (v is ObservableMap) { | 2731 } else if (v is ObservableMap) { |
2924 _upgradeObservableMap(v, owner); | 2732 _upgradeObservableMap(v, owner); |
2925 } | 2733 } |
2926 } | 2734 } |
2927 } | 2735 } |
OLD | NEW |