Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 part of cpu_profiler; | |
| 6 | |
| 7 class CodeTrieNode { | |
| 8 final ProfileCode profileCode; | |
| 9 final int count; | |
| 10 final children = new List<CodeTrieNode>(); | |
| 11 CodeTrieNode(this.profileCode, this.count); | |
| 12 } | |
| 13 | |
| 14 class FunctionTrieNodeCode { | |
| 15 final ProfileCode code; | |
| 16 final int ticks; | |
| 17 FunctionTrieNodeCode(this.code, this.ticks); | |
| 18 } | |
| 19 | |
| 20 class FunctionTrieNode { | |
| 21 final ProfileFunction profileFunction; | |
| 22 final int count; | |
| 23 final children = new List<FunctionTrieNode>(); | |
| 24 final codes = new List<FunctionTrieNodeCode>(); | |
| 25 int _totalCodeTicks = 0; | |
| 26 int get totalCodesTicks => _totalCodeTicks; | |
| 27 FunctionTrieNode(this.profileFunction, this.count); | |
| 28 } | |
| 29 | |
| 30 class CodeTick { | |
| 31 final int address; | |
| 32 final int exclusiveTicks; | |
| 33 final int inclusiveTicks; | |
| 34 CodeTick(this.address, this.exclusiveTicks, this.inclusiveTicks); | |
| 35 } | |
| 36 | |
| 37 class InlineIntervalTick { | |
| 38 final int startAddress; | |
| 39 int _inclusiveTicks = 0; | |
| 40 int get inclusiveTicks => _inclusiveTicks; | |
| 41 int _exclusiveTicks = 0; | |
| 42 int get exclusiveTicks => _exclusiveTicks; | |
| 43 InlineIntervalTick(this.startAddress); | |
| 44 } | |
| 45 | |
| 46 class ProfileCode { | |
| 47 final CpuProfile profile; | |
| 48 final Code code; | |
| 49 int exclusiveTicks; | |
| 50 int inclusiveTicks; | |
| 51 final addressTicks = new Map<int, CodeTick>(); | |
| 52 final intervalTicks = new Map<int, InlineIntervalTick>(); | |
| 53 String formattedInclusiveTicks = ''; | |
| 54 String formattedExclusiveTicks = ''; | |
| 55 | |
| 56 void _processTicks(List<String> profileTicks) { | |
| 57 assert(profileTicks != null); | |
| 58 assert((profileTicks.length % 3) == 0); | |
| 59 for (var i = 0; i < profileTicks.length; i += 3) { | |
| 60 var address = int.parse(profileTicks[i], radix:16); | |
| 61 var exclusive = int.parse(profileTicks[i + 1]); | |
| 62 var inclusive = int.parse(profileTicks[i + 2]); | |
| 63 var tick = new CodeTick(address, exclusive, inclusive); | |
| 64 addressTicks[address] = tick; | |
|
siva
2015/02/25 22:58:38
Does the key (address) also have to be in the Code
Cutch
2015/02/26 18:44:39
Done.
| |
| 65 | |
| 66 var interval = code.findInterval(address); | |
| 67 if (interval != null) { | |
| 68 var intervalTick = intervalTicks[interval.start]; | |
| 69 if (intervalTick == null) { | |
| 70 // Insert into map. | |
| 71 intervalTick = new InlineIntervalTick(interval.start); | |
| 72 intervalTicks[interval.start] = intervalTick; | |
| 73 } | |
| 74 intervalTick._inclusiveTicks += inclusive; | |
| 75 intervalTick._exclusiveTicks += exclusive; | |
| 76 } | |
| 77 } | |
| 78 } | |
| 79 | |
| 80 ProfileCode.fromMap(this.profile, this.code, Map data) { | |
| 81 assert(profile != null); | |
| 82 assert(code != null); | |
| 83 | |
| 84 code.profile = this; | |
| 85 | |
| 86 inclusiveTicks = int.parse(data['inclusiveTicks']); | |
| 87 exclusiveTicks = int.parse(data['exclusiveTicks']); | |
| 88 var ticks = data['ticks']; | |
| 89 if (ticks != null) { | |
| 90 _processTicks(ticks); | |
| 91 } | |
| 92 | |
| 93 formattedInclusiveTicks = | |
| 94 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' | |
| 95 '($inclusiveTicks)'; | |
| 96 | |
| 97 formattedExclusiveTicks = | |
| 98 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' | |
| 99 '($exclusiveTicks)'; | |
| 100 } | |
| 101 } | |
| 102 | |
| 103 class ProfileFunction { | |
| 104 final CpuProfile profile; | |
| 105 final ServiceFunction function; | |
| 106 // List of compiled code objects containing this function. | |
| 107 final List<ProfileCode> profileCodes = new List<ProfileCode>(); | |
| 108 | |
| 109 // Absolute ticks: | |
| 110 int exclusiveTicks = 0; | |
| 111 int inclusiveTicks = 0; | |
| 112 | |
| 113 // Global percentages: | |
| 114 double globalExclusiveTicks = 0.0; | |
| 115 double globalInclusiveTicks = 0.0; | |
| 116 | |
| 117 int _sortCodes(ProfileCode a, ProfileCode b) { | |
| 118 if (a.code.isOptimized == b.code.isOptimized) { | |
| 119 return b.code.profile.exclusiveTicks - a.code.profile.exclusiveTicks; | |
| 120 } | |
| 121 if (a.code.isOptimized) { | |
| 122 return -1; | |
| 123 } | |
| 124 return 1; | |
| 125 } | |
| 126 | |
| 127 ProfileFunction.fromMap(this.profile, this.function, Map data) { | |
| 128 for (var codeIndex in data['codes']) { | |
| 129 var profileCode = profile.codes[codeIndex]; | |
| 130 profileCodes.add(profileCode); | |
| 131 } | |
| 132 profileCodes.sort(_sortCodes); | |
| 133 | |
| 134 exclusiveTicks = int.parse(data['exclusiveTicks']); | |
| 135 inclusiveTicks = int.parse(data['inclusiveTicks']); | |
| 136 | |
| 137 globalExclusiveTicks = exclusiveTicks / profile.sampleCount; | |
| 138 globalInclusiveTicks = inclusiveTicks / profile.sampleCount; | |
| 139 } | |
| 140 } | |
| 141 | |
| 142 | |
| 143 class CpuProfile { | |
| 144 final double MICROSECONDS_PER_SECOND = 1000000.0; | |
| 145 final double displayThreshold = 0.0002; // 0.02%. | |
| 146 | |
| 147 Isolate isolate; | |
| 148 | |
| 149 int sampleCount = 0; | |
| 150 int samplePeriod = 0; | |
| 151 double sampleRate = 0.0; | |
| 152 | |
| 153 int stackDepth = 0; | |
| 154 | |
| 155 double timeSpan = 0.0; | |
| 156 | |
| 157 CodeTrieNode codeTrieRoot; | |
| 158 FunctionTrieNode functionTrieRoot; | |
| 159 | |
| 160 final List<ProfileCode> codes = new List<ProfileCode>(); | |
| 161 final List<ProfileFunction> functions = new List<ProfileFunction>(); | |
| 162 | |
| 163 void clear() { | |
| 164 sampleCount = 0; | |
| 165 samplePeriod = 0; | |
| 166 sampleRate = 0.0; | |
| 167 stackDepth = 0; | |
| 168 timeSpan = 0.0; | |
| 169 codeTrieRoot = null; | |
| 170 functionTrieRoot = null; | |
| 171 codes.clear(); | |
| 172 functions.clear(); | |
| 173 } | |
| 174 | |
| 175 void load(Isolate isolate, ServiceMap profile) { | |
| 176 if ((isolate == null) || (profile == null)) { | |
| 177 return; | |
| 178 } | |
| 179 | |
| 180 this.isolate = isolate; | |
| 181 isolate.resetCachedProfileData(); | |
| 182 | |
| 183 clear(); | |
| 184 | |
| 185 sampleCount = profile['sampleCount']; | |
| 186 samplePeriod = profile['samplePeriod']; | |
| 187 sampleRate = (MICROSECONDS_PER_SECOND / samplePeriod); | |
| 188 stackDepth = profile['stackDepth']; | |
| 189 timeSpan = profile['timeSpan']; | |
| 190 | |
| 191 // Process code table. | |
| 192 for (var codeRegion in profile['codes']) { | |
| 193 Code code = codeRegion['code']; | |
| 194 assert(code != null); | |
| 195 codes.add(new ProfileCode.fromMap(this, code, codeRegion)); | |
| 196 } | |
| 197 | |
| 198 // Process function table. | |
| 199 for (var profileFunction in profile['functions']) { | |
| 200 ServiceFunction function = profileFunction['function']; | |
| 201 assert(function != null); | |
| 202 functions.add( | |
| 203 new ProfileFunction.fromMap(this, function, profileFunction)); | |
| 204 } | |
| 205 | |
| 206 // Process code trie. | |
| 207 var exclusiveCodeTrie = profile['exclusiveCodeTrie']; | |
| 208 assert(exclusiveCodeTrie != null); | |
| 209 codeTrieRoot = _processCodeTrie(exclusiveCodeTrie); | |
| 210 | |
| 211 // Process function trie. | |
| 212 var exclusiveFunctionTrie = profile['exclusiveFunctionTrie']; | |
| 213 assert(exclusiveFunctionTrie != null); | |
| 214 functionTrieRoot = _processFunctionTrie(exclusiveFunctionTrie); | |
| 215 } | |
| 216 | |
| 217 // Data shared across calls to _read*TrieNode. | |
| 218 int _trieDataCursor; | |
| 219 List<int> _trieData; | |
| 220 | |
| 221 // The code trie is serialized as a list of integers. Each node | |
| 222 // is recreated by consuming some portion of the list. The format is as | |
| 223 // follows: | |
| 224 // [0] index into codeTable of code object. | |
| 225 // [1] tick count (number of times this stack frame occured). | |
| 226 // [2] child node count | |
| 227 // Reading the trie is done by recursively reading the tree depth-first | |
| 228 // pre-order. | |
| 229 CodeTrieNode _processCodeTrie(List<int> data) { | |
| 230 // Setup state shared across calls to _readTrieNode. | |
| 231 _trieDataCursor = 0; | |
| 232 _trieData = data; | |
| 233 if (_trieData == null) { | |
| 234 return null; | |
| 235 } | |
| 236 if (_trieData.length < 3) { | |
| 237 // Not enough integers for 1 node. | |
| 238 return null; | |
| 239 } | |
| 240 // Read the tree, returns the root node. | |
| 241 return _readCodeTrieNode(); | |
| 242 } | |
| 243 | |
| 244 CodeTrieNode _readCodeTrieNode() { | |
| 245 // Read index into code table. | |
| 246 var index = _trieData[_trieDataCursor++]; | |
| 247 // Lookup code object. | |
| 248 var code = codes[index]; | |
| 249 // Frame counter. | |
| 250 var count = _trieData[_trieDataCursor++]; | |
| 251 // Create node. | |
| 252 var node = new CodeTrieNode(code, count); | |
| 253 // Number of children. | |
| 254 var children = _trieData[_trieDataCursor++]; | |
| 255 // Recursively read child nodes. | |
| 256 for (var i = 0; i < children; i++) { | |
| 257 var child = _readCodeTrieNode(); | |
| 258 node.children.add(child); | |
| 259 } | |
| 260 return node; | |
| 261 } | |
| 262 | |
| 263 FunctionTrieNode _processFunctionTrie(List<int> data) { | |
| 264 // Setup state shared across calls to _readTrieNode. | |
| 265 _trieDataCursor = 0; | |
| 266 _trieData = data; | |
| 267 if (_trieData == null) { | |
| 268 return null; | |
| 269 } | |
| 270 if (_trieData.length < 3) { | |
| 271 // Not enough integers for 1 node. | |
| 272 return null; | |
| 273 } | |
| 274 // Read the tree, returns the root node. | |
| 275 return _readFunctionTrieNode(); | |
| 276 } | |
| 277 | |
| 278 FunctionTrieNode _readFunctionTrieNode() { | |
| 279 // Read index into function table. | |
| 280 var index = _trieData[_trieDataCursor++]; | |
| 281 // Lookup function object. | |
| 282 var function = functions[index]; | |
| 283 // Frame counter. | |
| 284 var count = _trieData[_trieDataCursor++]; | |
| 285 // Create node. | |
| 286 var node = new FunctionTrieNode(function, count); | |
| 287 // Number of code index / count pairs. | |
| 288 var codeCount = _trieData[_trieDataCursor++]; | |
| 289 var totalCodeTicks = 0; | |
| 290 for (var i = 0; i < codeCount; i++) { | |
| 291 var codeIndex = _trieData[_trieDataCursor++]; | |
| 292 var code = codes[codeIndex]; | |
| 293 var codeTicks = _trieData[_trieDataCursor++]; | |
| 294 totalCodeTicks += codeTicks; | |
| 295 var nodeCode = new FunctionTrieNodeCode(code, codeTicks); | |
| 296 node.codes.add(nodeCode); | |
| 297 } | |
| 298 node._totalCodeTicks = totalCodeTicks; | |
| 299 // Number of children. | |
| 300 var children = _trieData[_trieDataCursor++]; | |
| 301 // Recursively read child nodes. | |
| 302 for (var i = 0; i < children; i++) { | |
| 303 var child = _readFunctionTrieNode(); | |
| 304 node.children.add(child); | |
| 305 } | |
| 306 return node; | |
| 307 } | |
| 308 | |
| 309 double approximateSecondsForCount(count) { | |
| 310 var MICROSECONDS_PER_SECOND = 1000000.0; | |
| 311 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; | |
| 312 } | |
| 313 } | |
| OLD | NEW |