| 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 CodeCallTreeNode { | 7 class CodeCallTreeNode { |
| 8 final ProfileCode profileCode; | 8 final ProfileCode profileCode; |
| 9 final int count; | 9 final int count; |
| 10 double get percentage => _percentage; | 10 double get percentage => _percentage; |
| 11 double _percentage = 0.0; | 11 double _percentage = 0.0; |
| 12 final children = new List<CodeCallTreeNode>(); | 12 final children; |
| 13 final Set<String> attributes = new Set<String>(); | 13 final Set<String> attributes = new Set<String>(); |
| 14 CodeCallTreeNode(this.profileCode, this.count) { | 14 CodeCallTreeNode(this.profileCode, this.count, int childCount) |
| 15 : children = new List<CodeCallTreeNode>(childCount) { |
| 15 attributes.addAll(profileCode.attributes); | 16 attributes.addAll(profileCode.attributes); |
| 16 } | 17 } |
| 17 } | 18 } |
| 18 | 19 |
| 19 class CodeCallTree { | 20 class CodeCallTree { |
| 20 final bool inclusive; | 21 final bool inclusive; |
| 21 final CodeCallTreeNode root; | 22 final CodeCallTreeNode root; |
| 22 CodeCallTree(this.inclusive, this.root) { | 23 CodeCallTree(this.inclusive, this.root) { |
| 23 _setCodePercentage(null, root); | 24 _setCodePercentage(null, root); |
| 24 } | 25 } |
| (...skipping 27 matching lines...) Expand all Loading... |
| 52 final ProfileFunction profileFunction; | 53 final ProfileFunction profileFunction; |
| 53 final int count; | 54 final int count; |
| 54 double get percentage => _percentage; | 55 double get percentage => _percentage; |
| 55 double _percentage = 0.0; | 56 double _percentage = 0.0; |
| 56 final children = new List<FunctionCallTreeNode>(); | 57 final children = new List<FunctionCallTreeNode>(); |
| 57 final Set<String> attributes = new Set<String>(); | 58 final Set<String> attributes = new Set<String>(); |
| 58 final codes = new List<FunctionCallTreeNodeCode>(); | 59 final codes = new List<FunctionCallTreeNodeCode>(); |
| 59 int _totalCodeTicks = 0; | 60 int _totalCodeTicks = 0; |
| 60 int get totalCodesTicks => _totalCodeTicks; | 61 int get totalCodesTicks => _totalCodeTicks; |
| 61 | 62 |
| 63 FunctionCallTreeNode(this.profileFunction, this.count){ |
| 64 profileFunction._addKindBasedAttributes(attributes); |
| 65 } |
| 66 |
| 62 // Does this function have an optimized version of itself? | 67 // Does this function have an optimized version of itself? |
| 63 bool hasOptimizedCode() { | 68 bool hasOptimizedCode() { |
| 64 for (var nodeCode in codes) { | 69 for (var nodeCode in codes) { |
| 65 var profileCode = nodeCode.code; | 70 var profileCode = nodeCode.code; |
| 66 if (!profileCode.code.isDartCode) { | 71 if (!profileCode.code.isDartCode) { |
| 67 continue; | 72 continue; |
| 68 } | 73 } |
| 69 if (profileCode.code.function != profileFunction.function) { | 74 if (profileCode.code.function != profileFunction.function) { |
| 70 continue; | 75 continue; |
| 71 } | 76 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 115 if (hasOptimizedCode()) { | 120 if (hasOptimizedCode()) { |
| 116 attributes.add('optimized'); | 121 attributes.add('optimized'); |
| 117 } | 122 } |
| 118 if (hasUnoptimizedCode()) { | 123 if (hasUnoptimizedCode()) { |
| 119 attributes.add('unoptimized'); | 124 attributes.add('unoptimized'); |
| 120 } | 125 } |
| 121 if (isInlined()) { | 126 if (isInlined()) { |
| 122 attributes.add('inlined'); | 127 attributes.add('inlined'); |
| 123 } | 128 } |
| 124 } | 129 } |
| 125 | |
| 126 FunctionCallTreeNode(this.profileFunction, this.count) { | |
| 127 profileFunction._addKindBasedAttributes(attributes); | |
| 128 } | |
| 129 } | 130 } |
| 130 | 131 |
| 131 class FunctionCallTree { | 132 class FunctionCallTree { |
| 132 final bool inclusive; | 133 final bool inclusive; |
| 133 final FunctionCallTreeNode root; | 134 final FunctionCallTreeNode root; |
| 134 FunctionCallTree(this.inclusive, this.root) { | 135 FunctionCallTree(this.inclusive, this.root) { |
| 135 _setFunctionPercentage(null, root); | 136 _setFunctionPercentage(null, root); |
| 136 } | 137 } |
| 137 | 138 |
| 138 void _setFunctionPercentage(FunctionCallTreeNode parent, | 139 void _setFunctionPercentage(FunctionCallTreeNode parent, |
| (...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 406 Isolate isolate; | 407 Isolate isolate; |
| 407 | 408 |
| 408 int sampleCount = 0; | 409 int sampleCount = 0; |
| 409 int samplePeriod = 0; | 410 int samplePeriod = 0; |
| 410 double sampleRate = 0.0; | 411 double sampleRate = 0.0; |
| 411 | 412 |
| 412 int stackDepth = 0; | 413 int stackDepth = 0; |
| 413 | 414 |
| 414 double timeSpan = 0.0; | 415 double timeSpan = 0.0; |
| 415 | 416 |
| 416 final Map<String, CodeCallTree> codeTrees = | 417 final Map<String, List> tries = <String, List>{}; |
| 417 <String, CodeCallTree>{}; | |
| 418 final Map<String, FunctionCallTree> functionTrees = | |
| 419 <String, FunctionCallTree>{}; | |
| 420 | |
| 421 final List<ProfileCode> codes = new List<ProfileCode>(); | 418 final List<ProfileCode> codes = new List<ProfileCode>(); |
| 422 final List<ProfileFunction> functions = new List<ProfileFunction>(); | 419 final List<ProfileFunction> functions = new List<ProfileFunction>(); |
| 423 | 420 |
| 421 CodeCallTree loadCodeTree(String name) { |
| 422 if (name == 'inclusive') { |
| 423 return _loadCodeTree(true, tries['inclusiveCodeTrie']); |
| 424 } else { |
| 425 return _loadCodeTree(false, tries['exclusiveCodeTrie']); |
| 426 } |
| 427 } |
| 428 |
| 429 FunctionCallTree loadFunctionTree(String name) { |
| 430 if (name == 'inclusive') { |
| 431 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']); |
| 432 } else { |
| 433 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']); |
| 434 } |
| 435 } |
| 436 |
| 424 void clear() { | 437 void clear() { |
| 425 sampleCount = 0; | 438 sampleCount = 0; |
| 426 samplePeriod = 0; | 439 samplePeriod = 0; |
| 427 sampleRate = 0.0; | 440 sampleRate = 0.0; |
| 428 stackDepth = 0; | 441 stackDepth = 0; |
| 429 timeSpan = 0.0; | 442 timeSpan = 0.0; |
| 430 codeTrees.clear(); | |
| 431 functionTrees.clear(); | |
| 432 codes.clear(); | 443 codes.clear(); |
| 433 functions.clear(); | 444 functions.clear(); |
| 445 tries.clear(); |
| 434 } | 446 } |
| 435 | 447 |
| 436 void load(Isolate isolate, ServiceMap profile) { | 448 void load(Isolate isolate, ServiceMap profile) { |
| 449 clear(); |
| 437 if ((isolate == null) || (profile == null)) { | 450 if ((isolate == null) || (profile == null)) { |
| 438 return; | 451 return; |
| 439 } | 452 } |
| 440 | 453 |
| 441 this.isolate = isolate; | 454 this.isolate = isolate; |
| 442 isolate.resetCachedProfileData(); | 455 isolate.resetCachedProfileData(); |
| 443 | 456 |
| 444 clear(); | |
| 445 | |
| 446 sampleCount = profile['sampleCount']; | 457 sampleCount = profile['sampleCount']; |
| 447 samplePeriod = profile['samplePeriod']; | 458 samplePeriod = profile['samplePeriod']; |
| 448 sampleRate = (MICROSECONDS_PER_SECOND / samplePeriod); | 459 sampleRate = (MICROSECONDS_PER_SECOND / samplePeriod); |
| 449 stackDepth = profile['stackDepth']; | 460 stackDepth = profile['stackDepth']; |
| 450 timeSpan = profile['timeSpan']; | 461 timeSpan = profile['timeSpan']; |
| 451 | 462 |
| 452 // Process code table. | 463 // Process code table. |
| 453 for (var codeRegion in profile['codes']) { | 464 for (var codeRegion in profile['codes']) { |
| 454 Code code = codeRegion['code']; | 465 Code code = codeRegion['code']; |
| 455 assert(code != null); | 466 assert(code != null); |
| 456 codes.add(new ProfileCode.fromMap(this, code, codeRegion)); | 467 codes.add(new ProfileCode.fromMap(this, code, codeRegion)); |
| 457 } | 468 } |
| 458 | 469 |
| 459 // Process function table. | 470 // Process function table. |
| 460 for (var profileFunction in profile['functions']) { | 471 for (var profileFunction in profile['functions']) { |
| 461 ServiceFunction function = profileFunction['function']; | 472 ServiceFunction function = profileFunction['function']; |
| 462 assert(function != null); | 473 assert(function != null); |
| 463 functions.add( | 474 functions.add( |
| 464 new ProfileFunction.fromMap(this, function, profileFunction)); | 475 new ProfileFunction.fromMap(this, function, profileFunction)); |
| 465 } | 476 } |
| 466 | 477 |
| 467 // Process code trees. | 478 tries['exclusiveCodeTrie'] = |
| 468 var exclusiveCodeTrie = profile['exclusiveCodeTrie']; | 479 new Uint32List.fromList(profile['exclusiveCodeTrie']); |
| 469 if (exclusiveCodeTrie != null) { | 480 tries['inclusiveCodeTrie'] = |
| 470 codeTrees['exclusive'] = _loadCodeTree(false, exclusiveCodeTrie); | 481 new Uint32List.fromList(profile['inclusiveCodeTrie']); |
| 471 } | 482 tries['exclusiveFunctionTrie'] = |
| 472 var inclusiveCodeTrie = profile['inclusiveCodeTrie']; | 483 new Uint32List.fromList(profile['exclusiveFunctionTrie']); |
| 473 if (inclusiveCodeTrie != null) { | 484 tries['inclusiveFunctionTrie'] = |
| 474 codeTrees['inclusive'] = _loadCodeTree(true, inclusiveCodeTrie); | 485 new Uint32List.fromList(profile['inclusiveFunctionTrie']); |
| 475 } | |
| 476 | |
| 477 // Process function trees. | |
| 478 var exclusiveFunctionTrie = profile['exclusiveFunctionTrie']; | |
| 479 if (exclusiveFunctionTrie != null) { | |
| 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 } | |
| 488 } | 486 } |
| 489 | 487 |
| 490 // Data shared across calls to _read*TrieNode. | 488 // Data shared across calls to _read*TrieNode. |
| 491 int _trieDataCursor; | 489 int _dataCursor = 0; |
| 492 List<int> _trieData; | |
| 493 | 490 |
| 494 // The code trie is serialized as a list of integers. Each node | 491 // The code trie is serialized as a list of integers. Each node |
| 495 // is recreated by consuming some portion of the list. The format is as | 492 // is recreated by consuming some portion of the list. The format is as |
| 496 // follows: | 493 // follows: |
| 497 // [0] index into codeTable of code object. | 494 // [0] index into codeTable of code object. |
| 498 // [1] tick count (number of times this stack frame occured). | 495 // [1] tick count (number of times this stack frame occured). |
| 499 // [2] child node count | 496 // [2] child node count |
| 500 // Reading the trie is done by recursively reading the tree depth-first | 497 // Reading the trie is done by recursively reading the tree depth-first |
| 501 // pre-order. | 498 // pre-order. |
| 502 CodeCallTree _loadCodeTree(bool inclusive, List<int> data) { | 499 CodeCallTree _loadCodeTree(bool inclusive, List<int> data) { |
| 503 // Setup state shared across calls to _readTrieNode. | 500 if (data == null) { |
| 504 _trieDataCursor = 0; | |
| 505 _trieData = data; | |
| 506 if (_trieData == null) { | |
| 507 return null; | 501 return null; |
| 508 } | 502 } |
| 509 if (_trieData.length < 3) { | 503 if (data.length < 3) { |
| 504 // Not enough for root node. |
| 505 return null; |
| 506 } |
| 507 // Read the tree, returns the root node. |
| 508 var root = _readCodeTrie(data); |
| 509 return new CodeCallTree(inclusive, root); |
| 510 } |
| 511 |
| 512 CodeCallTreeNode _readCodeTrieNode(List<int> data) { |
| 513 // Lookup code object. |
| 514 var codeIndex = data[_dataCursor++]; |
| 515 var code = codes[codeIndex]; |
| 516 // Node tick counter. |
| 517 var count = data[_dataCursor++]; |
| 518 // Child node count. |
| 519 var children = data[_dataCursor++]; |
| 520 // Create node. |
| 521 var node = new CodeCallTreeNode(code, count, children); |
| 522 return node; |
| 523 } |
| 524 |
| 525 CodeCallTreeNode _readCodeTrie(List<int> data) { |
| 526 final nodeStack = new List<CodeCallTreeNode>(); |
| 527 final childIndexStack = new List<int>(); |
| 528 |
| 529 _dataCursor = 0; |
| 530 // Read root. |
| 531 var root = _readCodeTrieNode(data); |
| 532 |
| 533 // Push root onto stack. |
| 534 if (root.children.length > 0) { |
| 535 nodeStack.add(root); |
| 536 childIndexStack.add(0); |
| 537 } |
| 538 |
| 539 while (nodeStack.length > 0) { |
| 540 var lastIndex = nodeStack.length - 1; |
| 541 // Pop parent from stack. |
| 542 var parent = nodeStack[lastIndex]; |
| 543 var childIndex = childIndexStack[lastIndex]; |
| 544 |
| 545 // Read child node. |
| 546 assert(childIndex < parent.children.length); |
| 547 var node = _readCodeTrieNode(data); |
| 548 parent.children[childIndex++] = node; |
| 549 |
| 550 // If parent still has children, update child index. |
| 551 if (childIndex < parent.children.length) { |
| 552 childIndexStack[lastIndex] = childIndex; |
| 553 } else { |
| 554 // Finished processing parent node. |
| 555 nodeStack.removeLast(); |
| 556 childIndexStack.removeLast(); |
| 557 } |
| 558 |
| 559 // If node has children, push onto stack. |
| 560 if (node.children.length > 0) { |
| 561 nodeStack.add(node); |
| 562 childIndexStack.add(0); |
| 563 } |
| 564 } |
| 565 |
| 566 return root; |
| 567 } |
| 568 |
| 569 FunctionCallTree _loadFunctionTree(bool inclusive, List<int> data) { |
| 570 if (data == null) { |
| 571 return null; |
| 572 } |
| 573 if (data.length < 3) { |
| 510 // Not enough integers for 1 node. | 574 // Not enough integers for 1 node. |
| 511 return null; | 575 return null; |
| 512 } | 576 } |
| 513 // Read the tree, returns the root node. | 577 // Read the tree, returns the root node. |
| 514 var root = _readCodeTrieNode(); | 578 var root = _readFunctionTrie(data); |
| 515 return new CodeCallTree(inclusive, root); | 579 return new FunctionCallTree(inclusive, root); |
| 516 } | 580 } |
| 517 | 581 |
| 518 CodeCallTreeNode _readCodeTrieNode() { | 582 FunctionCallTreeNode _readFunctionTrieNode(List<int> data) { |
| 519 // Read index into code table. | 583 // Read index into function table. |
| 520 var index = _trieData[_trieDataCursor++]; | 584 var index = data[_dataCursor++]; |
| 521 // Lookup code object. | 585 // Lookup function object. |
| 522 var code = codes[index]; | 586 var function = functions[index]; |
| 523 // Frame counter. | 587 // Counter. |
| 524 var count = _trieData[_trieDataCursor++]; | 588 var count = data[_dataCursor++]; |
| 525 // Create node. | 589 // Create node. |
| 526 var node = new CodeCallTreeNode(code, count); | 590 var node = new FunctionCallTreeNode(function, count); |
| 591 // Number of code index / count pairs. |
| 592 var codeCount = data[_dataCursor++]; |
| 593 node.codes.length = codeCount; |
| 594 var totalCodeTicks = 0; |
| 595 for (var i = 0; i < codeCount; i++) { |
| 596 var codeIndex = data[_dataCursor++]; |
| 597 var code = codes[codeIndex]; |
| 598 assert(code != null); |
| 599 var codeTicks = data[_dataCursor++]; |
| 600 totalCodeTicks += codeTicks; |
| 601 var nodeCode = new FunctionCallTreeNodeCode(code, codeTicks); |
| 602 node.codes[i] = nodeCode; |
| 603 } |
| 604 node.setCodeAttributes(); |
| 605 node._totalCodeTicks = totalCodeTicks; |
| 527 // Number of children. | 606 // Number of children. |
| 528 var children = _trieData[_trieDataCursor++]; | 607 var childCount = data[_dataCursor++]; |
| 529 // Recursively read child nodes. | 608 node.children.length = childCount; |
| 530 for (var i = 0; i < children; i++) { | |
| 531 var child = _readCodeTrieNode(); | |
| 532 node.children.add(child); | |
| 533 } | |
| 534 return node; | 609 return node; |
| 535 } | 610 } |
| 536 | 611 |
| 537 FunctionCallTree _loadFunctionTree(bool inclusive, List<int> data) { | 612 FunctionCallTreeNode _readFunctionTrie(List<int> data) { |
| 538 // Setup state shared across calls to _readTrieNode. | 613 final nodeStack = new List<FunctionCallTreeNode>(); |
| 539 _trieDataCursor = 0; | 614 final childIndexStack = new List<int>(); |
| 540 _trieData = data; | 615 |
| 541 if (_trieData == null) { | 616 _dataCursor = 0; |
| 542 return null; | 617 |
| 618 // Read root. |
| 619 var root = _readFunctionTrieNode(data); |
| 620 |
| 621 // Push root onto stack. |
| 622 if (root.children.length > 0) { |
| 623 nodeStack.add(root); |
| 624 childIndexStack.add(0); |
| 543 } | 625 } |
| 544 if (_trieData.length < 3) { | 626 |
| 545 // Not enough integers for 1 node. | 627 while (nodeStack.length > 0) { |
| 546 return null; | 628 var lastIndex = nodeStack.length - 1; |
| 629 // Pop parent from stack. |
| 630 var parent = nodeStack[lastIndex]; |
| 631 var childIndex = childIndexStack[lastIndex]; |
| 632 |
| 633 // Read child node. |
| 634 assert(childIndex < parent.children.length); |
| 635 var node = _readFunctionTrieNode(data); |
| 636 parent.children[childIndex++] = node; |
| 637 |
| 638 // If parent still has children, update child index. |
| 639 if (childIndex < parent.children.length) { |
| 640 childIndexStack[lastIndex] = childIndex; |
| 641 } else { |
| 642 // Finished processing parent node. |
| 643 nodeStack.removeLast(); |
| 644 childIndexStack.removeLast(); |
| 645 } |
| 646 |
| 647 // If node has children, push onto stack. |
| 648 if (node.children.length > 0) { |
| 649 nodeStack.add(node); |
| 650 childIndexStack.add(0); |
| 651 } |
| 547 } | 652 } |
| 548 // Read the tree, returns the root node. | |
| 549 var root = _readFunctionTrieNode(); | |
| 550 return new FunctionCallTree(inclusive, root); | |
| 551 } | |
| 552 | 653 |
| 553 FunctionCallTreeNode _readFunctionTrieNode() { | 654 return root; |
| 554 // Read index into function table. | |
| 555 var index = _trieData[_trieDataCursor++]; | |
| 556 // Lookup function object. | |
| 557 var function = functions[index]; | |
| 558 // Frame counter. | |
| 559 var count = _trieData[_trieDataCursor++]; | |
| 560 // Create node. | |
| 561 var node = new FunctionCallTreeNode(function, count); | |
| 562 // Number of code index / count pairs. | |
| 563 var codeCount = _trieData[_trieDataCursor++]; | |
| 564 var totalCodeTicks = 0; | |
| 565 for (var i = 0; i < codeCount; i++) { | |
| 566 var codeIndex = _trieData[_trieDataCursor++]; | |
| 567 var code = codes[codeIndex]; | |
| 568 var codeTicks = _trieData[_trieDataCursor++]; | |
| 569 totalCodeTicks += codeTicks; | |
| 570 var nodeCode = new FunctionCallTreeNodeCode(code, codeTicks); | |
| 571 node.codes.add(nodeCode); | |
| 572 node.setCodeAttributes(); | |
| 573 } | |
| 574 node._totalCodeTicks = totalCodeTicks; | |
| 575 // Number of children. | |
| 576 var children = _trieData[_trieDataCursor++]; | |
| 577 // Recursively read child nodes. | |
| 578 for (var i = 0; i < children; i++) { | |
| 579 var child = _readFunctionTrieNode(); | |
| 580 node.children.add(child); | |
| 581 } | |
| 582 return node; | |
| 583 } | 655 } |
| 584 | 656 |
| 585 int approximateMillisecondsForCount(count) { | 657 int approximateMillisecondsForCount(count) { |
| 586 var MICROSECONDS_PER_MILLISECOND = 1000.0; | 658 var MICROSECONDS_PER_MILLISECOND = 1000.0; |
| 587 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; | 659 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; |
| 588 } | 660 } |
| 589 | 661 |
| 590 double approximateSecondsForCount(count) { | 662 double approximateSecondsForCount(count) { |
| 591 var MICROSECONDS_PER_SECOND = 1000000.0; | 663 var MICROSECONDS_PER_SECOND = 1000000.0; |
| 592 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; | 664 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; |
| 593 } | 665 } |
| 594 } | 666 } |
| OLD | NEW |