| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of cpu_profiler; | 5 part of cpu_profiler; |
| 6 | 6 |
| 7 class CodeTrieNode { | 7 class CodeCallTreeNode { |
| 8 final ProfileCode profileCode; | 8 final ProfileCode profileCode; |
| 9 final int count; | 9 final int count; |
| 10 final children = new List<CodeTrieNode>(); | 10 double get percentage => _percentage; |
| 11 CodeTrieNode(this.profileCode, this.count); | 11 double _percentage = 0.0; |
| 12 final children = new List<CodeCallTreeNode>(); |
| 13 final Set<String> attributes = new Set<String>(); |
| 14 CodeCallTreeNode(this.profileCode, this.count) { |
| 15 attributes.addAll(profileCode.attributes); |
| 16 } |
| 12 } | 17 } |
| 13 | 18 |
| 14 class FunctionTrieNodeCode { | 19 class CodeCallTree { |
| 20 final bool inclusive; |
| 21 final CodeCallTreeNode root; |
| 22 CodeCallTree(this.inclusive, this.root) { |
| 23 _setCodePercentage(null, root); |
| 24 } |
| 25 |
| 26 _setCodePercentage(CodeCallTreeNode parent, CodeCallTreeNode node) { |
| 27 assert(node != null); |
| 28 var parentPercentage = 1.0; |
| 29 var parentCount = node.count; |
| 30 if (parent != null) { |
| 31 parentPercentage = parent._percentage; |
| 32 parentCount = parent.count; |
| 33 } |
| 34 if (inclusive) { |
| 35 node._percentage = parentPercentage * (node.count / parentCount); |
| 36 } else { |
| 37 node._percentage = (node.count / parentCount); |
| 38 } |
| 39 for (var child in node.children) { |
| 40 _setCodePercentage(node, child); |
| 41 } |
| 42 } |
| 43 } |
| 44 |
| 45 class FunctionCallTreeNodeCode { |
| 15 final ProfileCode code; | 46 final ProfileCode code; |
| 16 final int ticks; | 47 final int ticks; |
| 17 FunctionTrieNodeCode(this.code, this.ticks); | 48 FunctionCallTreeNodeCode(this.code, this.ticks); |
| 18 } | 49 } |
| 19 | 50 |
| 20 class FunctionTrieNode { | 51 class FunctionCallTreeNode { |
| 21 final ProfileFunction profileFunction; | 52 final ProfileFunction profileFunction; |
| 22 final int count; | 53 final int count; |
| 23 final children = new List<FunctionTrieNode>(); | 54 double get percentage => _percentage; |
| 24 final codes = new List<FunctionTrieNodeCode>(); | 55 double _percentage = 0.0; |
| 56 final children = new List<FunctionCallTreeNode>(); |
| 57 final Set<String> attributes = new Set<String>(); |
| 58 final codes = new List<FunctionCallTreeNodeCode>(); |
| 25 int _totalCodeTicks = 0; | 59 int _totalCodeTicks = 0; |
| 26 int get totalCodesTicks => _totalCodeTicks; | 60 int get totalCodesTicks => _totalCodeTicks; |
| 27 FunctionTrieNode(this.profileFunction, this.count); | 61 |
| 62 // Does this function have an optimized version of itself? |
| 63 bool hasOptimizedCode() { |
| 64 for (var nodeCode in codes) { |
| 65 var profileCode = nodeCode.code; |
| 66 if (!profileCode.code.isDartCode) { |
| 67 continue; |
| 68 } |
| 69 if (profileCode.code.function != profileFunction.function) { |
| 70 continue; |
| 71 } |
| 72 if (profileCode.code.isOptimized) { |
| 73 return true; |
| 74 } |
| 75 } |
| 76 return false; |
| 77 } |
| 78 |
| 79 // Does this function have an unoptimized version of itself? |
| 80 bool hasUnoptimizedCode() { |
| 81 for (var nodeCode in codes) { |
| 82 var profileCode = nodeCode.code; |
| 83 if (!profileCode.code.isDartCode) { |
| 84 continue; |
| 85 } |
| 86 if (profileCode.code.kind == CodeKind.Stub) { |
| 87 continue; |
| 88 } |
| 89 if (!profileCode.code.isOptimized) { |
| 90 return true; |
| 91 } |
| 92 } |
| 93 return false; |
| 94 } |
| 95 |
| 96 // Has this function been inlined in another function? |
| 97 bool isInlined() { |
| 98 for (var nodeCode in codes) { |
| 99 var profileCode = nodeCode.code; |
| 100 if (!profileCode.code.isDartCode) { |
| 101 continue; |
| 102 } |
| 103 if (profileCode.code.kind == CodeKind.Stub) { |
| 104 continue; |
| 105 } |
| 106 // If the code's function isn't this function. |
| 107 if (profileCode.code.function != profileFunction.function) { |
| 108 return true; |
| 109 } |
| 110 } |
| 111 return false; |
| 112 } |
| 113 |
| 114 setCodeAttributes() { |
| 115 if (hasOptimizedCode()) { |
| 116 attributes.add('optimized'); |
| 117 } |
| 118 if (hasUnoptimizedCode()) { |
| 119 attributes.add('unoptimized'); |
| 120 } |
| 121 if (isInlined()) { |
| 122 attributes.add('inlined'); |
| 123 } |
| 124 } |
| 125 |
| 126 FunctionCallTreeNode(this.profileFunction, this.count) { |
| 127 profileFunction._addKindBasedAttributes(attributes); |
| 128 } |
| 129 } |
| 130 |
| 131 class FunctionCallTree { |
| 132 final bool inclusive; |
| 133 final FunctionCallTreeNode root; |
| 134 FunctionCallTree(this.inclusive, this.root) { |
| 135 _setFunctionPercentage(null, root); |
| 136 } |
| 137 |
| 138 void _setFunctionPercentage(FunctionCallTreeNode parent, |
| 139 FunctionCallTreeNode node) { |
| 140 assert(node != null); |
| 141 var parentPercentage = 1.0; |
| 142 var parentCount = node.count; |
| 143 if (parent != null) { |
| 144 parentPercentage = parent._percentage; |
| 145 parentCount = parent.count; |
| 146 } |
| 147 if (inclusive) { |
| 148 node._percentage = parentPercentage * (node.count / parentCount); |
| 149 } else { |
| 150 node._percentage = (node.count / parentCount); |
| 151 } |
| 152 for (var child in node.children) { |
| 153 _setFunctionPercentage(node, child); |
| 154 } |
| 155 } |
| 28 } | 156 } |
| 29 | 157 |
| 30 class CodeTick { | 158 class CodeTick { |
| 31 final int exclusiveTicks; | 159 final int exclusiveTicks; |
| 32 final int inclusiveTicks; | 160 final int inclusiveTicks; |
| 33 CodeTick(this.exclusiveTicks, this.inclusiveTicks); | 161 CodeTick(this.exclusiveTicks, this.inclusiveTicks); |
| 34 } | 162 } |
| 35 | 163 |
| 36 class InlineIntervalTick { | 164 class InlineIntervalTick { |
| 37 final int startAddress; | 165 final int startAddress; |
| 38 int _inclusiveTicks = 0; | 166 int _inclusiveTicks = 0; |
| 39 int get inclusiveTicks => _inclusiveTicks; | 167 int get inclusiveTicks => _inclusiveTicks; |
| 40 int _exclusiveTicks = 0; | 168 int _exclusiveTicks = 0; |
| 41 int get exclusiveTicks => _exclusiveTicks; | 169 int get exclusiveTicks => _exclusiveTicks; |
| 42 InlineIntervalTick(this.startAddress); | 170 InlineIntervalTick(this.startAddress); |
| 43 } | 171 } |
| 44 | 172 |
| 45 class ProfileCode { | 173 class ProfileCode { |
| 46 final CpuProfile profile; | 174 final CpuProfile profile; |
| 47 final Code code; | 175 final Code code; |
| 48 int exclusiveTicks; | 176 int exclusiveTicks; |
| 49 int inclusiveTicks; | 177 int inclusiveTicks; |
| 178 double normalizedExclusiveTicks = 0.0; |
| 179 double normalizedInclusiveTicks = 0.0; |
| 50 final addressTicks = new Map<int, CodeTick>(); | 180 final addressTicks = new Map<int, CodeTick>(); |
| 51 final intervalTicks = new Map<int, InlineIntervalTick>(); | 181 final intervalTicks = new Map<int, InlineIntervalTick>(); |
| 52 String formattedInclusiveTicks = ''; | 182 String formattedInclusiveTicks = ''; |
| 53 String formattedExclusiveTicks = ''; | 183 String formattedExclusiveTicks = ''; |
| 184 String formattedExclusivePercent = ''; |
| 185 String formattedCpuTime = ''; |
| 186 String formattedOnStackTime = ''; |
| 187 final Set<String> attributes = new Set<String>(); |
| 54 | 188 |
| 55 void _processTicks(List<String> profileTicks) { | 189 void _processTicks(List<String> profileTicks) { |
| 56 assert(profileTicks != null); | 190 assert(profileTicks != null); |
| 57 assert((profileTicks.length % 3) == 0); | 191 assert((profileTicks.length % 3) == 0); |
| 58 for (var i = 0; i < profileTicks.length; i += 3) { | 192 for (var i = 0; i < profileTicks.length; i += 3) { |
| 59 var address = int.parse(profileTicks[i], radix:16); | 193 var address = int.parse(profileTicks[i], radix:16); |
| 60 var exclusive = int.parse(profileTicks[i + 1]); | 194 var exclusive = int.parse(profileTicks[i + 1]); |
| 61 var inclusive = int.parse(profileTicks[i + 2]); | 195 var inclusive = int.parse(profileTicks[i + 2]); |
| 62 var tick = new CodeTick(exclusive, inclusive); | 196 var tick = new CodeTick(exclusive, inclusive); |
| 63 addressTicks[address] = tick; | 197 addressTicks[address] = tick; |
| (...skipping 11 matching lines...) Expand all Loading... |
| 75 } | 209 } |
| 76 } | 210 } |
| 77 } | 211 } |
| 78 | 212 |
| 79 ProfileCode.fromMap(this.profile, this.code, Map data) { | 213 ProfileCode.fromMap(this.profile, this.code, Map data) { |
| 80 assert(profile != null); | 214 assert(profile != null); |
| 81 assert(code != null); | 215 assert(code != null); |
| 82 | 216 |
| 83 code.profile = this; | 217 code.profile = this; |
| 84 | 218 |
| 219 if (code.isDartCode) { |
| 220 if (code.isOptimized) { |
| 221 attributes.add('optimized'); |
| 222 } else { |
| 223 attributes.add('unoptimized'); |
| 224 } |
| 225 } |
| 226 if (code.isDartCode) { |
| 227 attributes.add('dart'); |
| 228 } else if (code.kind == CodeKind.Tag) { |
| 229 attributes.add('tag'); |
| 230 } else if (code.kind == CodeKind.Native) { |
| 231 attributes.add('native'); |
| 232 } |
| 85 inclusiveTicks = int.parse(data['inclusiveTicks']); | 233 inclusiveTicks = int.parse(data['inclusiveTicks']); |
| 86 exclusiveTicks = int.parse(data['exclusiveTicks']); | 234 exclusiveTicks = int.parse(data['exclusiveTicks']); |
| 235 |
| 236 normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount; |
| 237 |
| 238 normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount; |
| 239 |
| 87 var ticks = data['ticks']; | 240 var ticks = data['ticks']; |
| 88 if (ticks != null) { | 241 if (ticks != null) { |
| 89 _processTicks(ticks); | 242 _processTicks(ticks); |
| 90 } | 243 } |
| 91 | 244 |
| 245 formattedExclusivePercent = |
| 246 Utils.formatPercent(exclusiveTicks, profile.sampleCount); |
| 247 |
| 248 formattedCpuTime = |
| 249 Utils.formatTimeMilliseconds( |
| 250 profile.approximateMillisecondsForCount(exclusiveTicks)); |
| 251 |
| 252 formattedOnStackTime = |
| 253 Utils.formatTimeMilliseconds( |
| 254 profile.approximateMillisecondsForCount(inclusiveTicks)); |
| 255 |
| 92 formattedInclusiveTicks = | 256 formattedInclusiveTicks = |
| 93 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' | 257 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' |
| 94 '($inclusiveTicks)'; | 258 '($inclusiveTicks)'; |
| 95 | 259 |
| 96 formattedExclusiveTicks = | 260 formattedExclusiveTicks = |
| 97 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' | 261 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' |
| 98 '($exclusiveTicks)'; | 262 '($exclusiveTicks)'; |
| 99 } | 263 } |
| 100 } | 264 } |
| 101 | 265 |
| 102 class ProfileFunction { | 266 class ProfileFunction { |
| 103 final CpuProfile profile; | 267 final CpuProfile profile; |
| 104 final ServiceFunction function; | 268 final ServiceFunction function; |
| 105 // List of compiled code objects containing this function. | 269 // List of compiled code objects containing this function. |
| 106 final List<ProfileCode> profileCodes = new List<ProfileCode>(); | 270 final List<ProfileCode> profileCodes = new List<ProfileCode>(); |
| 107 | |
| 108 // Absolute ticks: | 271 // Absolute ticks: |
| 109 int exclusiveTicks = 0; | 272 int exclusiveTicks = 0; |
| 110 int inclusiveTicks = 0; | 273 int inclusiveTicks = 0; |
| 111 | 274 |
| 112 // Global percentages: | 275 // Global percentages: |
| 113 double globalExclusiveTicks = 0.0; | 276 double normalizedExclusiveTicks = 0.0; |
| 114 double globalInclusiveTicks = 0.0; | 277 double normalizedInclusiveTicks = 0.0; |
| 278 |
| 279 String formattedInclusiveTicks = ''; |
| 280 String formattedExclusiveTicks = ''; |
| 281 String formattedExclusivePercent = ''; |
| 282 String formattedCpuTime = ''; |
| 283 String formattedOnStackTime = ''; |
| 284 final Set<String> attributes = new Set<String>(); |
| 115 | 285 |
| 116 int _sortCodes(ProfileCode a, ProfileCode b) { | 286 int _sortCodes(ProfileCode a, ProfileCode b) { |
| 117 if (a.code.isOptimized == b.code.isOptimized) { | 287 if (a.code.isOptimized == b.code.isOptimized) { |
| 118 return b.code.profile.exclusiveTicks - a.code.profile.exclusiveTicks; | 288 return b.code.profile.exclusiveTicks - a.code.profile.exclusiveTicks; |
| 119 } | 289 } |
| 120 if (a.code.isOptimized) { | 290 if (a.code.isOptimized) { |
| 121 return -1; | 291 return -1; |
| 122 } | 292 } |
| 123 return 1; | 293 return 1; |
| 124 } | 294 } |
| 125 | 295 |
| 296 // Does this function have an optimized version of itself? |
| 297 bool hasOptimizedCode() { |
| 298 for (var profileCode in profileCodes) { |
| 299 if (profileCode.code.function != function) { |
| 300 continue; |
| 301 } |
| 302 if (profileCode.code.isOptimized) { |
| 303 return true; |
| 304 } |
| 305 } |
| 306 return false; |
| 307 } |
| 308 |
| 309 // Does this function have an unoptimized version of itself? |
| 310 bool hasUnoptimizedCode() { |
| 311 for (var profileCode in profileCodes) { |
| 312 if (profileCode.code.kind == CodeKind.Stub) { |
| 313 continue; |
| 314 } |
| 315 if (!profileCode.code.isDartCode) { |
| 316 continue; |
| 317 } |
| 318 if (!profileCode.code.isOptimized) { |
| 319 return true; |
| 320 } |
| 321 } |
| 322 return false; |
| 323 } |
| 324 |
| 325 // Has this function been inlined in another function? |
| 326 bool isInlined() { |
| 327 for (var profileCode in profileCodes) { |
| 328 if (profileCode.code.kind == CodeKind.Stub) { |
| 329 continue; |
| 330 } |
| 331 if (!profileCode.code.isDartCode) { |
| 332 continue; |
| 333 } |
| 334 // If the code's function isn't this function. |
| 335 if (profileCode.code.function != function) { |
| 336 return true; |
| 337 } |
| 338 } |
| 339 return false; |
| 340 } |
| 341 |
| 342 void _addKindBasedAttributes(Set<String> attribs) { |
| 343 if (function.kind == FunctionKind.kTag) { |
| 344 attribs.add('tag'); |
| 345 } else if (function.kind == FunctionKind.kStub) { |
| 346 attribs.add('dart'); |
| 347 attribs.add('stub'); |
| 348 } else if (function.kind == FunctionKind.kNative) { |
| 349 attribs.add('native'); |
| 350 } else if (function.kind.isSynthetic()) { |
| 351 attribs.add('synthetic'); |
| 352 } else { |
| 353 attribs.add('dart'); |
| 354 } |
| 355 } |
| 356 |
| 126 ProfileFunction.fromMap(this.profile, this.function, Map data) { | 357 ProfileFunction.fromMap(this.profile, this.function, Map data) { |
| 127 for (var codeIndex in data['codes']) { | 358 for (var codeIndex in data['codes']) { |
| 128 var profileCode = profile.codes[codeIndex]; | 359 var profileCode = profile.codes[codeIndex]; |
| 129 profileCodes.add(profileCode); | 360 profileCodes.add(profileCode); |
| 130 } | 361 } |
| 131 profileCodes.sort(_sortCodes); | 362 profileCodes.sort(_sortCodes); |
| 132 | 363 |
| 364 if (hasOptimizedCode()) { |
| 365 attributes.add('optimized'); |
| 366 } |
| 367 if (hasUnoptimizedCode()) { |
| 368 attributes.add('unoptimized'); |
| 369 } |
| 370 if (isInlined()) { |
| 371 attributes.add('inlined'); |
| 372 } |
| 373 _addKindBasedAttributes(attributes); |
| 133 exclusiveTicks = int.parse(data['exclusiveTicks']); | 374 exclusiveTicks = int.parse(data['exclusiveTicks']); |
| 134 inclusiveTicks = int.parse(data['inclusiveTicks']); | 375 inclusiveTicks = int.parse(data['inclusiveTicks']); |
| 135 | 376 |
| 136 globalExclusiveTicks = exclusiveTicks / profile.sampleCount; | 377 normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount; |
| 137 globalInclusiveTicks = inclusiveTicks / profile.sampleCount; | 378 normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount; |
| 379 |
| 380 formattedExclusivePercent = |
| 381 Utils.formatPercent(exclusiveTicks, profile.sampleCount); |
| 382 |
| 383 formattedCpuTime = |
| 384 Utils.formatTimeMilliseconds( |
| 385 profile.approximateMillisecondsForCount(exclusiveTicks)); |
| 386 |
| 387 formattedOnStackTime = |
| 388 Utils.formatTimeMilliseconds( |
| 389 profile.approximateMillisecondsForCount(inclusiveTicks)); |
| 390 |
| 391 formattedInclusiveTicks = |
| 392 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' |
| 393 '($inclusiveTicks)'; |
| 394 |
| 395 formattedExclusiveTicks = |
| 396 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' |
| 397 '($exclusiveTicks)'; |
| 138 } | 398 } |
| 139 } | 399 } |
| 140 | 400 |
| 141 | 401 |
| 142 class CpuProfile { | 402 class CpuProfile { |
| 143 final double MICROSECONDS_PER_SECOND = 1000000.0; | 403 final double MICROSECONDS_PER_SECOND = 1000000.0; |
| 144 final double displayThreshold = 0.0002; // 0.02%. | 404 final double displayThreshold = 0.0002; // 0.02%. |
| 145 | 405 |
| 146 Isolate isolate; | 406 Isolate isolate; |
| 147 | 407 |
| 148 int sampleCount = 0; | 408 int sampleCount = 0; |
| 149 int samplePeriod = 0; | 409 int samplePeriod = 0; |
| 150 double sampleRate = 0.0; | 410 double sampleRate = 0.0; |
| 151 | 411 |
| 152 int stackDepth = 0; | 412 int stackDepth = 0; |
| 153 | 413 |
| 154 double timeSpan = 0.0; | 414 double timeSpan = 0.0; |
| 155 | 415 |
| 156 CodeTrieNode codeTrieRoot; | 416 final Map<String, CodeCallTree> codeTrees = |
| 157 FunctionTrieNode functionTrieRoot; | 417 <String, CodeCallTree>{}; |
| 418 final Map<String, FunctionCallTree> functionTrees = |
| 419 <String, FunctionCallTree>{}; |
| 158 | 420 |
| 159 final List<ProfileCode> codes = new List<ProfileCode>(); | 421 final List<ProfileCode> codes = new List<ProfileCode>(); |
| 160 final List<ProfileFunction> functions = new List<ProfileFunction>(); | 422 final List<ProfileFunction> functions = new List<ProfileFunction>(); |
| 161 | 423 |
| 162 void clear() { | 424 void clear() { |
| 163 sampleCount = 0; | 425 sampleCount = 0; |
| 164 samplePeriod = 0; | 426 samplePeriod = 0; |
| 165 sampleRate = 0.0; | 427 sampleRate = 0.0; |
| 166 stackDepth = 0; | 428 stackDepth = 0; |
| 167 timeSpan = 0.0; | 429 timeSpan = 0.0; |
| 168 codeTrieRoot = null; | 430 codeTrees.clear(); |
| 169 functionTrieRoot = null; | 431 functionTrees.clear(); |
| 170 codes.clear(); | 432 codes.clear(); |
| 171 functions.clear(); | 433 functions.clear(); |
| 172 } | 434 } |
| 173 | 435 |
| 174 void load(Isolate isolate, ServiceMap profile) { | 436 void load(Isolate isolate, ServiceMap profile) { |
| 175 if ((isolate == null) || (profile == null)) { | 437 if ((isolate == null) || (profile == null)) { |
| 176 return; | 438 return; |
| 177 } | 439 } |
| 178 | 440 |
| 179 this.isolate = isolate; | 441 this.isolate = isolate; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 195 } | 457 } |
| 196 | 458 |
| 197 // Process function table. | 459 // Process function table. |
| 198 for (var profileFunction in profile['functions']) { | 460 for (var profileFunction in profile['functions']) { |
| 199 ServiceFunction function = profileFunction['function']; | 461 ServiceFunction function = profileFunction['function']; |
| 200 assert(function != null); | 462 assert(function != null); |
| 201 functions.add( | 463 functions.add( |
| 202 new ProfileFunction.fromMap(this, function, profileFunction)); | 464 new ProfileFunction.fromMap(this, function, profileFunction)); |
| 203 } | 465 } |
| 204 | 466 |
| 205 // Process code trie. | 467 // Process code trees. |
| 206 var exclusiveCodeTrie = profile['exclusiveCodeTrie']; | 468 var exclusiveCodeTrie = profile['exclusiveCodeTrie']; |
| 207 assert(exclusiveCodeTrie != null); | 469 if (exclusiveCodeTrie != null) { |
| 208 codeTrieRoot = _processCodeTrie(exclusiveCodeTrie); | 470 codeTrees['exclusive'] = _loadCodeTree(false, exclusiveCodeTrie); |
| 471 } |
| 472 var inclusiveCodeTrie = profile['inclusiveCodeTrie']; |
| 473 if (inclusiveCodeTrie != null) { |
| 474 codeTrees['inclusive'] = _loadCodeTree(true, inclusiveCodeTrie); |
| 475 } |
| 209 | 476 |
| 210 // Process function trie. | 477 // Process function trees. |
| 211 var exclusiveFunctionTrie = profile['exclusiveFunctionTrie']; | 478 var exclusiveFunctionTrie = profile['exclusiveFunctionTrie']; |
| 212 assert(exclusiveFunctionTrie != null); | 479 if (exclusiveFunctionTrie != null) { |
| 213 functionTrieRoot = _processFunctionTrie(exclusiveFunctionTrie); | 480 functionTrees['exclusive'] = |
| 481 _loadFunctionTree(false, exclusiveFunctionTrie); |
| 482 } |
| 483 var inclusiveFunctionTrie = profile['inclusiveFunctionTrie']; |
| 484 if (inclusiveFunctionTrie != null) { |
| 485 functionTrees['inclusive'] = |
| 486 _loadFunctionTree(true, inclusiveFunctionTrie); |
| 487 } |
| 214 } | 488 } |
| 215 | 489 |
| 216 // Data shared across calls to _read*TrieNode. | 490 // Data shared across calls to _read*TrieNode. |
| 217 int _trieDataCursor; | 491 int _trieDataCursor; |
| 218 List<int> _trieData; | 492 List<int> _trieData; |
| 219 | 493 |
| 220 // The code trie is serialized as a list of integers. Each node | 494 // 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 | 495 // is recreated by consuming some portion of the list. The format is as |
| 222 // follows: | 496 // follows: |
| 223 // [0] index into codeTable of code object. | 497 // [0] index into codeTable of code object. |
| 224 // [1] tick count (number of times this stack frame occured). | 498 // [1] tick count (number of times this stack frame occured). |
| 225 // [2] child node count | 499 // [2] child node count |
| 226 // Reading the trie is done by recursively reading the tree depth-first | 500 // Reading the trie is done by recursively reading the tree depth-first |
| 227 // pre-order. | 501 // pre-order. |
| 228 CodeTrieNode _processCodeTrie(List<int> data) { | 502 CodeCallTree _loadCodeTree(bool inclusive, List<int> data) { |
| 229 // Setup state shared across calls to _readTrieNode. | 503 // Setup state shared across calls to _readTrieNode. |
| 230 _trieDataCursor = 0; | 504 _trieDataCursor = 0; |
| 231 _trieData = data; | 505 _trieData = data; |
| 232 if (_trieData == null) { | 506 if (_trieData == null) { |
| 233 return null; | 507 return null; |
| 234 } | 508 } |
| 235 if (_trieData.length < 3) { | 509 if (_trieData.length < 3) { |
| 236 // Not enough integers for 1 node. | 510 // Not enough integers for 1 node. |
| 237 return null; | 511 return null; |
| 238 } | 512 } |
| 239 // Read the tree, returns the root node. | 513 // Read the tree, returns the root node. |
| 240 return _readCodeTrieNode(); | 514 var root = _readCodeTrieNode(); |
| 515 return new CodeCallTree(inclusive, root); |
| 241 } | 516 } |
| 242 | 517 |
| 243 CodeTrieNode _readCodeTrieNode() { | 518 CodeCallTreeNode _readCodeTrieNode() { |
| 244 // Read index into code table. | 519 // Read index into code table. |
| 245 var index = _trieData[_trieDataCursor++]; | 520 var index = _trieData[_trieDataCursor++]; |
| 246 // Lookup code object. | 521 // Lookup code object. |
| 247 var code = codes[index]; | 522 var code = codes[index]; |
| 248 // Frame counter. | 523 // Frame counter. |
| 249 var count = _trieData[_trieDataCursor++]; | 524 var count = _trieData[_trieDataCursor++]; |
| 250 // Create node. | 525 // Create node. |
| 251 var node = new CodeTrieNode(code, count); | 526 var node = new CodeCallTreeNode(code, count); |
| 252 // Number of children. | 527 // Number of children. |
| 253 var children = _trieData[_trieDataCursor++]; | 528 var children = _trieData[_trieDataCursor++]; |
| 254 // Recursively read child nodes. | 529 // Recursively read child nodes. |
| 255 for (var i = 0; i < children; i++) { | 530 for (var i = 0; i < children; i++) { |
| 256 var child = _readCodeTrieNode(); | 531 var child = _readCodeTrieNode(); |
| 257 node.children.add(child); | 532 node.children.add(child); |
| 258 } | 533 } |
| 259 return node; | 534 return node; |
| 260 } | 535 } |
| 261 | 536 |
| 262 FunctionTrieNode _processFunctionTrie(List<int> data) { | 537 FunctionCallTree _loadFunctionTree(bool inclusive, List<int> data) { |
| 263 // Setup state shared across calls to _readTrieNode. | 538 // Setup state shared across calls to _readTrieNode. |
| 264 _trieDataCursor = 0; | 539 _trieDataCursor = 0; |
| 265 _trieData = data; | 540 _trieData = data; |
| 266 if (_trieData == null) { | 541 if (_trieData == null) { |
| 267 return null; | 542 return null; |
| 268 } | 543 } |
| 269 if (_trieData.length < 3) { | 544 if (_trieData.length < 3) { |
| 270 // Not enough integers for 1 node. | 545 // Not enough integers for 1 node. |
| 271 return null; | 546 return null; |
| 272 } | 547 } |
| 273 // Read the tree, returns the root node. | 548 // Read the tree, returns the root node. |
| 274 return _readFunctionTrieNode(); | 549 var root = _readFunctionTrieNode(); |
| 550 return new FunctionCallTree(inclusive, root); |
| 275 } | 551 } |
| 276 | 552 |
| 277 FunctionTrieNode _readFunctionTrieNode() { | 553 FunctionCallTreeNode _readFunctionTrieNode() { |
| 278 // Read index into function table. | 554 // Read index into function table. |
| 279 var index = _trieData[_trieDataCursor++]; | 555 var index = _trieData[_trieDataCursor++]; |
| 280 // Lookup function object. | 556 // Lookup function object. |
| 281 var function = functions[index]; | 557 var function = functions[index]; |
| 282 // Frame counter. | 558 // Frame counter. |
| 283 var count = _trieData[_trieDataCursor++]; | 559 var count = _trieData[_trieDataCursor++]; |
| 284 // Create node. | 560 // Create node. |
| 285 var node = new FunctionTrieNode(function, count); | 561 var node = new FunctionCallTreeNode(function, count); |
| 286 // Number of code index / count pairs. | 562 // Number of code index / count pairs. |
| 287 var codeCount = _trieData[_trieDataCursor++]; | 563 var codeCount = _trieData[_trieDataCursor++]; |
| 288 var totalCodeTicks = 0; | 564 var totalCodeTicks = 0; |
| 289 for (var i = 0; i < codeCount; i++) { | 565 for (var i = 0; i < codeCount; i++) { |
| 290 var codeIndex = _trieData[_trieDataCursor++]; | 566 var codeIndex = _trieData[_trieDataCursor++]; |
| 291 var code = codes[codeIndex]; | 567 var code = codes[codeIndex]; |
| 292 var codeTicks = _trieData[_trieDataCursor++]; | 568 var codeTicks = _trieData[_trieDataCursor++]; |
| 293 totalCodeTicks += codeTicks; | 569 totalCodeTicks += codeTicks; |
| 294 var nodeCode = new FunctionTrieNodeCode(code, codeTicks); | 570 var nodeCode = new FunctionCallTreeNodeCode(code, codeTicks); |
| 295 node.codes.add(nodeCode); | 571 node.codes.add(nodeCode); |
| 572 node.setCodeAttributes(); |
| 296 } | 573 } |
| 297 node._totalCodeTicks = totalCodeTicks; | 574 node._totalCodeTicks = totalCodeTicks; |
| 298 // Number of children. | 575 // Number of children. |
| 299 var children = _trieData[_trieDataCursor++]; | 576 var children = _trieData[_trieDataCursor++]; |
| 300 // Recursively read child nodes. | 577 // Recursively read child nodes. |
| 301 for (var i = 0; i < children; i++) { | 578 for (var i = 0; i < children; i++) { |
| 302 var child = _readFunctionTrieNode(); | 579 var child = _readFunctionTrieNode(); |
| 303 node.children.add(child); | 580 node.children.add(child); |
| 304 } | 581 } |
| 305 return node; | 582 return node; |
| 306 } | 583 } |
| 307 | 584 |
| 585 int approximateMillisecondsForCount(count) { |
| 586 var MICROSECONDS_PER_MILLISECOND = 1000.0; |
| 587 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; |
| 588 } |
| 589 |
| 308 double approximateSecondsForCount(count) { | 590 double approximateSecondsForCount(count) { |
| 309 var MICROSECONDS_PER_SECOND = 1000000.0; | 591 var MICROSECONDS_PER_SECOND = 1000000.0; |
| 310 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; | 592 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; |
| 311 } | 593 } |
| 312 } | 594 } |
| OLD | NEW |