| Index: runtime/observatory/lib/src/elements/cpu_profile/virtual_tree.dart
|
| diff --git a/runtime/observatory/lib/src/elements/cpu_profile/virtual_tree.dart b/runtime/observatory/lib/src/elements/cpu_profile/virtual_tree.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..236b3952228b07075f5123fb116545c734754fcc
|
| --- /dev/null
|
| +++ b/runtime/observatory/lib/src/elements/cpu_profile/virtual_tree.dart
|
| @@ -0,0 +1,179 @@
|
| +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +import 'dart:async';
|
| +import 'dart:html';
|
| +import 'dart:math' as Math;
|
| +import 'package:observatory/models.dart' as M;
|
| +import 'package:observatory/src/elements/stack_trace_tree_config.dart'
|
| + show ProfileTreeMode;
|
| +import 'package:observatory/src/elements/code_ref.dart';
|
| +import 'package:observatory/src/elements/containers/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/utils.dart';
|
| +
|
| +export 'package:observatory/src/elements/stack_trace_tree_config.dart'
|
| + show ProfileTreeMode;
|
| +
|
| +class CpuProfileVirtualTreeElement extends HtmlElement implements Renderable {
|
| + static const tag =
|
| + const Tag<CpuProfileVirtualTreeElement>('cpu-profile-virtual-tree');
|
| +
|
| + RenderingScheduler<CpuProfileVirtualTreeElement> _r;
|
| +
|
| + Stream<RenderedEvent<CpuProfileVirtualTreeElement>> get onRendered =>
|
| + _r.onRendered;
|
| +
|
| + M.ProfileTreeDirection _direction;
|
| + ProfileTreeMode _mode;
|
| + M.IsolateRef _isolate;
|
| + M.SampleProfile _profile;
|
| + M.CallTreeNodeFilter _filter;
|
| +
|
| + M.ProfileTreeDirection get direction => _direction;
|
| + ProfileTreeMode get mode => _mode;
|
| + M.IsolateRef get isolate => _isolate;
|
| + M.SampleProfile get profile => _profile;
|
| + M.CallTreeNodeFilter get filter => _filter;
|
| +
|
| + set direction(M.ProfileTreeDirection value) =>
|
| + _direction = _r.checkAndReact(_direction, value);
|
| + set mode(ProfileTreeMode value) => _mode = _r.checkAndReact(_mode, value);
|
| + set filter(M.CallTreeNodeFilter value) =>
|
| + _filter = _r.checkAndReact(_filter, value);
|
| +
|
| + factory CpuProfileVirtualTreeElement(M.IsolateRef isolate,
|
| + M.SampleProfile profile, {ProfileTreeMode mode: ProfileTreeMode.function,
|
| + M.ProfileTreeDirection direction: M.ProfileTreeDirection.exclusive,
|
| + RenderingQueue queue}) {
|
| + assert(isolate != null);
|
| + assert(profile != null);
|
| + assert(mode != null);
|
| + assert(direction != null);
|
| + CpuProfileVirtualTreeElement e = document.createElement(tag.name);
|
| + e._r = new RenderingScheduler(e, queue: queue);
|
| + e._isolate = isolate;
|
| + e._profile = profile;
|
| + e._mode = mode;
|
| + e._direction = direction;
|
| + return e;
|
| + }
|
| +
|
| + CpuProfileVirtualTreeElement.created() : super.created();
|
| +
|
| + @override
|
| + attached() {
|
| + super.attached();
|
| + _r.enable();
|
| + }
|
| +
|
| + @override
|
| + detached() {
|
| + super.detached();
|
| + _r.disable(notify: true);
|
| + children = [];
|
| + }
|
| +
|
| + VirtualTreeElement _tree;
|
| +
|
| + void render() {
|
| + var tree;
|
| + var update;
|
| + switch (mode) {
|
| + case ProfileTreeMode.code:
|
| + tree = _profile.loadCodeTree(_direction);
|
| + update = _updateCodeRow;
|
| + break;
|
| + case ProfileTreeMode.function:
|
| + tree = _profile.loadFunctionTree(_direction);
|
| + update = _updateFunctionRow;
|
| + break;
|
| + default:
|
| + throw new Exception('Unknown ProfileTreeMode: $mode');
|
| + }
|
| + if (tree == null) {
|
| + children = [
|
| + new HeadingElement.h1()..text = 'No Results'
|
| + ];
|
| + return;
|
| + }
|
| + if (filter != null) {
|
| + tree = tree.filtered(filter);
|
| + }
|
| + _tree = new VirtualTreeElement(_createRow, update, _getChildren,
|
| + items: tree.root.children, queue: _r.queue);
|
| + if (tree.root.children.length == 1) {
|
| + _tree.expand(tree.root.children.first, autoExpandSingleChildNodes: true);
|
| + }
|
| + children = [_tree];
|
| + }
|
| +
|
| + static Element _createRow(toggle) {
|
| + return new DivElement()
|
| + ..classes = const ['tree-item']
|
| + ..children = [
|
| + new SpanElement()..classes = const ['inclusive']
|
| + ..title = 'global % on stack',
|
| + new SpanElement()..classes = const ['exclusive']
|
| + ..title = 'global % executing',
|
| + new SpanElement()..classes = const ['lines'],
|
| + new ButtonElement()..classes = const ['expander']
|
| + ..onClick.listen((_) => toggle(autoToggleSingleChildNodes: true)),
|
| + new SpanElement()..classes = const ['percentage']
|
| + ..title = 'tree node %',
|
| + new SpanElement()..classes = const ['name']
|
| + ];
|
| + }
|
| +
|
| + static _getChildren(M.CallTreeNode node) => node.children;
|
| +
|
| + void _updateFunctionRow(HtmlElement element, M.FunctionCallTreeNode item,
|
| + int depth) {
|
| + element.children[0].text = Utils.formatPercentNormalized(
|
| + item.profileFunction.normalizedInclusiveTicks);
|
| + element.children[1].text = Utils.formatPercentNormalized(
|
| + item.profileFunction.normalizedExclusiveTicks);
|
| + _updateLines(element.children[2].children, depth);
|
| + if (item.children.isNotEmpty) {
|
| + element.children[3].text = _tree.isExpanded(item) ? '▼' : '►';
|
| + } else {
|
| + element.children[3].text = '';
|
| + }
|
| + element.children[4].text = Utils.formatPercentNormalized(
|
| + item.percentage);
|
| + element.children[5] = new FunctionRefElement(_isolate,
|
| + item.profileFunction.function, queue: _r.queue)
|
| + ..classes = const ['name'];
|
| + }
|
| +
|
| + void _updateCodeRow(HtmlElement element, M.CodeCallTreeNode item, int depth) {
|
| + element.children[0].text = Utils.formatPercentNormalized(
|
| + item.profileCode.normalizedInclusiveTicks);
|
| + element.children[1].text = Utils.formatPercentNormalized(
|
| + item.profileCode.normalizedExclusiveTicks);
|
| + _updateLines(element.children[2].children, depth);
|
| + if (item.children.isNotEmpty) {
|
| + element.children[3].text = _tree.isExpanded(item) ? '▼' : '►';
|
| + } else {
|
| + element.children[3].text = '';
|
| + }
|
| + element.children[4].text = Utils.formatPercentNormalized(
|
| + item.percentage);
|
| + element.children[5] = new CodeRefElement(_isolate,
|
| + item.profileCode.code, queue: _r.queue)
|
| + ..classes = const ['name'];
|
| + }
|
| +
|
| + static _updateLines(List<Element> lines, int n) {
|
| + n = Math.max(0, n);
|
| + while (lines.length > n) {
|
| + lines.removeLast();
|
| + }
|
| + while (lines.length < n) {
|
| + lines.add(new SpanElement());
|
| + }
|
| + }
|
| +}
|
|
|