| Index: runtime/observatory/lib/src/elements/cpu_profile_table.dart
|
| diff --git a/runtime/observatory/lib/src/elements/cpu_profile_table.dart b/runtime/observatory/lib/src/elements/cpu_profile_table.dart
|
| index 2a76e17bc9275de1f03424cbbb7fbba50c566724..88edbc6b2cb20a2994662bbc656a456d301ccd0a 100644
|
| --- a/runtime/observatory/lib/src/elements/cpu_profile_table.dart
|
| +++ b/runtime/observatory/lib/src/elements/cpu_profile_table.dart
|
| @@ -6,924 +6,446 @@ library cpu_profile_table_element;
|
|
|
| import 'dart:async';
|
| import 'dart:html';
|
| -import 'observatory_element.dart';
|
| -import 'sample_buffer_control.dart';
|
| -import 'stack_trace_tree_config.dart';
|
| -import 'cpu_profile/virtual_tree.dart';
|
| -import 'package:observatory/service.dart';
|
| -import 'package:observatory/app.dart';
|
| -import 'package:observatory/cpu_profile.dart';
|
| -import 'package:observatory/elements.dart';
|
| import 'package:observatory/models.dart' as M;
|
| -import 'package:observatory/repositories.dart';
|
| -import 'package:polymer/polymer.dart';
|
| -
|
| -List<String> sorted(Set<String> attributes) {
|
| - var list = attributes.toList();
|
| - list.sort();
|
| - return list;
|
| +import 'package:observatory/src/elements/containers/virtual_collection.dart';
|
| +import 'package:observatory/src/elements/cpu_profile/virtual_tree.dart';
|
| +import 'package:observatory/src/elements/function_ref.dart';
|
| +import 'package:observatory/src/elements/helpers/rendering_scheduler.dart';
|
| +import 'package:observatory/src/elements/helpers/tag.dart';
|
| +import 'package:observatory/src/elements/helpers/uris.dart';
|
| +import 'package:observatory/src/elements/nav/bar.dart';
|
| +import 'package:observatory/src/elements/nav/isolate_menu.dart';
|
| +import 'package:observatory/src/elements/nav/menu.dart';
|
| +import 'package:observatory/src/elements/nav/notify.dart';
|
| +import 'package:observatory/src/elements/nav/refresh.dart';
|
| +import 'package:observatory/src/elements/nav/top_menu.dart';
|
| +import 'package:observatory/src/elements/nav/vm_menu.dart';
|
| +import 'package:observatory/src/elements/sample_buffer_control.dart';
|
| +import 'package:observatory/src/elements/stack_trace_tree_config.dart';
|
| +import 'package:observatory/utils.dart';
|
| +
|
| +enum _Table {
|
| + functions,
|
| + caller,
|
| + callee
|
| }
|
|
|
| -abstract class ProfileTreeRow<T> extends TableTreeRow {
|
| - final CpuProfile profile;
|
| - final T node;
|
| - final String selfPercent;
|
| - final String percent;
|
| - bool _infoBoxShown = false;
|
| - HtmlElement infoBox;
|
| - HtmlElement infoButton;
|
| -
|
| - ProfileTreeRow(TableTree tree, TableTreeRow parent,
|
| - this.profile, this.node, double selfPercent, double percent)
|
| - : super(tree, parent),
|
| - selfPercent = Utils.formatPercentNormalized(selfPercent),
|
| - percent = Utils.formatPercentNormalized(percent);
|
| -
|
| - static _addToMemberList(DivElement memberList, Map<String, String> items) {
|
| - items.forEach((k, v) {
|
| - var item = new DivElement();
|
| - item.classes.add('memberItem');
|
| - var name = new DivElement();
|
| - name.classes.add('memberName');
|
| - name.text = k;
|
| - var value = new DivElement();
|
| - value.classes.add('memberValue');
|
| - value.text = v;
|
| - item.children.add(name);
|
| - item.children.add(value);
|
| - memberList.children.add(item);
|
| - });
|
| - }
|
| -
|
| - makeInfoBox() {
|
| - if (infoBox != null) {
|
| - return;
|
| - }
|
| - infoBox = new DivElement();
|
| - infoBox.classes.add('infoBox');
|
| - infoBox.classes.add('shadow');
|
| - infoBox.style.display = 'none';
|
| - listeners.add(infoBox.onClick.listen((e) => e.stopPropagation()));
|
| - }
|
| -
|
| - makeInfoButton() {
|
| - infoButton = new SpanElement();
|
| - infoButton.style.marginLeft = 'auto';
|
| - infoButton.style.marginRight = '1em';
|
| - infoButton.children.add(new Element.tag('icon-info-outline'));
|
| - listeners.add(infoButton.onClick.listen((event) {
|
| - event.stopPropagation();
|
| - toggleInfoBox();
|
| - }));
|
| - }
|
| -
|
| - static const attributes = const {
|
| - 'optimized' : const ['O', null, 'Optimized'],
|
| - 'unoptimized' : const ['U', null, 'Unoptimized'],
|
| - 'inlined' : const ['I', null, 'Inlined'],
|
| - 'intrinsic' : const ['It', null, 'Intrinsic'],
|
| - 'ffi' : const ['F', null, 'FFI'],
|
| - 'dart' : const ['D', null, 'Dart'],
|
| - 'tag' : const ['T', null, 'Tag'],
|
| - 'native' : const ['N', null, 'Native'],
|
| - 'stub': const ['S', null, 'Stub'],
|
| - 'synthetic' : const ['?', null, 'Synthetic'],
|
| - };
|
| -
|
| - HtmlElement newAttributeBox(String attribute) {
|
| - List attributeDetails = attributes[attribute];
|
| - if (attributeDetails == null) {
|
| - print('could not find attribute $attribute');
|
| - return null;
|
| - }
|
| - var element = new SpanElement();
|
| - element.style.border = 'solid 2px #ECECEC';
|
| - element.style.height = '100%';
|
| - element.style.display = 'inline-block';
|
| - element.style.textAlign = 'center';
|
| - element.style.minWidth = '1.5em';
|
| - element.style.fontWeight = 'bold';
|
| - if (attributeDetails[1] != null) {
|
| - element.style.backgroundColor = attributeDetails[1];
|
| - }
|
| - element.text = attributeDetails[0];
|
| - element.title = attributeDetails[2];
|
| - return element;
|
| - }
|
| -
|
| - onHide() {
|
| - super.onHide();
|
| - infoBox = null;
|
| - infoButton = null;
|
| - }
|
| -
|
| - showInfoBox() {
|
| - if ((infoButton == null) || (infoBox == null)) {
|
| - return;
|
| - }
|
| - _infoBoxShown = true;
|
| - infoBox.style.display = 'block';
|
| - infoButton.children.clear();
|
| - infoButton.children.add(new Element.tag('icon-info'));
|
| - }
|
| -
|
| - hideInfoBox() {
|
| - _infoBoxShown = false;
|
| - if ((infoButton == null) || (infoBox == null)) {
|
| - return;
|
| - }
|
| - infoBox.style.display = 'none';
|
| - infoButton.children.clear();
|
| - infoButton.children.add(new Element.tag('icon-info-outline'));
|
| - }
|
| -
|
| - toggleInfoBox() {
|
| - if (_infoBoxShown) {
|
| - hideInfoBox();
|
| - } else {
|
| - showInfoBox();
|
| - }
|
| - }
|
| -
|
| - hideAllInfoBoxes() {
|
| - final List<ProfileTreeRow> rows = tree.rows;
|
| - for (var row in rows) {
|
| - row.hideInfoBox();
|
| - }
|
| - }
|
| -
|
| - onClick(MouseEvent e) {
|
| - e.stopPropagation();
|
| - if (e.altKey) {
|
| - bool show = !_infoBoxShown;
|
| - hideAllInfoBoxes();
|
| - if (show) {
|
| - showInfoBox();
|
| - }
|
| - return;
|
| - }
|
| - super.onClick(e);
|
| - }
|
| -
|
| - HtmlElement newCodeRef(ProfileCode code) {
|
| - var codeRef = new Element.tag('code-ref');
|
| - codeRef.ref = code.code;
|
| - return codeRef;
|
| - }
|
| -
|
| - HtmlElement newFunctionRef(ProfileFunction function) {
|
| - var ref = new Element.tag('function-ref');
|
| - ref.ref = function.function;
|
| - return ref;
|
| - }
|
| -
|
| - HtmlElement hr() {
|
| - var element = new HRElement();
|
| - return element;
|
| - }
|
| -
|
| - HtmlElement div(String text) {
|
| - var element = new DivElement();
|
| - element.text = text;
|
| - return element;
|
| - }
|
| -
|
| - HtmlElement br() {
|
| - return new BRElement();
|
| - }
|
| -
|
| - HtmlElement span(String text) {
|
| - var element = new SpanElement();
|
| - element.style.minWidth = '1em';
|
| - element.text = text;
|
| - return element;
|
| - }
|
| +enum _SortingField {
|
| + exclusive,
|
| + inclusive,
|
| + caller,
|
| + callee,
|
| + method
|
| }
|
|
|
| -class CodeProfileTreeRow extends ProfileTreeRow<CodeCallTreeNode> {
|
| - CodeProfileTreeRow(TableTree tree, CodeProfileTreeRow parent,
|
| - CpuProfile profile, CodeCallTreeNode node)
|
| - : super(tree, parent, profile, node,
|
| - node.profileCode.normalizedExclusiveTicks,
|
| - node.percentage) {
|
| - // fill out attributes.
|
| - }
|
| -
|
| - bool hasChildren() => node.children.length > 0;
|
| -
|
| - void onShow() {
|
| - super.onShow();
|
| -
|
| - if (children.length == 0) {
|
| - for (var childNode in node.children) {
|
| - var row = new CodeProfileTreeRow(tree, this, profile, childNode);
|
| - children.add(row);
|
| - }
|
| - }
|
| -
|
| - // Fill in method column.
|
| - var methodColumn = flexColumns[0];
|
| - methodColumn.style.justifyContent = 'flex-start';
|
| - methodColumn.style.position = 'relative';
|
| -
|
| - // Percent.
|
| - var percentNode = new DivElement();
|
| - percentNode.text = percent;
|
| - percentNode.style.minWidth = '5em';
|
| - percentNode.style.textAlign = 'right';
|
| - percentNode.title = 'Executing: $selfPercent';
|
| - methodColumn.children.add(percentNode);
|
| -
|
| - // Gap.
|
| - var gap = new SpanElement();
|
| - gap.style.minWidth = '1em';
|
| - methodColumn.children.add(gap);
|
| -
|
| - // Code link.
|
| - var codeRef = newCodeRef(node.profileCode);
|
| - codeRef.style.alignSelf = 'center';
|
| - methodColumn.children.add(codeRef);
|
| -
|
| - gap = new SpanElement();
|
| - gap.style.minWidth = '1em';
|
| - methodColumn.children.add(gap);
|
| -
|
| - for (var attribute in sorted(node.attributes)) {
|
| - methodColumn.children.add(newAttributeBox(attribute));
|
| - }
|
| -
|
| - makeInfoBox();
|
| - methodColumn.children.add(infoBox);
|
| -
|
| - infoBox.children.add(span('Code '));
|
| - infoBox.children.add(newCodeRef(node.profileCode));
|
| - infoBox.children.add(span(' '));
|
| - for (var attribute in sorted(node.profileCode.attributes)) {
|
| - infoBox.children.add(newAttributeBox(attribute));
|
| - }
|
| - infoBox.children.add(br());
|
| - infoBox.children.add(br());
|
| - var memberList = new DivElement();
|
| - memberList.classes.add('memberList');
|
| - infoBox.children.add(br());
|
| - infoBox.children.add(memberList);
|
| - ProfileTreeRow._addToMemberList(memberList, {
|
| - 'Exclusive ticks' : node.profileCode.formattedExclusiveTicks,
|
| - 'Cpu time' : node.profileCode.formattedCpuTime,
|
| - 'Inclusive ticks' : node.profileCode.formattedInclusiveTicks,
|
| - 'Call stack time' : node.profileCode.formattedOnStackTime,
|
| - });
|
| -
|
| - makeInfoButton();
|
| - methodColumn.children.add(infoButton);
|
| -
|
| - // Fill in self column.
|
| - var selfColumn = flexColumns[1];
|
| - selfColumn.style.position = 'relative';
|
| - selfColumn.style.alignItems = 'center';
|
| - selfColumn.text = selfPercent;
|
| - }
|
| +enum _SortingDirection {
|
| + ascending,
|
| + descending
|
| }
|
|
|
| -class FunctionProfileTreeRow extends ProfileTreeRow<FunctionCallTreeNode> {
|
| - FunctionProfileTreeRow(TableTree tree, FunctionProfileTreeRow parent,
|
| - CpuProfile profile, FunctionCallTreeNode node)
|
| - : super(tree, parent, profile, node,
|
| - node.profileFunction.normalizedExclusiveTicks,
|
| - node.percentage) {
|
| - // fill out attributes.
|
| - }
|
| -
|
| - bool hasChildren() => node.children.length > 0;
|
| -
|
| - onShow() {
|
| - super.onShow();
|
| - if (children.length == 0) {
|
| - for (var childNode in node.children) {
|
| - var row = new FunctionProfileTreeRow(tree, this, profile, childNode);
|
| - children.add(row);
|
| - }
|
| - }
|
| -
|
| - var methodColumn = flexColumns[0];
|
| - methodColumn.style.justifyContent = 'flex-start';
|
| -
|
| - var codeAndFunctionColumn = new DivElement();
|
| - codeAndFunctionColumn.classes.add('flex-column');
|
| - codeAndFunctionColumn.style.justifyContent = 'center';
|
| - codeAndFunctionColumn.style.width = '100%';
|
| - methodColumn.children.add(codeAndFunctionColumn);
|
| -
|
| - var functionRow = new DivElement();
|
| - functionRow.classes.add('flex-row');
|
| - functionRow.style.position = 'relative';
|
| - functionRow.style.justifyContent = 'flex-start';
|
| - codeAndFunctionColumn.children.add(functionRow);
|
| -
|
| - // Insert the parent percentage
|
| - var parentPercent = new SpanElement();
|
| - parentPercent.text = percent;
|
| - parentPercent.style.minWidth = '4em';
|
| - parentPercent.style.alignSelf = 'center';
|
| - parentPercent.style.textAlign = 'right';
|
| - parentPercent.title = 'Executing: $selfPercent';
|
| - functionRow.children.add(parentPercent);
|
| -
|
| - // Gap.
|
| - var gap = new SpanElement();
|
| - gap.style.minWidth = '1em';
|
| - gap.text = ' ';
|
| - functionRow.children.add(gap);
|
| -
|
| - var functionRef = new Element.tag('function-ref');
|
| - functionRef.ref = node.profileFunction.function;
|
| - functionRef.style.alignSelf = 'center';
|
| - functionRow.children.add(functionRef);
|
| -
|
| - gap = new SpanElement();
|
| - gap.style.minWidth = '1em';
|
| - gap.text = ' ';
|
| - functionRow.children.add(gap);
|
| -
|
| - for (var attribute in sorted(node.attributes)) {
|
| - functionRow.children.add(newAttributeBox(attribute));
|
| - }
|
| -
|
| - makeInfoBox();
|
| - functionRow.children.add(infoBox);
|
| -
|
| - if (M.hasDartCode(node.profileFunction.function.kind)) {
|
| - infoBox.children.add(div('Code for current node'));
|
| - infoBox.children.add(br());
|
| - var totalTicks = node.totalCodesTicks;
|
| - var numCodes = node.codes.length;
|
| - for (var i = 0; i < numCodes; i++) {
|
| - var codeRowSpan = new DivElement();
|
| - codeRowSpan.style.paddingLeft = '1em';
|
| - infoBox.children.add(codeRowSpan);
|
| - var nodeCode = node.codes[i];
|
| - var ticks = nodeCode.ticks;
|
| - var percentage = Utils.formatPercent(ticks, totalTicks);
|
| - var percentageSpan = new SpanElement();
|
| - percentageSpan.style.display = 'inline-block';
|
| - percentageSpan.text = '$percentage';
|
| - percentageSpan.style.minWidth = '5em';
|
| - percentageSpan.style.textAlign = 'right';
|
| - codeRowSpan.children.add(percentageSpan);
|
| - var codeRef = new Element.tag('code-ref');
|
| - codeRef.ref = nodeCode.code.code;
|
| - codeRef.style.marginLeft = '1em';
|
| - codeRef.style.marginRight = 'auto';
|
| - codeRef.style.width = '100%';
|
| - codeRowSpan.children.add(codeRef);
|
| - }
|
| - infoBox.children.add(hr());
|
| - }
|
| - infoBox.children.add(span('Function '));
|
| - infoBox.children.add(newFunctionRef(node.profileFunction));
|
| - infoBox.children.add(span(' '));
|
| - for (var attribute in sorted(node.profileFunction.attributes)) {
|
| - infoBox.children.add(newAttributeBox(attribute));
|
| - }
|
| - var memberList = new DivElement();
|
| - memberList.classes.add('memberList');
|
| - infoBox.children.add(br());
|
| - infoBox.children.add(br());
|
| - infoBox.children.add(memberList);
|
| - infoBox.children.add(br());
|
| - ProfileTreeRow._addToMemberList(memberList, {
|
| - 'Exclusive ticks' : node.profileFunction.formattedExclusiveTicks,
|
| - 'Cpu time' : node.profileFunction.formattedCpuTime,
|
| - 'Inclusive ticks' : node.profileFunction.formattedInclusiveTicks,
|
| - 'Call stack time' : node.profileFunction.formattedOnStackTime,
|
| - });
|
| -
|
| - if (M.hasDartCode(node.profileFunction.function.kind)) {
|
| - infoBox.children.add(div('Code containing function'));
|
| - infoBox.children.add(br());
|
| - var totalTicks = profile.sampleCount;
|
| - var codes = node.profileFunction.profileCodes;
|
| - var numCodes = codes.length;
|
| - for (var i = 0; i < numCodes; i++) {
|
| - var codeRowSpan = new DivElement();
|
| - codeRowSpan.style.paddingLeft = '1em';
|
| - infoBox.children.add(codeRowSpan);
|
| - var profileCode = codes[i];
|
| - var code = profileCode.code;
|
| - var ticks = profileCode.inclusiveTicks;
|
| - var percentage = Utils.formatPercent(ticks, totalTicks);
|
| - var percentageSpan = new SpanElement();
|
| - percentageSpan.style.display = 'inline-block';
|
| - percentageSpan.text = '$percentage';
|
| - percentageSpan.style.minWidth = '5em';
|
| - percentageSpan.style.textAlign = 'right';
|
| - percentageSpan.title = 'Inclusive ticks';
|
| - codeRowSpan.children.add(percentageSpan);
|
| - var codeRef = new Element.tag('code-ref');
|
| - codeRef.ref = code;
|
| - codeRef.style.marginLeft = '1em';
|
| - codeRef.style.marginRight = 'auto';
|
| - codeRef.style.width = '100%';
|
| - codeRowSpan.children.add(codeRef);
|
| - }
|
| - }
|
| -
|
| - makeInfoButton();
|
| - methodColumn.children.add(infoButton);
|
| -
|
| - // Fill in self column.
|
| - var selfColumn = flexColumns[1];
|
| - selfColumn.style.position = 'relative';
|
| - selfColumn.style.alignItems = 'center';
|
| - selfColumn.text = selfPercent;
|
| - }
|
| -}
|
| +class CpuProfileTableElement extends HtmlElement implements Renderable {
|
| + static const tag = const Tag<CpuProfileTableElement>('cpu-profile-table',
|
| + dependencies: const [
|
| + FunctionRefElement.tag,
|
| + NavBarElement.tag,
|
| + NavTopMenuElement.tag,
|
| + NavVMMenuElement.tag,
|
| + NavIsolateMenuElement.tag,
|
| + NavMenuElement.tag,
|
| + NavRefreshElement.tag,
|
| + NavNotifyElement.tag,
|
| + SampleBufferControlElement.tag,
|
| + StackTraceTreeConfigElement.tag,
|
| + CpuProfileVirtualTreeElement.tag,
|
| + VirtualCollectionElement.tag
|
| + ]);
|
| +
|
| + RenderingScheduler<CpuProfileTableElement> _r;
|
| +
|
| + Stream<RenderedEvent<CpuProfileTableElement>> get onRendered => _r.onRendered;
|
| +
|
| + M.VM _vm;
|
| + M.IsolateRef _isolate;
|
| + M.EventRepository _events;
|
| + M.NotificationRepository _notifications;
|
| + M.IsolateSampleProfileRepository _profiles;
|
| + Stream<M.SampleProfileLoadingProgressEvent> _progressStream;
|
| + M.SampleProfileLoadingProgress _progress;
|
| + final _sortingField = <_Table, _SortingField>{
|
| + _Table.functions : _SortingField.exclusive,
|
| + _Table.caller : _SortingField.caller,
|
| + _Table.callee : _SortingField.callee,
|
| + };
|
| + final _sortingDirection = <_Table, _SortingDirection>{
|
| + _Table.functions : _SortingDirection.descending,
|
| + _Table.caller : _SortingDirection.descending,
|
| + _Table.callee : _SortingDirection.descending,
|
| + };
|
| + String _filter = '';
|
| +
|
| +
|
| + M.IsolateRef get isolate => _isolate;
|
| + M.NotificationRepository get notifications => _notifications;
|
| + M.IsolateSampleProfileRepository get profiles => _profiles;
|
| + M.VMRef get vm => _vm;
|
| +
|
| + factory CpuProfileTableElement(M.VM vm, M.IsolateRef isolate,
|
| + M.EventRepository events,
|
| + M.NotificationRepository notifications,
|
| + M.IsolateSampleProfileRepository profiles,
|
| + {RenderingQueue queue}) {
|
| + assert(vm != null);
|
| + assert(isolate != null);
|
| + assert(events != null);
|
| + assert(notifications != null);
|
| + assert(profiles != null);
|
| + CpuProfileTableElement e = document.createElement(tag.name);
|
| + e._r = new RenderingScheduler(e, queue: queue);
|
| + e._vm = vm;
|
| + e._isolate = isolate;
|
| + e._events = events;
|
| + e._notifications = notifications;
|
| + e._profiles = profiles;
|
| + return e;
|
| + }
|
| +
|
| + CpuProfileTableElement.created() : super.created();
|
|
|
| -class NameSortedTable extends SortedTable {
|
| - NameSortedTable(columns) : super(columns);
|
| @override
|
| - dynamic getSortKeyFor(int row, int col) {
|
| - if (col == FUNCTION_COLUMN) {
|
| - // Use name as sort key.
|
| - return rows[row].values[col].name;
|
| - }
|
| - return super.getSortKeyFor(row, col);
|
| - }
|
| -
|
| - SortedTableRow rowFromIndex(int tableIndex) {
|
| - final modelIndex = sortedRows[tableIndex];
|
| - return rows[modelIndex];
|
| - }
|
| -
|
| - static const FUNCTION_SPACER_COLUMNS = const [];
|
| - static const FUNCTION_COLUMN = 2;
|
| - TableRowElement _makeFunctionRow() {
|
| - var tr = new TableRowElement();
|
| - var cell;
|
| -
|
| - // Add percentage.
|
| - cell = tr.insertCell(-1);
|
| - cell = tr.insertCell(-1);
|
| -
|
| - // Add function ref.
|
| - cell = tr.insertCell(-1);
|
| - var functionRef = new Element.tag('function-ref');
|
| - cell.children.add(functionRef);
|
| -
|
| - return tr;
|
| - }
|
| -
|
| - static const CALL_SPACER_COLUMNS = const [];
|
| - static const CALL_FUNCTION_COLUMN = 1;
|
| - TableRowElement _makeCallRow() {
|
| - var tr = new TableRowElement();
|
| - var cell;
|
| -
|
| - // Add percentage.
|
| - cell = tr.insertCell(-1);
|
| - // Add function ref.
|
| - cell = tr.insertCell(-1);
|
| - var functionRef = new Element.tag('function-ref');
|
| - cell.children.add(functionRef);
|
| - return tr;
|
| - }
|
| -
|
| - _updateRow(TableRowElement tr,
|
| - int rowIndex,
|
| - List spacerColumns,
|
| - int refColumn) {
|
| - var row = rows[rowIndex];
|
| - // Set reference
|
| - var ref = tr.children[refColumn].children[0];
|
| - ref.ref = row.values[refColumn];
|
| -
|
| - for (var i = 0; i < row.values.length; i++) {
|
| - if (spacerColumns.contains(i) || (i == refColumn)) {
|
| - // Skip spacer columns.
|
| - continue;
|
| - }
|
| - var cell = tr.children[i];
|
| - cell.title = row.values[i].toString();
|
| - cell.text = getFormattedValue(rowIndex, i);
|
| - }
|
| - }
|
| -
|
| - _updateTableView(HtmlElement table,
|
| - HtmlElement makeEmptyRow(),
|
| - void onRowClick(TableRowElement tr),
|
| - List spacerColumns,
|
| - int refColumn) {
|
| - assert(table != null);
|
| -
|
| - // Resize DOM table.
|
| - if (table.children.length > sortedRows.length) {
|
| - // Shrink the table.
|
| - var deadRows = table.children.length - sortedRows.length;
|
| - for (var i = 0; i < deadRows; i++) {
|
| - table.children.removeLast();
|
| - }
|
| - } else if (table.children.length < sortedRows.length) {
|
| - // Grow table.
|
| - var newRows = sortedRows.length - table.children.length;
|
| - for (var i = 0; i < newRows; i++) {
|
| - var row = makeEmptyRow();
|
| - row.onClick.listen((e) {
|
| - e.stopPropagation();
|
| - e.preventDefault();
|
| - onRowClick(row);
|
| - });
|
| - table.children.add(row);
|
| - }
|
| - }
|
| -
|
| - assert(table.children.length == sortedRows.length);
|
| -
|
| - // Fill table.
|
| - for (var i = 0; i < sortedRows.length; i++) {
|
| - var rowIndex = sortedRows[i];
|
| - var tr = table.children[i];
|
| - _updateRow(tr, rowIndex, spacerColumns, refColumn);
|
| - }
|
| - }
|
| -}
|
| -
|
| -@CustomTag('cpu-profile-table')
|
| -class CpuProfileTableElement extends ObservatoryElement {
|
| -
|
| -
|
| - CpuProfileTableElement.created() : super.created() {
|
| - _updateTask = new Task(update);
|
| - _renderTask = new Task(render);
|
| - var columns = [
|
| - new SortedTableColumn.withFormatter('Executing (%)',
|
| - Utils.formatPercentNormalized),
|
| - new SortedTableColumn.withFormatter('In stack (%)',
|
| - Utils.formatPercentNormalized),
|
| - new SortedTableColumn('Method'),
|
| - ];
|
| - profileTable = new NameSortedTable(columns);
|
| - profileTable.sortColumnIndex = 0;
|
| -
|
| - columns = [
|
| - new SortedTableColumn.withFormatter('Callees (%)',
|
| - Utils.formatPercentNormalized),
|
| - new SortedTableColumn('Method')
|
| - ];
|
| - profileCalleesTable = new NameSortedTable(columns);
|
| - profileCalleesTable.sortColumnIndex = 0;
|
| -
|
| - columns = [
|
| - new SortedTableColumn.withFormatter('Callers (%)',
|
| - Utils.formatPercentNormalized),
|
| - new SortedTableColumn('Method')
|
| - ];
|
| - profileCallersTable = new NameSortedTable(columns);
|
| - profileCallersTable.sortColumnIndex = 0;
|
| - }
|
| -
|
| attached() {
|
| super.attached();
|
| - _updateTask.queue();
|
| - _resizeSubscription = window.onResize.listen((_) => _updateSize());
|
| - _updateSize();
|
| + _r.enable();
|
| + _request();
|
| }
|
|
|
| + @override
|
| detached() {
|
| super.detached();
|
| - if (_resizeSubscription != null) {
|
| - _resizeSubscription.cancel();
|
| - }
|
| - }
|
| -
|
| - _updateSize() {
|
| - HtmlElement e = $['main'];
|
| - final totalHeight = window.innerHeight;
|
| - final top = e.offset.top;
|
| - final bottomMargin = 32;
|
| - final mainHeight = totalHeight - top - bottomMargin;
|
| - e.style.setProperty('height', '${mainHeight}px');
|
| - }
|
| -
|
| - isolateChanged(oldValue) {
|
| - _updateTask.queue();
|
| - }
|
| -
|
| - update() async {
|
| - _clearView();
|
| - if (isolate == null) {
|
| - return;
|
| - }
|
| - final stream = _repository.get(isolate, M.SampleProfileTag.none);
|
| - var progress = (await stream.first).progress;
|
| - shadowRoot.querySelector('#sampleBufferControl').children = [
|
| - sampleBufferControlElement =
|
| - new SampleBufferControlElement(progress, stream, showTag: false,
|
| - selectedTag: M.SampleProfileTag.none, queue: app.queue)
|
| + _r.disable(notify: true);
|
| + children = [];
|
| + }
|
| +
|
| + void render() {
|
| + var content = [
|
| + new NavBarElement(queue: _r.queue)
|
| + ..children = [
|
| + new NavTopMenuElement(queue: _r.queue),
|
| + new NavVMMenuElement(_vm, _events, queue: _r.queue),
|
| + new NavIsolateMenuElement(_isolate, _events, queue: _r.queue),
|
| + new NavMenuElement('cpu profile (table)',
|
| + link: Uris.profiler(_isolate), last: true, queue: _r.queue),
|
| + new NavRefreshElement(queue: _r.queue)
|
| + ..onRefresh.listen(_refresh),
|
| + new NavRefreshElement(label: 'Clear', queue: _r.queue)
|
| + ..onRefresh.listen(_clearCpuProfile),
|
| + new NavNotifyElement(_notifications, queue: _r.queue)
|
| + ],
|
| ];
|
| - if (M.isSampleProcessRunning(progress.status)) {
|
| - progress = (await stream.last).progress;
|
| - }
|
| - if (progress.status == M.SampleProfileLoadingStatus.loaded) {
|
| - _profile = progress.profile;
|
| - shadowRoot.querySelector('#stackTraceTreeConfig').children = [
|
| - stackTraceTreeConfigElement =
|
| - new StackTraceTreeConfigElement(showMode: false,
|
| - showDirection: false, mode: ProfileTreeMode.function,
|
| - direction: M.ProfileTreeDirection.exclusive, queue: app.queue)
|
| - ..onModeChange.listen((e) {
|
| - cpuProfileTreeElement.mode = e.element.mode;
|
| - _renderTask.queue();
|
| - })
|
| - ..onDirectionChange.listen((e) {
|
| - cpuProfileTreeElement.direction = e.element.direction;
|
| - _renderTask.queue();
|
| - })
|
| - ..onFilterChange.listen((e) =>_renderTask.queue())
|
| - ];
|
| - shadowRoot.querySelector('#cpuProfileTree').children = [
|
| - cpuProfileTreeElement =
|
| - new CpuProfileVirtualTreeElement(isolate, _profile,
|
| - queue: app.queue)
|
| - ];
|
| - }
|
| - _renderTask.queue();
|
| - }
|
| -
|
| - Future clearCpuProfile() async {
|
| - await isolate.invokeRpc('_clearCpuProfile', { });
|
| - _updateTask.queue();
|
| - return new Future.value(null);
|
| - }
|
| -
|
| - Future refresh() {
|
| - _updateTask.queue();
|
| - return new Future.value(null);
|
| - }
|
| -
|
| - render() {
|
| - _updateView();
|
| - }
|
| -
|
| - checkParameters() {
|
| - if (isolate == null) {
|
| - return;
|
| - }
|
| - var functionId = app.locationManager.uri.queryParameters['functionId'];
|
| - var functionName =
|
| - app.locationManager.uri.queryParameters['functionName'];
|
| - if (functionId == '') {
|
| - // Fallback to searching by name.
|
| - _focusOnFunction(_findFunction(functionName));
|
| - } else {
|
| - if (functionId == null) {
|
| - _focusOnFunction(null);
|
| - return;
|
| - }
|
| - isolate.getObject(functionId).then((func) => _focusOnFunction(func));
|
| - }
|
| - }
|
| -
|
| - _clearView() {
|
| - profileTable.clearRows();
|
| - _renderTable();
|
| - }
|
| -
|
| - _updateView() {
|
| - _buildFunctionTable();
|
| - _renderTable();
|
| - _updateFunctionTreeView();
|
| - }
|
| -
|
| - int _findFunctionRow(ServiceFunction function) {
|
| - for (var i = 0; i < profileTable.sortedRows.length; i++) {
|
| - var rowIndex = profileTable.sortedRows[i];
|
| - var row = profileTable.rows[rowIndex];
|
| - if (row.values[NameSortedTable.FUNCTION_COLUMN] == function) {
|
| - return i;
|
| - }
|
| - }
|
| - return -1;
|
| - }
|
| -
|
| - _scrollToFunction(ServiceFunction function) {
|
| - TableSectionElement tableBody = $['profile-table'];
|
| - var row = _findFunctionRow(function);
|
| - if (row == -1) {
|
| + if (_progress == null) {
|
| + children = content;
|
| return;
|
| }
|
| - tableBody.children[row].classes.remove('shake');
|
| - // trigger reflow.
|
| - tableBody.children[row].offsetHeight;
|
| - tableBody.children[row].scrollIntoView(ScrollAlignment.CENTER);
|
| - tableBody.children[row].classes.add('shake');
|
| - // Focus on clicked function.
|
| - _focusOnFunction(function);
|
| - }
|
| -
|
| - _clearFocusedFunction() {
|
| - TableSectionElement tableBody = $['profile-table'];
|
| - // Clear current focus.
|
| - if (focusedRow != null) {
|
| - tableBody.children[focusedRow].classes.remove('focused');
|
| - }
|
| - focusedRow = null;
|
| - focusedFunction = null;
|
| - }
|
| -
|
| - ServiceFunction _findFunction(String functionName) {
|
| - for (var func in _profile.functions) {
|
| - if (func.function.name == functionName) {
|
| - return func.function;
|
| - }
|
| + content.add(new SampleBufferControlElement(_progress, _progressStream,
|
| + showTag: false, queue: _r.queue));
|
| + if (_progress.status == M.SampleProfileLoadingStatus.loaded) {
|
| + content.add(new BRElement());
|
| + content.addAll(_createTables());
|
| + content.add(new BRElement());
|
| + content.addAll(_createTree());
|
| }
|
| - return null;
|
| + children = content;
|
| }
|
|
|
| - _focusOnFunction(ServiceFunction function) {
|
| - if (focusedFunction == function) {
|
| - // Do nothing.
|
| - return;
|
| - }
|
| -
|
| - _clearFocusedFunction();
|
| + M.ProfileFunction _selected;
|
| + VirtualCollectionElement _functions;
|
| + VirtualCollectionElement _callers;
|
| + VirtualCollectionElement _callees;
|
|
|
| - if (function == null) {
|
| - _updateFunctionTreeView();
|
| - _clearCallTables();
|
| - return;
|
| - }
|
| -
|
| - var row = _findFunctionRow(function);
|
| - if (row == -1) {
|
| - _updateFunctionTreeView();
|
| - _clearCallTables();
|
| - return;
|
| - }
|
| -
|
| - focusedRow = row;
|
| - focusedFunction = function;
|
| -
|
| - TableSectionElement tableBody = $['profile-table'];
|
| - tableBody.children[focusedRow].classes.add('focused');
|
| - _updateFunctionTreeView();
|
| - _buildCallersTable(focusedFunction);
|
| - _buildCalleesTable(focusedFunction);
|
| - }
|
| -
|
| - _onRowClick(TableRowElement tr) {
|
| - var tableBody = $['profile-table'];
|
| - var row = profileTable.rowFromIndex(tableBody.children.indexOf(tr));
|
| - var function = row.values[NameSortedTable.FUNCTION_COLUMN];
|
| - app.locationManager.goReplacingParameters(
|
| - {
|
| - 'functionId': function.id,
|
| - 'functionName': function.vmName
|
| - }
|
| + List<Element> _createTables() {
|
| + _functions = _functions ?? new VirtualCollectionElement(
|
| + _createFunction,
|
| + _updateFunction,
|
| + createHeader: _createFunctionHeader,
|
| + queue: _r.queue
|
| );
|
| + _functions.items = _progress.profile.functions.toList()
|
| + ..sort(_createSorter(_Table.functions));
|
| + _functions.takeIntoView(_selected);
|
| + _callers = _callers ?? new VirtualCollectionElement(
|
| + _createCaller,
|
| + _updateCaller,
|
| + createHeader: _createCallerHeader,
|
| + queue: _r.queue
|
| + );
|
| + _callees = _callees ?? new VirtualCollectionElement(
|
| + _createCallee,
|
| + _updateCallee,
|
| + createHeader: _createCalleeHeader,
|
| + queue: _r.queue
|
| + );
|
| + if (_selected != null) {
|
| + _callers.items = _selected.callers.keys.toList()
|
| + ..sort(_createSorter(_Table.caller));
|
| + _callees.items = _selected.callees.keys.toList()
|
| + ..sort(_createSorter(_Table.callee));
|
| + } else {
|
| + _callers.items = const [];
|
| + _callees.items = const [];
|
| + }
|
| + return [
|
| + new DivElement()..classes = ['profile-trees']
|
| + ..children = [
|
| + new DivElement()..classes = ['profile-trees-all']
|
| + ..children = [_functions],
|
| + new DivElement()..classes = ['profile-trees-current']
|
| + ..children = [
|
| + new DivElement()..classes = ['profile-trees-caller']
|
| + ..children = [_callers],
|
| + new DivElement()..classes = ['profile-trees-selected']
|
| + ..children = _selected == null
|
| + ? [new SpanElement()..text = 'No element selected']
|
| + : [new FunctionRefElement(_isolate, _selected.function,
|
| + queue : _r.queue)],
|
| + new DivElement()..classes = ['profile-trees-callee']
|
| + ..children = [_callees]
|
| + ]
|
| + ]
|
| + ];
|
| }
|
|
|
| - _renderTable() {
|
| - profileTable._updateTableView($['profile-table'],
|
| - profileTable._makeFunctionRow,
|
| - _onRowClick,
|
| - NameSortedTable.FUNCTION_SPACER_COLUMNS,
|
| - NameSortedTable.FUNCTION_COLUMN);
|
| - }
|
| -
|
| - _buildFunctionTable() {
|
| - for (var func in _profile.functions) {
|
| - if ((func.exclusiveTicks == 0) && (func.inclusiveTicks == 0)) {
|
| - // Skip.
|
| - continue;
|
| - }
|
| - var row = [
|
| - func.normalizedExclusiveTicks,
|
| - func.normalizedInclusiveTicks,
|
| - func.function,
|
| - ];
|
| - profileTable.addRow(new SortedTableRow(row));
|
| - }
|
| - profileTable.sort();
|
| - }
|
| -
|
| - _renderCallTable(TableSectionElement view,
|
| - NameSortedTable model,
|
| - void onRowClick(TableRowElement tr)) {
|
| - model._updateTableView(view,
|
| - model._makeCallRow,
|
| - onRowClick,
|
| - NameSortedTable.CALL_SPACER_COLUMNS,
|
| - NameSortedTable.CALL_FUNCTION_COLUMN);
|
| - }
|
| -
|
| - _buildCallTable(Map<ProfileFunction, int> calls,
|
| - NameSortedTable model) {
|
| - model.clearRows();
|
| - if (calls == null) {
|
| - return;
|
| - }
|
| - var sum = 0;
|
| - calls.values.forEach((i) => sum += i);
|
| - calls.forEach((func, count) {
|
| - var row = [
|
| - count / sum,
|
| - func.function,
|
| + Element _createFunction() {
|
| + final element = new DivElement()
|
| + ..classes = const ['function-item']
|
| + ..children = [
|
| + new SpanElement()..classes = const ['exclusive']
|
| + ..text = '0%',
|
| + new SpanElement()..classes = const ['inclusive']
|
| + ..text = '0%',
|
| + new SpanElement()..classes = const ['name']
|
| ];
|
| - model.addRow(new SortedTableRow(row));
|
| + element.onClick.listen((_) {
|
| + _selected = _functions.getItemFromElement(element);
|
| + _r.dirty();
|
| });
|
| - model.sort();
|
| - }
|
| -
|
| - _clearCallTables() {
|
| - _buildCallersTable(null);
|
| - _buildCalleesTable(null);
|
| - }
|
| -
|
| - _onCallersClick(TableRowElement tr) {
|
| - var table = $['callers-table'];
|
| - final row = profileCallersTable.rowFromIndex(table.children.indexOf(tr));
|
| - var function = row.values[NameSortedTable.CALL_FUNCTION_COLUMN];
|
| - _scrollToFunction(function);
|
| - }
|
| -
|
| - _buildCallersTable(ServiceFunction function) {
|
| - var calls = (function != null) ? function.profile.callers : null;
|
| - var table = $['callers-table'];
|
| - _buildCallTable(calls, profileCallersTable);
|
| - _renderCallTable(table, profileCallersTable, _onCallersClick);
|
| - }
|
| -
|
| - _onCalleesClick(TableRowElement tr) {
|
| - var table = $['callees-table'];
|
| - final row = profileCalleesTable.rowFromIndex(table.children.indexOf(tr));
|
| - var function = row.values[NameSortedTable.CALL_FUNCTION_COLUMN];
|
| - _scrollToFunction(function);
|
| + return element;
|
| }
|
|
|
| - _buildCalleesTable(ServiceFunction function) {
|
| - var calls = (function != null) ? function.profile.callees : null;
|
| - var table = $['callees-table'];
|
| - _buildCallTable(calls, profileCalleesTable);
|
| - _renderCallTable(table, profileCalleesTable, _onCalleesClick);
|
| - }
|
| + void _updateFunction(Element e, M.ProfileFunction item, int index) {
|
| + if (item == _selected) {
|
| + e.classes = const ['function-item', 'selected'];
|
| + } else {
|
| + e.classes = const ['function-item'];
|
| + }
|
| + e.children[0].text = Utils.formatPercentNormalized(_getExclusiveT(item));
|
| + e.children[1].text = Utils.formatPercentNormalized(_getInclusiveT(item));
|
| + e.children[2] = new FunctionRefElement(_isolate, item.function,
|
| + queue: _r.queue)..classes = const ['name'];
|
| + }
|
| +
|
| + Element _createFunctionHeader() =>
|
| + new DivElement()
|
| + ..classes = const ['function-item']
|
| + ..children = [
|
| + _createHeaderButton(const ['exclusive'], 'Execution(%)',
|
| + _Table.functions,
|
| + _SortingField.exclusive,
|
| + _SortingDirection.descending),
|
| + _createHeaderButton(const ['inclusive'], 'Stack(%)',
|
| + _Table.functions,
|
| + _SortingField.inclusive,
|
| + _SortingDirection.descending),
|
| + _createHeaderButton(const ['name'], 'Method',
|
| + _Table.functions,
|
| + _SortingField.method,
|
| + _SortingDirection.descending),
|
| + ];
|
|
|
| - _changeSort(Element target, NameSortedTable table) {
|
| - if (target is TableCellElement) {
|
| - if (table.sortColumnIndex != target.cellIndex) {
|
| - table.sortColumnIndex = target.cellIndex;
|
| - table.sortDescending = true;
|
| + void _setSorting(_Table table,
|
| + _SortingField field,
|
| + _SortingDirection defaultDirection) {
|
| + if (_sortingField[table] == field) {
|
| + switch (_sortingDirection[table]) {
|
| + case _SortingDirection.descending:
|
| + _sortingDirection[table] = _SortingDirection.ascending;
|
| + break;
|
| + case _SortingDirection.ascending:
|
| + _sortingDirection[table] = _SortingDirection.descending;
|
| + break;
|
| + }
|
| } else {
|
| - table.sortDescending = !profileTable.sortDescending;
|
| + _sortingDirection[table] = defaultDirection;
|
| + _sortingField[table] = field;
|
| }
|
| - table.sort();
|
| + _r.dirty();
|
| }
|
| - }
|
|
|
| - changeSortProfile(Event e, var detail, Element target) {
|
| - _changeSort(target, profileTable);
|
| - _renderTable();
|
| - }
|
| -
|
| - changeSortCallers(Event e, var detail, Element target) {
|
| - _changeSort(target, profileCallersTable);
|
| - _renderCallTable($['callers-table'], profileCallersTable, _onCallersClick);
|
| + Element _createCallee() {
|
| + final element = new DivElement()
|
| + ..classes = const ['function-item']
|
| + ..children = [
|
| + new SpanElement()..classes = const ['inclusive']
|
| + ..text = '0%',
|
| + new SpanElement()..classes = const ['name']
|
| + ];
|
| + element.onClick.listen((_) {
|
| + _selected = _callees.getItemFromElement(element);
|
| + _r.dirty();
|
| + });
|
| + return element;
|
| }
|
|
|
| - changeSortCallees(Event e, var detail, Element target) {
|
| - _changeSort(target, profileCalleesTable);
|
| - _renderCallTable($['callees-table'], profileCalleesTable, _onCalleesClick);
|
| - }
|
| + void _updateCallee(Element e, item, int index) {
|
| + e.children[0].text = Utils.formatPercentNormalized(_getCalleeT(item));
|
| + e.children[1] = new FunctionRefElement(_isolate, item.function,
|
| + queue: _r.queue)..classes = const ['name'];
|
| + }
|
| +
|
| + Element _createCalleeHeader() =>
|
| + new DivElement()
|
| + ..classes = const ['function-item']
|
| + ..children = [
|
| + _createHeaderButton(const ['inclusive'], 'Callees(%)',
|
| + _Table.callee,
|
| + _SortingField.callee,
|
| + _SortingDirection.descending),
|
| + _createHeaderButton(const ['name'], 'Method',
|
| + _Table.callee,
|
| + _SortingField.method,
|
| + _SortingDirection.descending),
|
| + ];
|
|
|
| - //////
|
| - ///
|
| - /// Function tree.
|
| - ///
|
| - TableTree functionTree;
|
| - _updateFunctionTreeView() {
|
| - if (cpuProfileTreeElement == null) {
|
| - return;
|
| - }
|
| - cpuProfileTreeElement.filter = (FunctionCallTreeNode node) {
|
| - return node.profileFunction.function == focusedFunction;
|
| - };
|
| + Element _createCaller() {
|
| + final element = new DivElement()
|
| + ..classes = const ['function-item']
|
| + ..children = [
|
| + new SpanElement()..classes = const ['inclusive']
|
| + ..text = '0%',
|
| + new SpanElement()..classes = const ['name']
|
| + ];
|
| + element.onClick.listen((_) {
|
| + _selected = _callers.getItemFromElement(element);
|
| + _r.dirty();
|
| + });
|
| + return element;
|
| }
|
|
|
| - @published Isolate isolate;
|
| - @observable NameSortedTable profileTable;
|
| - @observable NameSortedTable profileCallersTable;
|
| - @observable NameSortedTable profileCalleesTable;
|
| - @observable ServiceFunction focusedFunction;
|
| - @observable int focusedRow;
|
| -
|
| + void _updateCaller(Element e, item, int index) {
|
| + e.children[0].text = Utils.formatPercentNormalized(_getCallerT(item));
|
| + e.children[1] = new FunctionRefElement(_isolate, item.function,
|
| + queue: _r.queue)..classes = const ['name'];
|
| + }
|
| +
|
| + Element _createCallerHeader() =>
|
| + new DivElement()
|
| + ..classes = const ['function-item']
|
| + ..children = [
|
| + _createHeaderButton(const ['inclusive'], 'Callers(%)',
|
| + _Table.caller,
|
| + _SortingField.caller,
|
| + _SortingDirection.descending),
|
| + _createHeaderButton(const ['name'], 'Method',
|
| + _Table.caller,
|
| + _SortingField.method,
|
| + _SortingDirection.descending),
|
| + ];
|
|
|
| - StreamSubscription _resizeSubscription;
|
| - Task _updateTask;
|
| - Task _renderTask;
|
| + ButtonElement _createHeaderButton(List<String> classes,
|
| + String text,
|
| + _Table table,
|
| + _SortingField field,
|
| + _SortingDirection direction) =>
|
| + new ButtonElement()..classes = classes
|
| + ..text = _sortingField[table] != field ? text :
|
| + _sortingDirection[table] == _SortingDirection.ascending
|
| + ? '$textâ–¼' : '$textâ–²'
|
| + ..onClick.listen((_) => _setSorting(table, field, direction));
|
| +
|
| + List<Element> _createTree() {
|
| + CpuProfileVirtualTreeElement tree;
|
| + return [
|
| + new StackTraceTreeConfigElement(showMode: false,
|
| + showDirection: false, mode: ProfileTreeMode.function,
|
| + direction: M.ProfileTreeDirection.exclusive, filter: _filter,
|
| + queue: _r.queue)
|
| + ..onFilterChange.listen((e) {
|
| + _filter = e.element.filter.trim();
|
| + tree.filters = _filter.isNotEmpty
|
| + ? [_filterTree, (node) { return node.name.contains(_filter); }]
|
| + : [_filterTree];
|
| + }),
|
| + new BRElement(),
|
| + tree = new CpuProfileVirtualTreeElement(_isolate, _progress.profile,
|
| + mode: ProfileTreeMode.function,
|
| + direction: M.ProfileTreeDirection.exclusive,
|
| + queue: _r.queue)
|
| + ..filters = _filter.isNotEmpty
|
| + ? [_filterTree, (node) { return node.name.contains(_filter); }]
|
| + : [_filterTree]
|
| + ];
|
| + }
|
|
|
| - IsolateSampleProfileRepository _repository =
|
| - new IsolateSampleProfileRepository();
|
| - CpuProfile _profile;
|
| - SampleBufferControlElement sampleBufferControlElement;
|
| - StackTraceTreeConfigElement stackTraceTreeConfigElement;
|
| - CpuProfileVirtualTreeElement cpuProfileTreeElement;
|
| + bool _filterTree(M.FunctionCallTreeNode node) =>
|
| + node.profileFunction == _selected;
|
| +
|
| + Future _request({bool clear: false, bool forceFetch: false}) async {
|
| + _progress = null;
|
| + _progressStream = _profiles.get(isolate, M.SampleProfileTag.none,
|
| + clear: clear, forceFetch: forceFetch);
|
| + _r.dirty();
|
| + _progress = (await _progressStream.first).progress;
|
| + _r.dirty();
|
| + if (M.isSampleProcessRunning(_progress.status)) {
|
| + _progress = (await _progressStream.last).progress;
|
| + _r.dirty();
|
| + }
|
| + }
|
| +
|
| + Future _clearCpuProfile(RefreshEvent e) async {
|
| + e.element.disabled = true;
|
| + await _request(clear: true);
|
| + e.element.disabled = false;
|
| + }
|
| +
|
| + Future _refresh(e) async {
|
| + e.element.disabled = true;
|
| + await _request(forceFetch: true);
|
| + e.element.disabled = false;
|
| + }
|
| +
|
| + _createSorter(_Table table) {
|
| + var getter;
|
| + switch (_sortingField[table]) {
|
| + case _SortingField.exclusive:
|
| + getter = _getExclusiveT;
|
| + break;
|
| + case _SortingField.inclusive:
|
| + getter = _getInclusiveT;
|
| + break;
|
| + case _SortingField.callee:
|
| + getter = _getCalleeT;
|
| + break;
|
| + case _SortingField.caller:
|
| + getter = _getCallerT;
|
| + break;
|
| + case _SortingField.method:
|
| + getter = (M.ProfileFunction s) =>
|
| + M.getFunctionFullName(s.function);
|
| + break;
|
| + }
|
| + switch (_sortingDirection[table]) {
|
| + case _SortingDirection.ascending:
|
| + return (a, b) => getter(a).compareTo(getter(b));
|
| + case _SortingDirection.descending:
|
| + return (a, b) => getter(b).compareTo(getter(a));
|
| + }
|
| + }
|
| +
|
| + static double _getExclusiveT(M.ProfileFunction f) =>
|
| + f.normalizedExclusiveTicks;
|
| + static double _getInclusiveT(M.ProfileFunction f) =>
|
| + f.normalizedExclusiveTicks;
|
| + double _getCalleeT(M.ProfileFunction f) =>
|
| + _selected.callees[f] / _selected.callees.values.reduce((a, b) => a + b);
|
| + double _getCallerT(M.ProfileFunction f) =>
|
| + _selected.callers[f] / _selected.callers.values.reduce((a, b) => a + b);
|
| }
|
|
|