| Index: runtime/observatory/lib/src/elements/cpu_profile.dart
|
| diff --git a/runtime/observatory/lib/src/elements/cpu_profile.dart b/runtime/observatory/lib/src/elements/cpu_profile.dart
|
| index c026d7c04477718bebb916c27fc15fd32fbc110c..6f74bbc2db792e703bffe0a627fae71223911493 100644
|
| --- a/runtime/observatory/lib/src/elements/cpu_profile.dart
|
| +++ b/runtime/observatory/lib/src/elements/cpu_profile.dart
|
| @@ -569,6 +569,109 @@ class StackTraceTreeConfigElement extends ObservatoryElement {
|
| @observable String directionSelector = 'Up';
|
| }
|
|
|
| +class FunctionCallTreeNodeRow extends VirtualTreeRow {
|
| + final CpuProfile profile;
|
| + final FunctionCallTreeNode node;
|
| + final String selfPercent;
|
| + final String totalPercent;
|
| + final String percent;
|
| +
|
| + FunctionCallTreeNodeRow(VirtualTree tree,
|
| + int depth,
|
| + this.profile,
|
| + FunctionCallTreeNode node)
|
| + : node = node,
|
| + selfPercent = Utils.formatPercentNormalized(node.profileFunction.normalizedExclusiveTicks),
|
| + totalPercent = Utils.formatPercentNormalized(node.profileFunction.normalizedInclusiveTicks),
|
| + percent = Utils.formatPercentNormalized(node.percentage),
|
| + super(tree, depth) {
|
| + }
|
| +
|
| + void onRender(DivElement rowDiv) {
|
| + rowDiv.children.add(
|
| + makeText(totalPercent, toolTip: 'global % on stack'));
|
| + rowDiv.children.add(makeGap());
|
| + rowDiv.children.add(
|
| + makeText(selfPercent, toolTip: 'global % executing'));
|
| + rowDiv.children.add(makeGap());
|
| + rowDiv.children.add(makeIndenter(colored: false));
|
| + rowDiv.children.add(makeColorBar());
|
| + rowDiv.children.add(makeGap(ems: 1.0));
|
| + rowDiv.children.add(makeExpander());
|
| + rowDiv.children.add(
|
| + makeText(percent, toolTip: 'tree node %', flexBasis: null));
|
| + rowDiv.children.add(makeGap(ems: 0.5));
|
| + functionRef.ref = node.profileFunction.function;
|
| + rowDiv.children.add(functionRef);
|
| + }
|
| +
|
| + var functionRef = new Element.tag('function-ref');
|
| +
|
| + int get childCount => node.children.length;
|
| +
|
| + void onShow() {
|
| + if (children.length > 0) {
|
| + return;
|
| + }
|
| + for (var childNode in node.children) {
|
| + var row = new FunctionCallTreeNodeRow(tree, depth + 1, profile, childNode);
|
| + children.add(row);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +class CodeCallTreeNodeRow extends VirtualTreeRow {
|
| + final CpuProfile profile;
|
| + final CodeCallTreeNode node;
|
| + final String selfPercent;
|
| + final String totalPercent;
|
| + final String percent;
|
| +
|
| + CodeCallTreeNodeRow(VirtualTree tree,
|
| + int depth,
|
| + this.profile,
|
| + CodeCallTreeNode node)
|
| + : node = node,
|
| + selfPercent = Utils.formatPercentNormalized(node.profileCode.normalizedExclusiveTicks),
|
| + totalPercent = Utils.formatPercentNormalized(node.profileCode.normalizedInclusiveTicks),
|
| + percent = Utils.formatPercentNormalized(node.percentage),
|
| + super(tree, depth) {
|
| + }
|
| +
|
| + void onRender(DivElement rowDiv) {
|
| + rowDiv.children.add(
|
| + makeText(totalPercent, toolTip: 'global % on stack'));
|
| + rowDiv.children.add(makeGap());
|
| + rowDiv.children.add(
|
| + makeText(selfPercent, toolTip: 'global % executing'));
|
| + rowDiv.children.add(makeGap());
|
| + rowDiv.children.add(makeIndenter(colored: false));
|
| + rowDiv.children.add(makeColorBar());
|
| + rowDiv.children.add(makeGap(ems: 1.0));
|
| + rowDiv.children.add(makeExpander());
|
| + rowDiv.children.add(
|
| + makeText(percent, toolTip: 'tree node %', flexBasis: null));
|
| + rowDiv.children.add(makeGap(ems: 0.5));
|
| + codeRef.ref = node.profileCode.code;
|
| + rowDiv.children.add(codeRef);
|
| + }
|
| +
|
| + var codeRef = new Element.tag('code-ref');
|
| +
|
| + int get childCount => node.children.length;
|
| +
|
| + void onShow() {
|
| + if (children.length > 0) {
|
| + return;
|
| + }
|
| + for (var childNode in node.children) {
|
| + var row = new CodeCallTreeNodeRow(tree, depth + 1, profile, childNode);
|
| + children.add(row);
|
| + }
|
| + }
|
| +}
|
| +
|
| /// Displays a CpuProfile
|
| @CustomTag('cpu-profile')
|
| class CpuProfileElement extends ObservatoryElement {
|
| @@ -587,10 +690,32 @@ class CpuProfileElement extends ObservatoryElement {
|
| shadowRoot.querySelector('#stackTraceTreeConfig');
|
| assert(stackTraceTreeConfigElement != null);
|
| stackTraceTreeConfigElement.onTreeConfigChange = onTreeConfigChange;
|
| - cpuProfileTreeElement = shadowRoot.querySelector('#cpuProfileTree');
|
| - assert(cpuProfileTreeElement != null);
|
| - cpuProfileTreeElement.profile = sampleBufferControlElement.profile;
|
| + cpuProfileVirtualTreeElement = shadowRoot.querySelector('#cpuProfileVirtualTree');
|
| + assert(cpuProfileVirtualTreeElement != null);
|
| + cpuProfileVirtualTreeElement.profile = sampleBufferControlElement.profile;
|
| + _resizeSubscription = window.onResize.listen((_) => _updateSize());
|
| _updateTask.queue();
|
| + _updateSize();
|
| + }
|
| +
|
| + detached() {
|
| + super.detached();
|
| + if (_resizeSubscription != null) {
|
| + _resizeSubscription.cancel();
|
| + }
|
| + }
|
| +
|
| + _updateSize() {
|
| + var applySize = (e) {
|
| + Rectangle rect = e.getBoundingClientRect();
|
| + final totalHeight = window.innerHeight;
|
| + final top = rect.top;
|
| + final bottomMargin = 200;
|
| + final mainHeight = totalHeight - top - bottomMargin;
|
| + e.style.setProperty('height', '${mainHeight}px');
|
| + };
|
| + HtmlElement e2 = $['cpuProfileVirtualTree'];
|
| + applySize(e2);
|
| }
|
|
|
| isolateChanged(oldValue) {
|
| @@ -616,8 +741,8 @@ class CpuProfileElement extends ObservatoryElement {
|
| if (modeSelector == 'Code') {
|
| mode = ProfileTreeMode.Code;
|
| }
|
| - cpuProfileTreeElement.direction = direction;
|
| - cpuProfileTreeElement.mode = mode;
|
| + cpuProfileVirtualTreeElement.direction = direction;
|
| + cpuProfileVirtualTreeElement.mode = mode;
|
| _renderTask.queue();
|
| }
|
|
|
| @@ -633,16 +758,17 @@ class CpuProfileElement extends ObservatoryElement {
|
| }
|
|
|
| render() {
|
| - cpuProfileTreeElement.render();
|
| + cpuProfileVirtualTreeElement.render();
|
| }
|
|
|
| @published Isolate isolate;
|
|
|
| + StreamSubscription _resizeSubscription;
|
| Task _updateTask;
|
| Task _renderTask;
|
| SampleBufferControlElement sampleBufferControlElement;
|
| StackTraceTreeConfigElement stackTraceTreeConfigElement;
|
| - CpuProfileTreeElement cpuProfileTreeElement;
|
| + CpuProfileVirtualTreeElement cpuProfileVirtualTreeElement;
|
| }
|
|
|
| class NameSortedTable extends SortedTable {
|
| @@ -1140,6 +1266,85 @@ enum ProfileTreeMode {
|
| Function,
|
| }
|
|
|
| +@CustomTag('cpu-profile-virtual-tree')
|
| +class CpuProfileVirtualTreeElement extends ObservatoryElement {
|
| + ProfileTreeDirection direction = ProfileTreeDirection.Exclusive;
|
| + ProfileTreeMode mode = ProfileTreeMode.Function;
|
| + CpuProfile profile;
|
| + VirtualTree virtualTree;
|
| + FunctionCallTreeNodeFilter functionFilter;
|
| + @observable bool show = true;
|
| +
|
| + CpuProfileVirtualTreeElement.created() : super.created();
|
| +
|
| + void render() {
|
| + _updateView();
|
| + }
|
| +
|
| + showChanged(oldValue) {
|
| + var treeTable = shadowRoot.querySelector('#treeTable');
|
| + assert(treeTable != null);
|
| + treeTable.style.display = show ? 'table' : 'none';
|
| + }
|
| +
|
| + void _updateView() {
|
| + virtualTree?.clear();
|
| + virtualTree?.uninstall();
|
| + virtualTree = null;
|
| + bool exclusive = direction == ProfileTreeDirection.Exclusive;
|
| + if (mode == ProfileTreeMode.Code) {
|
| + _buildCodeTree(exclusive);
|
| + } else {
|
| + assert(mode == ProfileTreeMode.Function);
|
| + _buildFunctionTree(exclusive);
|
| + }
|
| + virtualTree?.refresh();
|
| + }
|
| +
|
| + void _buildFunctionTree(bool exclusive) {
|
| + var treeBody = shadowRoot.querySelector('#tree');
|
| + assert(treeBody != null);
|
| + virtualTree = new VirtualTree(32, treeBody);
|
| + if (profile == null) {
|
| + return;
|
| + }
|
| + var tree = profile.loadFunctionTree(exclusive ? 'exclusive' : 'inclusive');
|
| + if (tree == null) {
|
| + return;
|
| + }
|
| + if (functionFilter != null) {
|
| + tree = tree.filtered(functionFilter);
|
| + }
|
| + for (var child in tree.root.children) {
|
| + virtualTree.rows.add(
|
| + new FunctionCallTreeNodeRow(virtualTree, 0, profile, child));
|
| + }
|
| + if (virtualTree.rows.length == 1) {
|
| + virtualTree.rows[0].expanded = true;
|
| + }
|
| + }
|
| +
|
| + void _buildCodeTree(bool exclusive) {
|
| + var treeBody = shadowRoot.querySelector('#tree');
|
| + assert(treeBody != null);
|
| + virtualTree = new VirtualTree(32, treeBody);
|
| + if (profile == null) {
|
| + return;
|
| + }
|
| + var tree = profile.loadCodeTree(exclusive ? 'exclusive' : 'inclusive');
|
| + if (tree == null) {
|
| + return;
|
| + }
|
| + for (var child in tree.root.children) {
|
| + virtualTree.rows.add(
|
| + new CodeCallTreeNodeRow(virtualTree, 0, profile, child));
|
| + }
|
| + if (virtualTree.rows.length == 1) {
|
| + virtualTree.rows[0].expanded = true;
|
| + }
|
| + }
|
| +}
|
| +
|
| @CustomTag('cpu-profile-tree')
|
| class CpuProfileTreeElement extends ObservatoryElement {
|
| ProfileTreeDirection direction = ProfileTreeDirection.Exclusive;
|
|
|