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