Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, 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 library cpu_profile_element; | 5 library cpu_profile_element; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:html'; | 8 import 'dart:html'; |
| 9 import 'dart:math' as Math; | |
| 9 import 'observatory_element.dart'; | 10 import 'observatory_element.dart'; |
| 10 import 'package:logging/logging.dart'; | 11 import 'package:logging/logging.dart'; |
| 11 import 'package:observatory/service.dart'; | 12 import 'package:observatory/service.dart'; |
| 12 import 'package:observatory/app.dart'; | 13 import 'package:observatory/app.dart'; |
| 13 import 'package:observatory/cpu_profile.dart'; | 14 import 'package:observatory/cpu_profile.dart'; |
| 14 import 'package:observatory/elements.dart'; | 15 import 'package:observatory/elements.dart'; |
| 15 import 'package:polymer/polymer.dart'; | 16 import 'package:polymer/polymer.dart'; |
| 16 | 17 |
| 17 List<String> sorted(Set<String> attributes) { | 18 List<String> sorted(Set<String> attributes) { |
| 18 var list = attributes.toList(); | 19 var list = attributes.toList(); |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 218 // Fill in method column. | 219 // Fill in method column. |
| 219 var methodColumn = flexColumns[0]; | 220 var methodColumn = flexColumns[0]; |
| 220 methodColumn.style.justifyContent = 'flex-start'; | 221 methodColumn.style.justifyContent = 'flex-start'; |
| 221 methodColumn.style.position = 'relative'; | 222 methodColumn.style.position = 'relative'; |
| 222 | 223 |
| 223 // Percent. | 224 // Percent. |
| 224 var percentNode = new DivElement(); | 225 var percentNode = new DivElement(); |
| 225 percentNode.text = percent; | 226 percentNode.text = percent; |
| 226 percentNode.style.minWidth = '5em'; | 227 percentNode.style.minWidth = '5em'; |
| 227 percentNode.style.textAlign = 'right'; | 228 percentNode.style.textAlign = 'right'; |
| 228 percentNode.title = 'Self: $selfPercent'; | 229 percentNode.title = 'Executing: $selfPercent'; |
| 229 methodColumn.children.add(percentNode); | 230 methodColumn.children.add(percentNode); |
| 230 | 231 |
| 231 // Gap. | 232 // Gap. |
| 232 var gap = new SpanElement(); | 233 var gap = new SpanElement(); |
| 233 gap.style.minWidth = '1em'; | 234 gap.style.minWidth = '1em'; |
| 234 methodColumn.children.add(gap); | 235 methodColumn.children.add(gap); |
| 235 | 236 |
| 236 // Code link. | 237 // Code link. |
| 237 var codeRef = newCodeRef(node.profileCode); | 238 var codeRef = newCodeRef(node.profileCode); |
| 238 codeRef.style.alignSelf = 'center'; | 239 codeRef.style.alignSelf = 'center'; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 313 functionRow.style.position = 'relative'; | 314 functionRow.style.position = 'relative'; |
| 314 functionRow.style.justifyContent = 'flex-start'; | 315 functionRow.style.justifyContent = 'flex-start'; |
| 315 codeAndFunctionColumn.children.add(functionRow); | 316 codeAndFunctionColumn.children.add(functionRow); |
| 316 | 317 |
| 317 // Insert the parent percentage | 318 // Insert the parent percentage |
| 318 var parentPercent = new SpanElement(); | 319 var parentPercent = new SpanElement(); |
| 319 parentPercent.text = percent; | 320 parentPercent.text = percent; |
| 320 parentPercent.style.minWidth = '4em'; | 321 parentPercent.style.minWidth = '4em'; |
| 321 parentPercent.style.alignSelf = 'center'; | 322 parentPercent.style.alignSelf = 'center'; |
| 322 parentPercent.style.textAlign = 'right'; | 323 parentPercent.style.textAlign = 'right'; |
| 323 parentPercent.title = 'Self: $selfPercent'; | 324 parentPercent.title = 'Executing: $selfPercent'; |
| 324 functionRow.children.add(parentPercent); | 325 functionRow.children.add(parentPercent); |
| 325 | 326 |
| 326 // Gap. | 327 // Gap. |
| 327 var gap = new SpanElement(); | 328 var gap = new SpanElement(); |
| 328 gap.style.minWidth = '1em'; | 329 gap.style.minWidth = '1em'; |
| 329 gap.text = ' '; | 330 gap.text = ' '; |
| 330 functionRow.children.add(gap); | 331 functionRow.children.add(gap); |
| 331 | 332 |
| 332 var functionRef = new Element.tag('function-ref'); | 333 var functionRef = new Element.tag('function-ref'); |
| 333 functionRef.ref = node.profileFunction.function; | 334 functionRef.ref = node.profileFunction.function; |
| 334 functionRef.style.alignSelf = 'center'; | 335 functionRef.style.alignSelf = 'center'; |
| 335 functionRow.children.add(functionRef); | 336 functionRow.children.add(functionRef); |
| 336 | 337 |
| 337 gap = new SpanElement(); | 338 gap = new SpanElement(); |
| 338 gap.style.minWidth = '1em'; | 339 gap.style.minWidth = '1em'; |
| 339 gap.text = ' '; | 340 gap.text = ' '; |
| 340 functionRow.children.add(gap); | 341 functionRow.children.add(gap); |
| 341 | 342 |
| 342 for (var attribute in sorted(node.attributes)) { | 343 for (var attribute in sorted(node.attributes)) { |
| 343 functionRow.children.add(newAttributeBox(attribute)); | 344 functionRow.children.add(newAttributeBox(attribute)); |
| 344 } | 345 } |
| 345 | 346 |
| 346 makeInfoBox(); | 347 makeInfoBox(); |
| 347 functionRow.children.add(infoBox); | 348 functionRow.children.add(infoBox); |
| 348 | 349 |
| 349 if (node.profileFunction.function.kind.hasDartCode()) { | 350 if (node.profileFunction.function.kind.hasDartCode()) { |
| 350 infoBox.children.add(div('Hot code for current node')); | 351 infoBox.children.add(div('Code for current node')); |
| 351 infoBox.children.add(br()); | 352 infoBox.children.add(br()); |
| 352 var totalTicks = node.totalCodesTicks; | 353 var totalTicks = node.totalCodesTicks; |
| 353 var numCodes = node.codes.length; | 354 var numCodes = node.codes.length; |
| 354 for (var i = 0; i < numCodes; i++) { | 355 for (var i = 0; i < numCodes; i++) { |
| 355 var codeRowSpan = new DivElement(); | 356 var codeRowSpan = new DivElement(); |
| 356 codeRowSpan.style.paddingLeft = '1em'; | 357 codeRowSpan.style.paddingLeft = '1em'; |
| 357 infoBox.children.add(codeRowSpan); | 358 infoBox.children.add(codeRowSpan); |
| 358 var nodeCode = node.codes[i]; | 359 var nodeCode = node.codes[i]; |
| 359 var ticks = nodeCode.ticks; | 360 var ticks = nodeCode.ticks; |
| 360 var percentage = Utils.formatPercent(ticks, totalTicks); | 361 var percentage = Utils.formatPercent(ticks, totalTicks); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 386 infoBox.children.add(memberList); | 387 infoBox.children.add(memberList); |
| 387 infoBox.children.add(br()); | 388 infoBox.children.add(br()); |
| 388 ProfileTreeRow._addToMemberList(memberList, { | 389 ProfileTreeRow._addToMemberList(memberList, { |
| 389 'Exclusive ticks' : node.profileFunction.formattedExclusiveTicks, | 390 'Exclusive ticks' : node.profileFunction.formattedExclusiveTicks, |
| 390 'Cpu time' : node.profileFunction.formattedCpuTime, | 391 'Cpu time' : node.profileFunction.formattedCpuTime, |
| 391 'Inclusive ticks' : node.profileFunction.formattedInclusiveTicks, | 392 'Inclusive ticks' : node.profileFunction.formattedInclusiveTicks, |
| 392 'Call stack time' : node.profileFunction.formattedOnStackTime, | 393 'Call stack time' : node.profileFunction.formattedOnStackTime, |
| 393 }); | 394 }); |
| 394 | 395 |
| 395 if (node.profileFunction.function.kind.hasDartCode()) { | 396 if (node.profileFunction.function.kind.hasDartCode()) { |
| 396 infoBox.children.add(div('Hot code containing function')); | 397 infoBox.children.add(div('Code containing function')); |
| 397 infoBox.children.add(br()); | 398 infoBox.children.add(br()); |
| 398 var totalTicks = profile.sampleCount; | 399 var totalTicks = profile.sampleCount; |
| 399 var codes = node.profileFunction.profileCodes; | 400 var codes = node.profileFunction.profileCodes; |
| 400 var numCodes = codes.length; | 401 var numCodes = codes.length; |
| 401 for (var i = 0; i < numCodes; i++) { | 402 for (var i = 0; i < numCodes; i++) { |
| 402 var codeRowSpan = new DivElement(); | 403 var codeRowSpan = new DivElement(); |
| 403 codeRowSpan.style.paddingLeft = '1em'; | 404 codeRowSpan.style.paddingLeft = '1em'; |
| 404 infoBox.children.add(codeRowSpan); | 405 infoBox.children.add(codeRowSpan); |
| 405 var profileCode = codes[i]; | 406 var profileCode = codes[i]; |
| 406 var code = profileCode.code; | 407 var code = profileCode.code; |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 504 _sw.reset(); | 505 _sw.reset(); |
| 505 _sw.start(); | 506 _sw.start(); |
| 506 state = 'Requested'; | 507 state = 'Requested'; |
| 507 } | 508 } |
| 508 | 509 |
| 509 _onFetchFinished() { | 510 _onFetchFinished() { |
| 510 _sw.stop(); | 511 _sw.stop(); |
| 511 fetchTime = formatTimeMilliseconds(_sw.elapsedMilliseconds); | 512 fetchTime = formatTimeMilliseconds(_sw.elapsedMilliseconds); |
| 512 } | 513 } |
| 513 | 514 |
| 514 _onLoadStarted() { | 515 _onLoadStarted() async { |
| 515 _sw.reset(); | 516 _sw.reset(); |
| 516 _sw.start(); | 517 _sw.start(); |
| 517 state = 'Loading'; | 518 state = 'Loading'; |
| 519 var num = await window.animationFrame; | |
| 520 print('$num'); | |
|
turnidge
2015/04/07 21:24:31
Does this print belong here still?
Cutch
2015/04/14 21:45:15
Done.
| |
| 518 } | 521 } |
| 519 | 522 |
| 520 _onLoadFinished() { | 523 _onLoadFinished() { |
| 521 _sw.stop(); | 524 _sw.stop(); |
| 522 loadTime = formatTimeMilliseconds(_sw.elapsedMilliseconds); | 525 loadTime = formatTimeMilliseconds(_sw.elapsedMilliseconds); |
| 523 state = 'Loaded'; | 526 state = 'Loaded'; |
| 524 } | 527 } |
| 525 | 528 |
| 526 Future _getCpuProfile() { | 529 Future _getCpuProfile() { |
| 527 profile.clear(); | 530 profile.clear(); |
| 528 if (functionTree != null) { | 531 if (functionTree != null) { |
| 529 functionTree.clear(); | 532 functionTree.clear(); |
| 530 functionTree = null; | 533 functionTree = null; |
| 531 } | 534 } |
| 532 if (codeTree != null) { | 535 if (codeTree != null) { |
| 533 codeTree.clear(); | 536 codeTree.clear(); |
| 534 codeTree = null; | 537 codeTree = null; |
| 535 } | 538 } |
| 536 if (isolate == null) { | 539 if (isolate == null) { |
| 537 return new Future.value(null); | 540 return new Future.value(null); |
| 538 } | 541 } |
| 539 _onFetchStarted(); | 542 _onFetchStarted(); |
| 540 return isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector }) | 543 return isolate.invokeRpc('getCpuProfile', { 'tags': tagSelector }) |
| 541 .then((response) { | 544 .then((response) async { |
| 542 _onFetchFinished(); | 545 _onFetchFinished(); |
| 543 _onLoadStarted(); | 546 await _onLoadStarted(); |
| 544 try { | 547 try { |
| 545 profile.load(isolate, response); | 548 profile.load(isolate, response); |
| 546 _onLoadFinished(); | 549 _onLoadFinished(); |
| 547 _updateView(); | 550 _updateView(); |
| 548 } catch (e, st) { | 551 } catch (e, st) { |
| 549 state = 'Exception'; | 552 state = 'Exception'; |
| 550 exception = e; | 553 exception = e; |
| 551 stackTrace = st; | 554 stackTrace = st; |
| 552 } | 555 } |
| 553 }).catchError((e, st) { | 556 }).catchError((e, st) { |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 604 codeTree = new TableTree(tableBody, 2); | 607 codeTree = new TableTree(tableBody, 2); |
| 605 } | 608 } |
| 606 var tree = profile.loadCodeTree(exclusive ? 'exclusive' : 'inclusive'); | 609 var tree = profile.loadCodeTree(exclusive ? 'exclusive' : 'inclusive'); |
| 607 if (tree == null) { | 610 if (tree == null) { |
| 608 return; | 611 return; |
| 609 } | 612 } |
| 610 var rootRow = new CodeProfileTreeRow(codeTree, null, profile, tree.root); | 613 var rootRow = new CodeProfileTreeRow(codeTree, null, profile, tree.root); |
| 611 codeTree.initialize(rootRow); | 614 codeTree.initialize(rootRow); |
| 612 } | 615 } |
| 613 } | 616 } |
| 617 | |
| 618 class NameSortedTable extends SortedTable { | |
| 619 NameSortedTable(columns) : super(columns); | |
| 620 @override | |
| 621 dynamic getSortKeyFor(int row, int col) { | |
| 622 if (col == FUNCTION_COLUMN) { | |
| 623 // Use name as sort key. | |
| 624 return rows[row].values[col].name; | |
| 625 } | |
| 626 return super.getSortKeyFor(row, col); | |
| 627 } | |
| 628 | |
| 629 SortedTableRow rowFromIndex(int tableIndex) { | |
| 630 final modelIndex = sortedRows[tableIndex]; | |
| 631 return rows[modelIndex]; | |
| 632 } | |
| 633 | |
| 634 static const FUNCTION_SPACER_COLUMNS = const []; | |
| 635 static const FUNCTION_COLUMN = 2; | |
| 636 TableRowElement _makeFunctionRow() { | |
| 637 var tr = new TableRowElement(); | |
| 638 var cell; | |
| 639 | |
| 640 // Add percentage. | |
| 641 cell = tr.insertCell(-1); | |
| 642 cell = tr.insertCell(-1); | |
| 643 | |
| 644 // Add function ref. | |
| 645 cell = tr.insertCell(-1); | |
| 646 var functionRef = new Element.tag('function-ref'); | |
| 647 cell.children.add(functionRef); | |
| 648 | |
| 649 return tr; | |
| 650 } | |
| 651 | |
| 652 static const CALL_SPACER_COLUMNS = const []; | |
| 653 static const CALL_FUNCTION_COLUMN = 1; | |
| 654 TableRowElement _makeCallRow() { | |
| 655 var tr = new TableRowElement(); | |
| 656 var cell; | |
| 657 | |
| 658 // Add percentage. | |
| 659 cell = tr.insertCell(-1); | |
| 660 // Add function ref. | |
| 661 cell = tr.insertCell(-1); | |
| 662 var functionRef = new Element.tag('function-ref'); | |
| 663 cell.children.add(functionRef); | |
| 664 return tr; | |
| 665 } | |
| 666 | |
| 667 _updateRow(TableRowElement tr, | |
| 668 int rowIndex, | |
| 669 List spacerColumns, | |
| 670 int refColumn) { | |
| 671 var row = rows[rowIndex]; | |
| 672 // Set reference | |
| 673 var ref = tr.children[refColumn].children[0]; | |
| 674 ref.ref = row.values[refColumn]; | |
| 675 | |
| 676 for (var i = 0; i < row.values.length; i++) { | |
| 677 if (spacerColumns.contains(i) || (i == refColumn)) { | |
| 678 // Skip spacer columns. | |
| 679 continue; | |
| 680 } | |
| 681 var cell = tr.children[i]; | |
| 682 cell.title = row.values[i].toString(); | |
| 683 cell.text = getFormattedValue(rowIndex, i); | |
| 684 } | |
| 685 } | |
| 686 | |
| 687 _updateTableView(HtmlElement table, | |
| 688 HtmlElement makeEmptyRow(), | |
| 689 void onRowClick(TableRowElement tr), | |
| 690 List spacerColumns, | |
| 691 int refColumn) { | |
| 692 assert(table != null); | |
| 693 | |
| 694 // Resize DOM table. | |
| 695 if (table.children.length > sortedRows.length) { | |
| 696 // Shrink the table. | |
| 697 var deadRows = table.children.length - sortedRows.length; | |
| 698 for (var i = 0; i < deadRows; i++) { | |
| 699 table.children.removeLast(); | |
| 700 } | |
| 701 } else if (table.children.length < sortedRows.length) { | |
| 702 // Grow table. | |
| 703 var newRows = sortedRows.length - table.children.length; | |
| 704 for (var i = 0; i < newRows; i++) { | |
| 705 var row = makeEmptyRow(); | |
| 706 row.onClick.listen((e) { | |
| 707 e.stopPropagation(); | |
| 708 e.preventDefault(); | |
| 709 onRowClick(row); | |
| 710 }); | |
| 711 table.children.add(row); | |
| 712 } | |
| 713 } | |
| 714 | |
| 715 assert(table.children.length == sortedRows.length); | |
| 716 | |
| 717 // Fill table. | |
| 718 for (var i = 0; i < sortedRows.length; i++) { | |
| 719 var rowIndex = sortedRows[i]; | |
| 720 var tr = table.children[i]; | |
| 721 _updateRow(tr, rowIndex, spacerColumns, refColumn); | |
| 722 } | |
| 723 } | |
| 724 } | |
| 725 | |
| 726 @CustomTag('cpu-profile-table') | |
| 727 class CpuProfileTableElement extends ObservatoryElement { | |
| 728 final Stopwatch _sw = new Stopwatch(); | |
| 729 final CpuProfile profile = new CpuProfile(); | |
| 730 StreamSubscription _resizeSubscription; | |
| 731 @observable NameSortedTable profileTable; | |
| 732 @observable NameSortedTable profileCallersTable; | |
| 733 @observable NameSortedTable profileCalleesTable; | |
| 734 @observable ServiceFunction focusedFunction; | |
| 735 @observable int focusedRow; | |
| 736 @observable String fetchTime = ''; | |
| 737 @observable String loadTime = ''; | |
| 738 @observable String state = 'Requested'; | |
| 739 @observable var exception; | |
| 740 @observable var stackTrace; | |
| 741 @observable Isolate isolate; | |
| 742 @observable String sampleCount = ''; | |
| 743 @observable String refreshTime = ''; | |
| 744 @observable String sampleRate = ''; | |
| 745 @observable String stackDepth = ''; | |
| 746 @observable String timeSpan = ''; | |
| 747 @observable String directionSelector = 'Up'; | |
| 748 | |
| 749 CpuProfileTableElement.created() : super.created() { | |
| 750 var columns = [ | |
| 751 new SortedTableColumn.withFormatter('Executing (%)', | |
| 752 Utils.formatPercentNormalized), | |
| 753 new SortedTableColumn.withFormatter('In stack (%)', | |
| 754 Utils.formatPercentNormalized), | |
| 755 new SortedTableColumn('Method'), | |
| 756 ]; | |
| 757 profileTable = new NameSortedTable(columns); | |
| 758 profileTable.sortColumnIndex = 0; | |
| 759 | |
| 760 columns = [ | |
| 761 new SortedTableColumn.withFormatter('Callees (%)', | |
| 762 Utils.formatPercentNormalized), | |
| 763 new SortedTableColumn('Method') | |
| 764 ]; | |
| 765 profileCalleesTable = new NameSortedTable(columns); | |
| 766 profileCalleesTable.sortColumnIndex = 0; | |
| 767 | |
| 768 columns = [ | |
| 769 new SortedTableColumn.withFormatter('Callers (%)', | |
| 770 Utils.formatPercentNormalized), | |
| 771 new SortedTableColumn('Method') | |
| 772 ]; | |
| 773 profileCallersTable = new NameSortedTable(columns); | |
| 774 profileCallersTable.sortColumnIndex = 0; | |
| 775 } | |
| 776 | |
| 777 attached() { | |
| 778 super.attached(); | |
| 779 _resizeSubscription = window.onResize.listen((_) => _updateSize()); | |
| 780 _updateSize(); | |
| 781 } | |
| 782 | |
| 783 detached() { | |
| 784 super.detached(); | |
| 785 if (_resizeSubscription != null) { | |
| 786 _resizeSubscription.cancel(); | |
| 787 } | |
| 788 } | |
| 789 | |
| 790 _updateSize() { | |
| 791 HtmlElement e = $['main']; | |
| 792 final totalHeight = window.innerHeight; | |
| 793 final top = e.offset.top; | |
| 794 final bottomMargin = 32; | |
| 795 final mainHeight = totalHeight - top - bottomMargin; | |
| 796 e.style.setProperty('height', '${mainHeight}px'); | |
| 797 } | |
| 798 | |
| 799 isolateChanged() { | |
| 800 _getCpuProfile().whenComplete(checkParameters); | |
| 801 } | |
| 802 | |
| 803 checkParameters() { | |
| 804 var functionId = app.locationManager.fragment.parameters['functionId']; | |
| 805 if (functionId == null) { | |
| 806 _focusOnFunction(null); | |
| 807 return; | |
| 808 } | |
| 809 if (isolate == null) { | |
| 810 return; | |
| 811 } | |
| 812 isolate.getObject(functionId).then((func) => _focusOnFunction(func)); | |
| 813 } | |
| 814 | |
| 815 void directionSelectorChanged(oldValue) { | |
| 816 _updateFunctionTreeView(); | |
| 817 } | |
| 818 | |
| 819 void refresh(var done) { | |
| 820 _getCpuProfile().whenComplete(done); | |
| 821 } | |
| 822 | |
| 823 void clear(var done) { | |
| 824 _clearCpuProfile().whenComplete(done); | |
| 825 } | |
| 826 | |
| 827 _onFetchStarted() { | |
| 828 _sw.reset(); | |
| 829 _sw.start(); | |
| 830 state = 'Requested'; | |
| 831 } | |
| 832 | |
| 833 _onFetchFinished() { | |
| 834 _sw.stop(); | |
| 835 fetchTime = formatTimeMilliseconds(_sw.elapsedMilliseconds); | |
| 836 } | |
| 837 | |
| 838 _onLoadStarted() { | |
| 839 _sw.reset(); | |
| 840 _sw.start(); | |
| 841 state = 'Loading'; | |
| 842 } | |
| 843 | |
| 844 _onLoadFinished() { | |
| 845 _sw.stop(); | |
| 846 loadTime = formatTimeMilliseconds(_sw.elapsedMilliseconds); | |
| 847 state = 'Loaded'; | |
| 848 } | |
| 849 | |
| 850 Future _clearCpuProfile() { | |
| 851 profile.clear(); | |
| 852 _clearView(); | |
| 853 if (isolate == null) { | |
| 854 return new Future.value(null); | |
| 855 } | |
| 856 return isolate.invokeRpc('clearCpuProfile', { }) | |
| 857 .then((ServiceMap response) { | |
| 858 _updateView(); | |
| 859 }); | |
| 860 } | |
| 861 | |
| 862 Future _getCpuProfile() { | |
| 863 profile.clear(); | |
| 864 _clearView(); | |
| 865 if (isolate == null) { | |
| 866 return new Future.value(null); | |
| 867 } | |
| 868 _onFetchStarted(); | |
| 869 return isolate.invokeRpc('getCpuProfile', { 'tags': 'None' }) | |
| 870 .then((response) { | |
| 871 _onFetchFinished(); | |
| 872 _onLoadStarted(); | |
| 873 try { | |
| 874 profile.load(isolate, response); | |
| 875 profile.markFunctionCalls(); | |
| 876 _onLoadFinished(); | |
| 877 _updateView(); | |
| 878 } catch (e, st) { | |
| 879 state = 'Exception'; | |
| 880 exception = e; | |
| 881 stackTrace = st; | |
| 882 } | |
| 883 }).catchError((e, st) { | |
| 884 state = 'Exception'; | |
| 885 exception = e; | |
| 886 stackTrace = st; | |
| 887 }); | |
| 888 } | |
| 889 | |
| 890 _clearView() { | |
| 891 profileTable.clearRows(); | |
| 892 _renderTable(); | |
| 893 } | |
| 894 | |
| 895 _updateView() { | |
| 896 sampleCount = profile.sampleCount.toString(); | |
| 897 refreshTime = new DateTime.now().toString(); | |
| 898 stackDepth = profile.stackDepth.toString(); | |
| 899 sampleRate = profile.sampleRate.toStringAsFixed(0); | |
| 900 timeSpan = formatTime(profile.timeSpan); | |
| 901 _buildFunctionTable(); | |
| 902 _renderTable(); | |
| 903 _updateFunctionTreeView(); | |
| 904 } | |
| 905 | |
| 906 int _findFunctionRow(ServiceFunction function) { | |
| 907 for (var i = 0; i < profileTable.sortedRows.length; i++) { | |
| 908 var rowIndex = profileTable.sortedRows[i]; | |
| 909 var row = profileTable.rows[rowIndex]; | |
| 910 if (row.values[NameSortedTable.FUNCTION_COLUMN] == function) { | |
| 911 return i; | |
| 912 } | |
| 913 } | |
| 914 return -1; | |
| 915 } | |
| 916 | |
| 917 _scrollToFunction(ServiceFunction function) { | |
| 918 TableSectionElement tableBody = $['profile-table']; | |
| 919 var row = _findFunctionRow(function); | |
| 920 if (row == -1) { | |
| 921 return; | |
| 922 } | |
| 923 tableBody.children[row].classes.remove('shake'); | |
| 924 // trigger reflow. | |
| 925 tableBody.children[row].offsetHeight; | |
| 926 tableBody.children[row].scrollIntoView(ScrollAlignment.CENTER); | |
| 927 tableBody.children[row].classes.add('shake'); | |
| 928 } | |
| 929 | |
| 930 _clearFocusedFunction() { | |
| 931 TableSectionElement tableBody = $['profile-table']; | |
| 932 // Clear current focus. | |
| 933 if (focusedRow != null) { | |
| 934 tableBody.children[focusedRow].classes.remove('focused'); | |
| 935 } | |
| 936 focusedRow = null; | |
| 937 focusedFunction = null; | |
| 938 } | |
| 939 | |
| 940 _focusOnFunction(ServiceFunction function) { | |
| 941 if (focusedFunction == function) { | |
| 942 return; | |
| 943 } | |
| 944 | |
| 945 _clearFocusedFunction(); | |
| 946 | |
| 947 if (function == null) { | |
| 948 _updateFunctionTreeView(); | |
| 949 return; | |
| 950 } | |
| 951 | |
| 952 var row = _findFunctionRow(function); | |
| 953 if (row == -1) { | |
| 954 _updateFunctionTreeView(); | |
| 955 return; | |
| 956 } | |
| 957 | |
| 958 focusedRow = row; | |
| 959 focusedFunction = function; | |
| 960 | |
| 961 app.locationManager.goParameter( | |
| 962 { | |
| 963 'functionId': focusedFunction.id | |
| 964 } | |
| 965 ); | |
| 966 | |
| 967 TableSectionElement tableBody = $['profile-table']; | |
| 968 tableBody.children[focusedRow].classes.add('focused'); | |
| 969 _updateFunctionTreeView(); | |
| 970 } | |
| 971 | |
| 972 _onRowClick(TableRowElement tr) { | |
| 973 var tableBody = $['profile-table']; | |
| 974 var row = profileTable.rowFromIndex(tableBody.children.indexOf(tr)); | |
| 975 var function = row.values[NameSortedTable.FUNCTION_COLUMN]; | |
| 976 _focusOnFunction(function); | |
| 977 _buildCallersTable(function); | |
| 978 _buildCalleesTable(function); | |
| 979 } | |
| 980 | |
| 981 _renderTable() { | |
| 982 profileTable._updateTableView($['profile-table'], | |
| 983 profileTable._makeFunctionRow, | |
| 984 _onRowClick, | |
| 985 NameSortedTable.FUNCTION_SPACER_COLUMNS, | |
| 986 NameSortedTable.FUNCTION_COLUMN); | |
| 987 } | |
| 988 | |
| 989 _buildFunctionTable() { | |
| 990 for (var func in profile.functions) { | |
| 991 if ((func.exclusiveTicks == 0) && (func.inclusiveTicks == 0)) { | |
| 992 // Skip. | |
| 993 continue; | |
| 994 } | |
| 995 var row = [ | |
| 996 func.normalizedExclusiveTicks, | |
| 997 func.normalizedInclusiveTicks, | |
| 998 func.function, | |
| 999 ]; | |
| 1000 profileTable.addRow(new SortedTableRow(row)); | |
| 1001 } | |
| 1002 profileTable.sort(); | |
| 1003 } | |
| 1004 | |
| 1005 _renderCallTable(TableSectionElement view, | |
| 1006 NameSortedTable model, | |
| 1007 void onRowClick(TableRowElement tr)) { | |
| 1008 model._updateTableView(view, | |
| 1009 model._makeCallRow, | |
| 1010 onRowClick, | |
| 1011 NameSortedTable.CALL_SPACER_COLUMNS, | |
| 1012 NameSortedTable.CALL_FUNCTION_COLUMN); | |
| 1013 } | |
| 1014 | |
| 1015 _buildCallTable(Map<ProfileFunction, int> calls, | |
| 1016 NameSortedTable model) { | |
| 1017 model.clearRows(); | |
| 1018 var sum = 0; | |
| 1019 calls.values.forEach((i) => sum += i); | |
| 1020 calls.forEach((func, count) { | |
| 1021 var row = [ | |
| 1022 count / sum, | |
| 1023 func.function, | |
| 1024 ]; | |
| 1025 model.addRow(new SortedTableRow(row)); | |
| 1026 }); | |
| 1027 model.sort(); | |
| 1028 } | |
| 1029 | |
| 1030 _onCallersClick(TableRowElement tr) { | |
| 1031 var table = $['callers-table']; | |
| 1032 final row = profileCallersTable.rowFromIndex(table.children.indexOf(tr)); | |
| 1033 var function = row.values[NameSortedTable.CALL_FUNCTION_COLUMN]; | |
| 1034 _scrollToFunction(function); | |
| 1035 } | |
| 1036 | |
| 1037 _buildCallersTable(ServiceFunction function) { | |
| 1038 var profileFunction = function.profile; | |
| 1039 var calls = profileFunction.callers; | |
| 1040 var table = $['callers-table']; | |
| 1041 _buildCallTable(calls, profileCallersTable); | |
| 1042 _renderCallTable(table, profileCallersTable, _onCallersClick); | |
| 1043 } | |
| 1044 | |
| 1045 _onCalleesClick(TableRowElement tr) { | |
| 1046 var table = $['callees-table']; | |
| 1047 final row = profileCalleesTable.rowFromIndex(table.children.indexOf(tr)); | |
| 1048 var function = row.values[NameSortedTable.CALL_FUNCTION_COLUMN]; | |
| 1049 _scrollToFunction(function); | |
| 1050 } | |
| 1051 | |
| 1052 _buildCalleesTable(ServiceFunction function) { | |
| 1053 var profileFunction = function.profile; | |
| 1054 var calls = profileFunction.callees; | |
| 1055 var table = $['callees-table']; | |
| 1056 _buildCallTable(calls, profileCalleesTable); | |
| 1057 _renderCallTable(table, profileCalleesTable, _onCalleesClick); | |
| 1058 } | |
| 1059 | |
| 1060 _changeSort(Element target, NameSortedTable table) { | |
| 1061 if (target is TableCellElement) { | |
| 1062 if (table.sortColumnIndex != target.cellIndex) { | |
| 1063 table.sortColumnIndex = target.cellIndex; | |
| 1064 table.sortDescending = true; | |
| 1065 } else { | |
| 1066 table.sortDescending = !profileTable.sortDescending; | |
| 1067 } | |
| 1068 table.sort(); | |
| 1069 } | |
| 1070 } | |
| 1071 | |
| 1072 changeSortProfile(Event e, var detail, Element target) { | |
| 1073 _changeSort(target, profileTable); | |
| 1074 _renderTable(); | |
| 1075 } | |
| 1076 | |
| 1077 changeSortCallers(Event e, var detail, Element target) { | |
| 1078 _changeSort(target, profileCallersTable); | |
| 1079 _renderCallTable($['callers-table'], profileCallersTable, _onCallersClick); | |
| 1080 } | |
| 1081 | |
| 1082 changeSortCallees(Event e, var detail, Element target) { | |
| 1083 _changeSort(target, profileCalleesTable); | |
| 1084 _renderCallTable($['callees-table'], profileCalleesTable, _onCalleesClick); | |
| 1085 } | |
| 1086 | |
| 1087 ////// | |
| 1088 /// | |
| 1089 /// Function tree. | |
| 1090 /// | |
| 1091 TableTree functionTree; | |
| 1092 _updateFunctionTreeView() { | |
| 1093 if (functionTree != null) { | |
| 1094 functionTree.clear(); | |
| 1095 functionTree = null; | |
| 1096 } | |
| 1097 _buildFunctionTree(); | |
| 1098 } | |
| 1099 | |
| 1100 void _buildFunctionTree() { | |
| 1101 if (functionTree == null) { | |
| 1102 var tableBody = shadowRoot.querySelector('#treeBody'); | |
| 1103 assert(tableBody != null); | |
| 1104 functionTree = new TableTree(tableBody, 2); | |
| 1105 } | |
| 1106 if (focusedFunction == null) { | |
| 1107 return; | |
| 1108 } | |
| 1109 bool exclusive = directionSelector == 'Up'; | |
| 1110 var tree = profile.loadFunctionTree(exclusive ? 'exclusive' : 'inclusive'); | |
| 1111 if (tree == null) { | |
| 1112 return; | |
| 1113 } | |
| 1114 var filter = (FunctionCallTreeNode node) { | |
| 1115 return node.profileFunction.function == focusedFunction; | |
| 1116 }; | |
| 1117 tree = tree.filtered(filter); | |
| 1118 var rootRow = | |
| 1119 new FunctionProfileTreeRow(functionTree, null, profile, tree.root); | |
| 1120 functionTree.initialize(rootRow); | |
| 1121 } | |
| 1122 } | |
| OLD | NEW |