| Index: runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
|
| diff --git a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart b/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
|
| deleted file mode 100644
|
| index 76e1a2680f6bdbbaad8e09abe52ad3703e5bfea0..0000000000000000000000000000000000000000
|
| --- a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
|
| +++ /dev/null
|
| @@ -1,1090 +0,0 @@
|
| -// Copyright (c) 2015, 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.
|
| -
|
| -part of cpu_profiler;
|
| -
|
| -abstract class CallTreeNode<NodeT extends M.CallTreeNode>
|
| - implements M.CallTreeNode {
|
| - final List<NodeT> children;
|
| - final int count;
|
| - final int inclusiveNativeAllocations;
|
| - final int exclusiveNativeAllocations;
|
| - double get percentage => _percentage;
|
| - double _percentage = 0.0;
|
| - final Set<String> attributes = new Set<String>();
|
| -
|
| - // Either a ProfileCode or a ProfileFunction.
|
| - Object get profileData;
|
| - String get name;
|
| -
|
| - CallTreeNode(this.children, this.count, this.inclusiveNativeAllocations,
|
| - this.exclusiveNativeAllocations);
|
| -}
|
| -
|
| -class CodeCallTreeNode extends CallTreeNode<CodeCallTreeNode>
|
| - implements M.CodeCallTreeNode {
|
| - final ProfileCode profileCode;
|
| -
|
| - Object get profileData => profileCode;
|
| -
|
| - String get name => profileCode.code.name;
|
| -
|
| - final Set<String> attributes = new Set<String>();
|
| - CodeCallTreeNode(this.profileCode, int count, int inclusiveNativeAllocations,
|
| - int exclusiveNativeAllocations)
|
| - : super(new List<CodeCallTreeNode>(), count, inclusiveNativeAllocations,
|
| - exclusiveNativeAllocations) {
|
| - attributes.addAll(profileCode.attributes);
|
| - }
|
| -}
|
| -
|
| -class CallTree<NodeT extends CallTreeNode> {
|
| - final bool inclusive;
|
| - final NodeT root;
|
| -
|
| - CallTree(this.inclusive, this.root);
|
| -}
|
| -
|
| -class CodeCallTree extends CallTree<CodeCallTreeNode>
|
| - implements M.CodeCallTree {
|
| - CodeCallTree(bool inclusive, CodeCallTreeNode root) : super(inclusive, root) {
|
| - if ((root.inclusiveNativeAllocations != null) &&
|
| - (root.inclusiveNativeAllocations != 0)) {
|
| - _setCodeMemoryPercentage(null, root);
|
| - } else {
|
| - _setCodePercentage(null, root);
|
| - }
|
| - }
|
| -
|
| - CodeCallTree filtered(CallTreeNodeFilter filter) {
|
| - var treeFilter = new _FilteredCodeCallTreeBuilder(filter, this);
|
| - treeFilter.build();
|
| - if ((treeFilter.filtered.root.inclusiveNativeAllocations != null) &&
|
| - (treeFilter.filtered.root.inclusiveNativeAllocations != 0)) {
|
| - _setCodeMemoryPercentage(null, treeFilter.filtered.root);
|
| - } else {
|
| - _setCodePercentage(null, treeFilter.filtered.root);
|
| - }
|
| - return treeFilter.filtered;
|
| - }
|
| -
|
| - _setCodePercentage(CodeCallTreeNode parent, CodeCallTreeNode node) {
|
| - assert(node != null);
|
| - var parentPercentage = 1.0;
|
| - var parentCount = node.count;
|
| - if (parent != null) {
|
| - parentPercentage = parent._percentage;
|
| - parentCount = parent.count;
|
| - }
|
| - if (inclusive) {
|
| - node._percentage = parentPercentage * (node.count / parentCount);
|
| - } else {
|
| - node._percentage = (node.count / parentCount);
|
| - }
|
| - for (var child in node.children) {
|
| - _setCodePercentage(node, child);
|
| - }
|
| - }
|
| -
|
| - _setCodeMemoryPercentage(CodeCallTreeNode parent, CodeCallTreeNode node) {
|
| - assert(node != null);
|
| - var parentPercentage = 1.0;
|
| - var parentMemory = node.inclusiveNativeAllocations;
|
| - if (parent != null) {
|
| - parentPercentage = parent._percentage;
|
| - parentMemory = parent.inclusiveNativeAllocations;
|
| - }
|
| - if (inclusive) {
|
| - node._percentage =
|
| - parentPercentage * (node.inclusiveNativeAllocations / parentMemory);
|
| - } else {
|
| - node._percentage = (node.inclusiveNativeAllocations / parentMemory);
|
| - }
|
| - for (var child in node.children) {
|
| - _setCodeMemoryPercentage(node, child);
|
| - }
|
| - node.children.sort((a, b) {
|
| - return b.inclusiveNativeAllocations - a.inclusiveNativeAllocations;
|
| - });
|
| - }
|
| -
|
| - _recordCallerAndCalleesInner(
|
| - CodeCallTreeNode caller, CodeCallTreeNode callee) {
|
| - if (caller != null) {
|
| - caller.profileCode._recordCallee(callee.profileCode, callee.count);
|
| - callee.profileCode._recordCaller(caller.profileCode, caller.count);
|
| - }
|
| -
|
| - for (var child in callee.children) {
|
| - _recordCallerAndCalleesInner(callee, child);
|
| - }
|
| - }
|
| -
|
| - _recordCallerAndCallees() {
|
| - for (var child in root.children) {
|
| - _recordCallerAndCalleesInner(null, child);
|
| - }
|
| - }
|
| -}
|
| -
|
| -class FunctionCallTreeNodeCode {
|
| - final ProfileCode code;
|
| - final int ticks;
|
| - FunctionCallTreeNodeCode(this.code, this.ticks);
|
| -}
|
| -
|
| -class FunctionCallTreeNode extends CallTreeNode {
|
| - final ProfileFunction profileFunction;
|
| - final codes = new List<FunctionCallTreeNodeCode>();
|
| - int _totalCodeTicks = 0;
|
| - int get totalCodesTicks => _totalCodeTicks;
|
| -
|
| - String get name => M.getFunctionFullName(profileFunction.function);
|
| - Object get profileData => profileFunction;
|
| -
|
| - FunctionCallTreeNode(this.profileFunction, int count,
|
| - inclusiveNativeAllocations, exclusiveNativeAllocations)
|
| - : super(new List<FunctionCallTreeNode>(), count,
|
| - inclusiveNativeAllocations, exclusiveNativeAllocations) {
|
| - profileFunction._addKindBasedAttributes(attributes);
|
| - }
|
| -
|
| - // Does this function have an optimized version of itself?
|
| - bool hasOptimizedCode() {
|
| - for (var nodeCode in codes) {
|
| - var profileCode = nodeCode.code;
|
| - if (!profileCode.code.isDartCode) {
|
| - continue;
|
| - }
|
| - if (profileCode.code.function != profileFunction.function) {
|
| - continue;
|
| - }
|
| - if (profileCode.code.isOptimized) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - // Does this function have an unoptimized version of itself?
|
| - bool hasUnoptimizedCode() {
|
| - for (var nodeCode in codes) {
|
| - var profileCode = nodeCode.code;
|
| - if (!profileCode.code.isDartCode) {
|
| - continue;
|
| - }
|
| - if (profileCode.code.kind == M.CodeKind.stub) {
|
| - continue;
|
| - }
|
| - if (!profileCode.code.isOptimized) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - // Has this function been inlined in another function?
|
| - bool isInlined() {
|
| - for (var nodeCode in codes) {
|
| - var profileCode = nodeCode.code;
|
| - if (!profileCode.code.isDartCode) {
|
| - continue;
|
| - }
|
| - if (profileCode.code.kind == M.CodeKind.stub) {
|
| - continue;
|
| - }
|
| - // If the code's function isn't this function.
|
| - if (profileCode.code.function != profileFunction.function) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - setCodeAttributes() {}
|
| -}
|
| -
|
| -/// Predicate filter function. Returns true if path from root to [node] and all
|
| -/// of [node]'s children should be added to the filtered tree.
|
| -typedef bool CallTreeNodeFilter(CallTreeNode node);
|
| -
|
| -/// Build a filter version of a FunctionCallTree.
|
| -abstract class _FilteredCallTreeBuilder {
|
| - /// The filter.
|
| - final CallTreeNodeFilter filter;
|
| -
|
| - /// The unfiltered tree.
|
| - final CallTree _unfilteredTree;
|
| -
|
| - /// The filtered tree (construct by [build]).
|
| - final CallTree filtered;
|
| - final List _currentPath = [];
|
| -
|
| - /// Construct a filtered tree builder using [filter] and [tree].
|
| - _FilteredCallTreeBuilder(this.filter, CallTree tree, this.filtered)
|
| - : _unfilteredTree = tree;
|
| -
|
| - /// Build the filtered tree.
|
| - build() {
|
| - assert(filtered != null);
|
| - assert(filter != null);
|
| - assert(_unfilteredTree != null);
|
| - _descend(_unfilteredTree.root);
|
| - }
|
| -
|
| - CallTreeNode _findInChildren(CallTreeNode current, CallTreeNode needle) {
|
| - for (var child in current.children) {
|
| - if (child.profileData == needle.profileData) {
|
| - return child;
|
| - }
|
| - }
|
| - return null;
|
| - }
|
| -
|
| - CallTreeNode _copyNode(CallTreeNode node);
|
| -
|
| - /// Add all nodes in [_currentPath].
|
| - FunctionCallTreeNode _addCurrentPath() {
|
| - FunctionCallTreeNode current = filtered.root;
|
| - // Tree root is always the first element of the current path.
|
| - assert(_unfilteredTree.root == _currentPath[0]);
|
| - // Assert that unfiltered tree's root and filtered tree's root are different.
|
| - assert(_unfilteredTree.root != current);
|
| - for (var i = 1; i < _currentPath.length; i++) {
|
| - // toAdd is from the unfiltered tree.
|
| - var toAdd = _currentPath[i];
|
| - // See if we already have a node for toAdd in the filtered tree.
|
| - var child = _findInChildren(current, toAdd);
|
| - if (child == null) {
|
| - // New node.
|
| - child = _copyNode(toAdd);
|
| - current.children.add(child);
|
| - }
|
| - current = child;
|
| - assert(current.count == toAdd.count);
|
| - }
|
| - return current;
|
| - }
|
| -
|
| - /// Starting at [current] append [next] and all of [next]'s sub-trees
|
| - _appendTree(CallTreeNode current, CallTreeNode next) {
|
| - if (next == null) {
|
| - return;
|
| - }
|
| - var child = _findInChildren(current, next);
|
| - if (child == null) {
|
| - child = _copyNode(next);
|
| - current.children.add(child);
|
| - }
|
| - current = child;
|
| - for (var nextChild in next.children) {
|
| - _appendTree(current, nextChild);
|
| - }
|
| - }
|
| -
|
| - /// Add path from root to [child], [child], and all of [child]'s sub-trees
|
| - /// to filtered tree.
|
| - _addTree(CallTreeNode child) {
|
| - var current = _addCurrentPath();
|
| - _appendTree(current, child);
|
| - }
|
| -
|
| - /// Descend further into the tree. [current] is from the unfiltered tree.
|
| - _descend(CallTreeNode current) {
|
| - if (current == null) {
|
| - return;
|
| - }
|
| - _currentPath.add(current);
|
| -
|
| - if (filter(current)) {
|
| - // Filter matched.
|
| - if (current.children.length == 0) {
|
| - // Have no children. Add this path.
|
| - _addTree(null);
|
| - } else {
|
| - // Add all child trees.
|
| - for (var child in current.children) {
|
| - _addTree(child);
|
| - }
|
| - }
|
| - } else {
|
| - // Did not match, descend to each child.
|
| - for (var child in current.children) {
|
| - _descend(child);
|
| - }
|
| - }
|
| -
|
| - var last = _currentPath.removeLast();
|
| - assert(current == last);
|
| - }
|
| -}
|
| -
|
| -class _FilteredFunctionCallTreeBuilder extends _FilteredCallTreeBuilder {
|
| - _FilteredFunctionCallTreeBuilder(
|
| - CallTreeNodeFilter filter, FunctionCallTree tree)
|
| - : super(
|
| - filter,
|
| - tree,
|
| - new FunctionCallTree(
|
| - tree.inclusive,
|
| - new FunctionCallTreeNode(
|
| - tree.root.profileData,
|
| - tree.root.count,
|
| - tree.root.inclusiveNativeAllocations,
|
| - tree.root.exclusiveNativeAllocations)));
|
| -
|
| - _copyNode(FunctionCallTreeNode node) {
|
| - return new FunctionCallTreeNode(node.profileData, node.count,
|
| - node.inclusiveNativeAllocations, node.exclusiveNativeAllocations);
|
| - }
|
| -}
|
| -
|
| -class _FilteredCodeCallTreeBuilder extends _FilteredCallTreeBuilder {
|
| - _FilteredCodeCallTreeBuilder(CallTreeNodeFilter filter, CodeCallTree tree)
|
| - : super(
|
| - filter,
|
| - tree,
|
| - new CodeCallTree(
|
| - tree.inclusive,
|
| - new CodeCallTreeNode(
|
| - tree.root.profileData,
|
| - tree.root.count,
|
| - tree.root.inclusiveNativeAllocations,
|
| - tree.root.exclusiveNativeAllocations)));
|
| -
|
| - _copyNode(CodeCallTreeNode node) {
|
| - return new CodeCallTreeNode(node.profileData, node.count,
|
| - node.inclusiveNativeAllocations, node.exclusiveNativeAllocations);
|
| - }
|
| -}
|
| -
|
| -class FunctionCallTree extends CallTree implements M.FunctionCallTree {
|
| - FunctionCallTree(bool inclusive, FunctionCallTreeNode root)
|
| - : super(inclusive, root) {
|
| - if ((root.inclusiveNativeAllocations != null) &&
|
| - (root.inclusiveNativeAllocations != 0)) {
|
| - _setFunctionMemoryPercentage(null, root);
|
| - } else {
|
| - _setFunctionPercentage(null, root);
|
| - }
|
| - }
|
| -
|
| - FunctionCallTree filtered(CallTreeNodeFilter filter) {
|
| - var treeFilter = new _FilteredFunctionCallTreeBuilder(filter, this);
|
| - treeFilter.build();
|
| - if ((treeFilter.filtered.root.inclusiveNativeAllocations != null) &&
|
| - (treeFilter.filtered.root.inclusiveNativeAllocations != 0)) {
|
| - _setFunctionMemoryPercentage(null, treeFilter.filtered.root);
|
| - } else {
|
| - _setFunctionPercentage(null, treeFilter.filtered.root);
|
| - }
|
| - return treeFilter.filtered;
|
| - }
|
| -
|
| - void _setFunctionPercentage(
|
| - FunctionCallTreeNode parent, FunctionCallTreeNode node) {
|
| - assert(node != null);
|
| - var parentPercentage = 1.0;
|
| - var parentCount = node.count;
|
| - if (parent != null) {
|
| - parentPercentage = parent._percentage;
|
| - parentCount = parent.count;
|
| - }
|
| - if (inclusive) {
|
| - node._percentage = parentPercentage * (node.count / parentCount);
|
| - } else {
|
| - node._percentage = (node.count / parentCount);
|
| - }
|
| - for (var child in node.children) {
|
| - _setFunctionPercentage(node, child);
|
| - }
|
| - }
|
| -
|
| - void _setFunctionMemoryPercentage(
|
| - FunctionCallTreeNode parent, FunctionCallTreeNode node) {
|
| - assert(node != null);
|
| - var parentPercentage = 1.0;
|
| - var parentMemory = node.inclusiveNativeAllocations;
|
| - if (parent != null) {
|
| - parentPercentage = parent._percentage;
|
| - parentMemory = parent.inclusiveNativeAllocations;
|
| - }
|
| - if (inclusive) {
|
| - node._percentage =
|
| - parentPercentage * (node.inclusiveNativeAllocations / parentMemory);
|
| - } else {
|
| - node._percentage = (node.inclusiveNativeAllocations / parentMemory);
|
| - }
|
| - for (var child in node.children) {
|
| - _setFunctionMemoryPercentage(node, child);
|
| - }
|
| - node.children.sort((a, b) {
|
| - return b.inclusiveNativeAllocations - a.inclusiveNativeAllocations;
|
| - });
|
| - }
|
| -
|
| - _markFunctionCallsInner(
|
| - FunctionCallTreeNode caller, FunctionCallTreeNode callee) {
|
| - if (caller != null) {
|
| - caller.profileFunction
|
| - ._recordCallee(callee.profileFunction, callee.count);
|
| - callee.profileFunction
|
| - ._recordCaller(caller.profileFunction, caller.count);
|
| - }
|
| - for (var child in callee.children) {
|
| - _markFunctionCallsInner(callee, child);
|
| - }
|
| - }
|
| -
|
| - _markFunctionCalls() {
|
| - for (var child in root.children) {
|
| - _markFunctionCallsInner(null, child);
|
| - }
|
| - }
|
| -}
|
| -
|
| -class CodeTick {
|
| - final int exclusiveTicks;
|
| - final int inclusiveTicks;
|
| - CodeTick(this.exclusiveTicks, this.inclusiveTicks);
|
| -}
|
| -
|
| -class InlineIntervalTick {
|
| - final int startAddress;
|
| - int _inclusiveTicks = 0;
|
| - int get inclusiveTicks => _inclusiveTicks;
|
| - int _exclusiveTicks = 0;
|
| - int get exclusiveTicks => _exclusiveTicks;
|
| - InlineIntervalTick(this.startAddress);
|
| -}
|
| -
|
| -class ProfileCode implements M.ProfileCode {
|
| - final CpuProfile profile;
|
| - final Code code;
|
| - int exclusiveTicks;
|
| - int inclusiveTicks;
|
| - int exclusiveNativeAllocations;
|
| - int inclusiveNativeAllocations;
|
| - double normalizedExclusiveTicks = 0.0;
|
| - double normalizedInclusiveTicks = 0.0;
|
| - final addressTicks = new Map<int, CodeTick>();
|
| - final intervalTicks = new Map<int, InlineIntervalTick>();
|
| - String formattedInclusiveTicks = '';
|
| - String formattedExclusiveTicks = '';
|
| - String formattedExclusivePercent = '';
|
| - String formattedCpuTime = '';
|
| - String formattedOnStackTime = '';
|
| - final Set<String> attributes = new Set<String>();
|
| - final Map<ProfileCode, int> callers = new Map<ProfileCode, int>();
|
| - final Map<ProfileCode, int> callees = new Map<ProfileCode, int>();
|
| -
|
| - void _processTicks(List<dynamic> profileTicks) {
|
| - assert(profileTicks != null);
|
| - assert((profileTicks.length % 3) == 0);
|
| - for (var i = 0; i < profileTicks.length; i += 3) {
|
| - // TODO(observatory): Address is not necessarily representable as a JS
|
| - // integer.
|
| - var address = int.parse(profileTicks[i] as String, radix: 16);
|
| - var exclusive = profileTicks[i + 1] as int;
|
| - var inclusive = profileTicks[i + 2] as int;
|
| - var tick = new CodeTick(exclusive, inclusive);
|
| - addressTicks[address] = tick;
|
| -
|
| - var interval = code.findInterval(address);
|
| - if (interval != null) {
|
| - var intervalTick = intervalTicks[interval.start];
|
| - if (intervalTick == null) {
|
| - // Insert into map.
|
| - intervalTick = new InlineIntervalTick(interval.start);
|
| - intervalTicks[interval.start] = intervalTick;
|
| - }
|
| - intervalTick._inclusiveTicks += inclusive;
|
| - intervalTick._exclusiveTicks += exclusive;
|
| - }
|
| - }
|
| - }
|
| -
|
| - ProfileCode.fromMap(this.profile, this.code, Map data) {
|
| - assert(profile != null);
|
| - assert(code != null);
|
| -
|
| - code.profile = this;
|
| -
|
| - if (code.kind == M.CodeKind.stub) {
|
| - attributes.add('stub');
|
| - } else if (code.kind == M.CodeKind.dart) {
|
| - if (code.isNative) {
|
| - attributes.add('ffi'); // Not to be confused with a C function.
|
| - } else {
|
| - attributes.add('dart');
|
| - }
|
| - if (code.hasIntrinsic) {
|
| - attributes.add('intrinsic');
|
| - }
|
| - if (code.isOptimized) {
|
| - attributes.add('optimized');
|
| - } else {
|
| - attributes.add('unoptimized');
|
| - }
|
| - } else if (code.kind == M.CodeKind.tag) {
|
| - attributes.add('tag');
|
| - } else if (code.kind == M.CodeKind.native) {
|
| - attributes.add('native');
|
| - }
|
| - inclusiveTicks = data['inclusiveTicks'];
|
| - exclusiveTicks = data['exclusiveTicks'];
|
| -
|
| - normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
|
| -
|
| - normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
|
| -
|
| - var ticks = data['ticks'];
|
| - if (ticks != null) {
|
| - _processTicks(ticks);
|
| - }
|
| -
|
| - if (data.containsKey('exclusiveNativeAllocations') &&
|
| - data.containsKey('inclusiveNativeAllocations')) {
|
| - exclusiveNativeAllocations =
|
| - int.parse(data['exclusiveNativeAllocations']);
|
| - inclusiveNativeAllocations =
|
| - int.parse(data['inclusiveNativeAllocations']);
|
| - }
|
| -
|
| - formattedExclusivePercent =
|
| - Utils.formatPercent(exclusiveTicks, profile.sampleCount);
|
| -
|
| - formattedCpuTime = Utils.formatTimeMilliseconds(
|
| - profile.approximateMillisecondsForCount(exclusiveTicks));
|
| -
|
| - formattedOnStackTime = Utils.formatTimeMilliseconds(
|
| - profile.approximateMillisecondsForCount(inclusiveTicks));
|
| -
|
| - formattedInclusiveTicks =
|
| - '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
|
| - '($inclusiveTicks)';
|
| -
|
| - formattedExclusiveTicks =
|
| - '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
|
| - '($exclusiveTicks)';
|
| - }
|
| -
|
| - _recordCaller(ProfileCode caller, int count) {
|
| - var r = callers[caller];
|
| - if (r == null) {
|
| - r = 0;
|
| - }
|
| - callers[caller] = r + count;
|
| - }
|
| -
|
| - _recordCallee(ProfileCode callee, int count) {
|
| - var r = callees[callee];
|
| - if (r == null) {
|
| - r = 0;
|
| - }
|
| - callees[callee] = r + count;
|
| - }
|
| -}
|
| -
|
| -class ProfileFunction implements M.ProfileFunction {
|
| - final CpuProfile profile;
|
| - final ServiceFunction function;
|
| - // List of compiled code objects containing this function.
|
| - final List<ProfileCode> profileCodes = new List<ProfileCode>();
|
| - final Map<ProfileFunction, int> callers = new Map<ProfileFunction, int>();
|
| - final Map<ProfileFunction, int> callees = new Map<ProfileFunction, int>();
|
| -
|
| - // Absolute ticks:
|
| - int exclusiveTicks = 0;
|
| - int inclusiveTicks = 0;
|
| -
|
| - // Global percentages:
|
| - double normalizedExclusiveTicks = 0.0;
|
| - double normalizedInclusiveTicks = 0.0;
|
| -
|
| - // Native allocations:
|
| - int exclusiveNativeAllocations = 0;
|
| - int inclusiveNativeAllocations = 0;
|
| -
|
| - String formattedInclusiveTicks = '';
|
| - String formattedExclusiveTicks = '';
|
| - String formattedExclusivePercent = '';
|
| - String formattedCpuTime = '';
|
| - String formattedOnStackTime = '';
|
| - final Set<String> attributes = new Set<String>();
|
| -
|
| - int _sortCodes(ProfileCode a, ProfileCode b) {
|
| - if (a.code.isOptimized == b.code.isOptimized) {
|
| - return b.code.profile.exclusiveTicks - a.code.profile.exclusiveTicks;
|
| - }
|
| - if (a.code.isOptimized) {
|
| - return -1;
|
| - }
|
| - return 1;
|
| - }
|
| -
|
| - // Does this function have an optimized version of itself?
|
| - bool hasOptimizedCode() {
|
| - for (var profileCode in profileCodes) {
|
| - if (profileCode.code.function != function) {
|
| - continue;
|
| - }
|
| - if (profileCode.code.isOptimized) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - // Does this function have an unoptimized version of itself?
|
| - bool hasUnoptimizedCode() {
|
| - for (var profileCode in profileCodes) {
|
| - if (profileCode.code.kind == M.CodeKind.stub) {
|
| - continue;
|
| - }
|
| - if (!profileCode.code.isDartCode) {
|
| - continue;
|
| - }
|
| - if (!profileCode.code.isOptimized) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - // Has this function been inlined in another function?
|
| - bool isInlined() {
|
| - for (var profileCode in profileCodes) {
|
| - if (profileCode.code.kind == M.CodeKind.stub) {
|
| - continue;
|
| - }
|
| - if (!profileCode.code.isDartCode) {
|
| - continue;
|
| - }
|
| - // If the code's function isn't this function.
|
| - if (profileCode.code.function != function) {
|
| - return true;
|
| - }
|
| - }
|
| - return false;
|
| - }
|
| -
|
| - void _addKindBasedAttributes(Set<String> attribs) {
|
| - if (function.kind == M.FunctionKind.tag) {
|
| - attribs.add('tag');
|
| - } else if (function.kind == M.FunctionKind.stub) {
|
| - attribs.add('stub');
|
| - } else if (function.kind == M.FunctionKind.native) {
|
| - attribs.add('native');
|
| - } else if (M.isSyntheticFunction(function.kind)) {
|
| - attribs.add('synthetic');
|
| - } else if (function.isNative) {
|
| - attribs.add('ffi'); // Not to be confused with a C function.
|
| - } else {
|
| - attribs.add('dart');
|
| - }
|
| - if (function.hasIntrinsic == true) {
|
| - attribs.add('intrinsic');
|
| - }
|
| - }
|
| -
|
| - ProfileFunction.fromMap(this.profile, this.function, Map data) {
|
| - function.profile = this;
|
| - for (var codeIndex in data['codes']) {
|
| - var profileCode = profile.codes[codeIndex];
|
| - profileCodes.add(profileCode);
|
| - }
|
| - profileCodes.sort(_sortCodes);
|
| -
|
| - _addKindBasedAttributes(attributes);
|
| - exclusiveTicks = data['exclusiveTicks'];
|
| - inclusiveTicks = data['inclusiveTicks'];
|
| -
|
| - normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
|
| - normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
|
| -
|
| - if (data.containsKey('exclusiveNativeAllocations') &&
|
| - data.containsKey('inclusiveNativeAllocations')) {
|
| - exclusiveNativeAllocations =
|
| - int.parse(data['exclusiveNativeAllocations']);
|
| - inclusiveNativeAllocations =
|
| - int.parse(data['inclusiveNativeAllocations']);
|
| - }
|
| -
|
| - formattedExclusivePercent =
|
| - Utils.formatPercent(exclusiveTicks, profile.sampleCount);
|
| -
|
| - formattedCpuTime = Utils.formatTimeMilliseconds(
|
| - profile.approximateMillisecondsForCount(exclusiveTicks));
|
| -
|
| - formattedOnStackTime = Utils.formatTimeMilliseconds(
|
| - profile.approximateMillisecondsForCount(inclusiveTicks));
|
| -
|
| - formattedInclusiveTicks =
|
| - '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} '
|
| - '($inclusiveTicks)';
|
| -
|
| - formattedExclusiveTicks =
|
| - '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} '
|
| - '($exclusiveTicks)';
|
| - }
|
| -
|
| - _recordCaller(ProfileFunction caller, int count) {
|
| - var r = callers[caller];
|
| - if (r == null) {
|
| - r = 0;
|
| - }
|
| - callers[caller] = r + count;
|
| - }
|
| -
|
| - _recordCallee(ProfileFunction callee, int count) {
|
| - var r = callees[callee];
|
| - if (r == null) {
|
| - r = 0;
|
| - }
|
| - callees[callee] = r + count;
|
| - }
|
| -}
|
| -
|
| -// TODO(johnmccutchan): Rename to SampleProfile
|
| -class CpuProfile extends M.SampleProfile {
|
| - Isolate isolate;
|
| -
|
| - int sampleCount = 0;
|
| - int samplePeriod = 0;
|
| - double sampleRate = 0.0;
|
| -
|
| - int stackDepth = 0;
|
| -
|
| - double timeSpan = 0.0;
|
| -
|
| - final Map<String, List> tries = <String, List>{};
|
| - final List<ProfileCode> codes = new List<ProfileCode>();
|
| - bool _builtCodeCalls = false;
|
| - final List<ProfileFunction> functions = new List<ProfileFunction>();
|
| - bool _builtFunctionCalls = false;
|
| -
|
| - CodeCallTree loadCodeTree(M.ProfileTreeDirection direction) {
|
| - switch (direction) {
|
| - case M.ProfileTreeDirection.inclusive:
|
| - return _loadCodeTree(true, tries['inclusiveCodeTrie']);
|
| - case M.ProfileTreeDirection.exclusive:
|
| - return _loadCodeTree(false, tries['exclusiveCodeTrie']);
|
| - }
|
| - throw new Exception('Unknown ProfileTreeDirection');
|
| - }
|
| -
|
| - FunctionCallTree loadFunctionTree(M.ProfileTreeDirection direction) {
|
| - switch (direction) {
|
| - case M.ProfileTreeDirection.inclusive:
|
| - return _loadFunctionTree(true, tries['inclusiveFunctionTrie']);
|
| - case M.ProfileTreeDirection.exclusive:
|
| - return _loadFunctionTree(false, tries['exclusiveFunctionTrie']);
|
| - }
|
| - throw new Exception('Unknown ProfileTreeDirection');
|
| - }
|
| -
|
| - buildCodeCallerAndCallees() {
|
| - if (_builtCodeCalls) {
|
| - return;
|
| - }
|
| - _builtCodeCalls = true;
|
| - var tree = loadCodeTree(M.ProfileTreeDirection.inclusive);
|
| - tree._recordCallerAndCallees();
|
| - }
|
| -
|
| - buildFunctionCallerAndCallees() {
|
| - if (_builtFunctionCalls) {
|
| - return;
|
| - }
|
| - _builtFunctionCalls = true;
|
| - var tree = loadFunctionTree(M.ProfileTreeDirection.inclusive);
|
| - tree._markFunctionCalls();
|
| - }
|
| -
|
| - clear() {
|
| - sampleCount = 0;
|
| - samplePeriod = 0;
|
| - sampleRate = 0.0;
|
| - stackDepth = 0;
|
| - timeSpan = 0.0;
|
| - codes.clear();
|
| - functions.clear();
|
| - tries.clear();
|
| - _builtCodeCalls = false;
|
| - _builtFunctionCalls = false;
|
| - }
|
| -
|
| - Future load(ServiceObjectOwner owner, ServiceMap profile) async {
|
| - await loadProgress(owner, profile).last;
|
| - }
|
| -
|
| - static Future sleep([Duration duration = const Duration(microseconds: 0)]) {
|
| - final Completer completer = new Completer();
|
| - new Timer(duration, () => completer.complete());
|
| - return completer.future;
|
| - }
|
| -
|
| - Stream<double> loadProgress(ServiceObjectOwner owner, ServiceMap profile) {
|
| - var progress = new StreamController<double>.broadcast();
|
| -
|
| - (() async {
|
| - final Stopwatch watch = new Stopwatch();
|
| - watch.start();
|
| - int count = 0;
|
| - var needToUpdate = () {
|
| - count++;
|
| - if (((count % 256) == 0) && (watch.elapsedMilliseconds > 16)) {
|
| - watch.reset();
|
| - return true;
|
| - }
|
| - return false;
|
| - };
|
| - var signal = (double p) {
|
| - progress.add(p);
|
| - return sleep();
|
| - };
|
| - try {
|
| - clear();
|
| - progress.add(0.0);
|
| - if (profile == null) {
|
| - return;
|
| - }
|
| -
|
| - if ((owner != null) && (owner is Isolate)) {
|
| - isolate = owner;
|
| - isolate.resetCachedProfileData();
|
| - }
|
| -
|
| - sampleCount = profile['sampleCount'];
|
| - samplePeriod = profile['samplePeriod'];
|
| - sampleRate = (Duration.MICROSECONDS_PER_SECOND / samplePeriod);
|
| - stackDepth = profile['stackDepth'];
|
| - timeSpan = profile['timeSpan'];
|
| -
|
| - num length = profile['codes'].length + profile['functions'].length;
|
| -
|
| - // Process code table.
|
| - for (var codeRegion in profile['codes']) {
|
| - if (needToUpdate()) {
|
| - await signal(count * 100.0 / length);
|
| - }
|
| - Code code = codeRegion['code'];
|
| - assert(code != null);
|
| - codes.add(new ProfileCode.fromMap(this, code, codeRegion));
|
| - }
|
| - // Process function table.
|
| - for (var profileFunction in profile['functions']) {
|
| - if (needToUpdate()) {
|
| - await signal(count * 100 / length);
|
| - }
|
| - ServiceFunction function = profileFunction['function'];
|
| - assert(function != null);
|
| - functions.add(
|
| - new ProfileFunction.fromMap(this, function, profileFunction));
|
| - }
|
| -
|
| - tries['exclusiveCodeTrie'] =
|
| - new Uint32List.fromList(profile['exclusiveCodeTrie']);
|
| - tries['inclusiveCodeTrie'] =
|
| - new Uint32List.fromList(profile['inclusiveCodeTrie']);
|
| - tries['exclusiveFunctionTrie'] =
|
| - new Uint32List.fromList(profile['exclusiveFunctionTrie']);
|
| - tries['inclusiveFunctionTrie'] =
|
| - new Uint32List.fromList(profile['inclusiveFunctionTrie']);
|
| - } finally {
|
| - progress.close();
|
| - }
|
| - }());
|
| - return progress.stream;
|
| - }
|
| -
|
| - // Data shared across calls to _read*TrieNode.
|
| - int _dataCursor = 0;
|
| -
|
| - // The code trie is serialized as a list of integers. Each node
|
| - // is recreated by consuming some portion of the list. The format is as
|
| - // follows:
|
| - // [0] index into codeTable of code object.
|
| - // [1] tick count (number of times this stack frame occured).
|
| - // [2] child node count
|
| - // Reading the trie is done by recursively reading the tree depth-first
|
| - // pre-order.
|
| - CodeCallTree _loadCodeTree(bool inclusive, List<int> data) {
|
| - if (data == null) {
|
| - return null;
|
| - }
|
| - if (data.length < 3) {
|
| - // Not enough for root node.
|
| - return null;
|
| - }
|
| - // Read the tree, returns the root node.
|
| - var root = _readCodeTrie(data);
|
| - return new CodeCallTree(inclusive, root);
|
| - }
|
| -
|
| - CodeCallTreeNode _readCodeTrieNode(List<int> data) {
|
| - // Lookup code object.
|
| - var codeIndex = data[_dataCursor++];
|
| - var code = codes[codeIndex];
|
| - // Node tick counter.
|
| - var count = data[_dataCursor++];
|
| - // Child node count.
|
| - var children = data[_dataCursor++];
|
| - // Inclusive native allocations.
|
| - var inclusiveNativeAllocations = data[_dataCursor++];
|
| - // Exclusive native allocations.
|
| - var exclusiveNativeAllocations = data[_dataCursor++];
|
| - // Create node.
|
| - var node = new CodeCallTreeNode(
|
| - code, count, inclusiveNativeAllocations, exclusiveNativeAllocations);
|
| - node.children.length = children;
|
| - return node;
|
| - }
|
| -
|
| - CodeCallTreeNode _readCodeTrie(List<int> data) {
|
| - final nodeStack = new List<CodeCallTreeNode>();
|
| - final childIndexStack = new List<int>();
|
| -
|
| - _dataCursor = 0;
|
| - // Read root.
|
| - var root = _readCodeTrieNode(data);
|
| -
|
| - // Push root onto stack.
|
| - if (root.children.length > 0) {
|
| - nodeStack.add(root);
|
| - childIndexStack.add(0);
|
| - }
|
| -
|
| - while (nodeStack.length > 0) {
|
| - var lastIndex = nodeStack.length - 1;
|
| - // Pop parent from stack.
|
| - var parent = nodeStack[lastIndex];
|
| - var childIndex = childIndexStack[lastIndex];
|
| -
|
| - // Read child node.
|
| - assert(childIndex < parent.children.length);
|
| - var node = _readCodeTrieNode(data);
|
| - parent.children[childIndex++] = node;
|
| -
|
| - // If parent still has children, update child index.
|
| - if (childIndex < parent.children.length) {
|
| - childIndexStack[lastIndex] = childIndex;
|
| - } else {
|
| - // Finished processing parent node.
|
| - nodeStack.removeLast();
|
| - childIndexStack.removeLast();
|
| - }
|
| -
|
| - // If node has children, push onto stack.
|
| - if (node.children.length > 0) {
|
| - nodeStack.add(node);
|
| - childIndexStack.add(0);
|
| - }
|
| - }
|
| -
|
| - return root;
|
| - }
|
| -
|
| - FunctionCallTree _loadFunctionTree(bool inclusive, List<int> data) {
|
| - if (data == null) {
|
| - return null;
|
| - }
|
| - if (data.length < 3) {
|
| - // Not enough integers for 1 node.
|
| - return null;
|
| - }
|
| - // Read the tree, returns the root node.
|
| - var root = _readFunctionTrie(data);
|
| - return new FunctionCallTree(inclusive, root);
|
| - }
|
| -
|
| - FunctionCallTreeNode _readFunctionTrieNode(List<int> data) {
|
| - // Read index into function table.
|
| - var index = data[_dataCursor++];
|
| - // Lookup function object.
|
| - var function = functions[index];
|
| - // Counter.
|
| - var count = data[_dataCursor++];
|
| - // Inclusive native allocations.
|
| - var inclusiveNativeAllocations = data[_dataCursor++];
|
| - // Exclusive native allocations.
|
| - var exclusiveNativeAllocations = data[_dataCursor++];
|
| - // Create node.
|
| - var node = new FunctionCallTreeNode(function, count,
|
| - inclusiveNativeAllocations, exclusiveNativeAllocations);
|
| - // Number of code index / count pairs.
|
| - var codeCount = data[_dataCursor++];
|
| - node.codes.length = codeCount;
|
| - var totalCodeTicks = 0;
|
| - for (var i = 0; i < codeCount; i++) {
|
| - var codeIndex = data[_dataCursor++];
|
| - var code = codes[codeIndex];
|
| - assert(code != null);
|
| - var codeTicks = data[_dataCursor++];
|
| - totalCodeTicks += codeTicks;
|
| - var nodeCode = new FunctionCallTreeNodeCode(code, codeTicks);
|
| - node.codes[i] = nodeCode;
|
| - }
|
| - node.setCodeAttributes();
|
| - node._totalCodeTicks = totalCodeTicks;
|
| - // Number of children.
|
| - var childCount = data[_dataCursor++];
|
| - node.children.length = childCount;
|
| - return node;
|
| - }
|
| -
|
| - FunctionCallTreeNode _readFunctionTrie(List<int> data) {
|
| - final nodeStack = new List<FunctionCallTreeNode>();
|
| - final childIndexStack = new List<int>();
|
| -
|
| - _dataCursor = 0;
|
| -
|
| - // Read root.
|
| - var root = _readFunctionTrieNode(data);
|
| -
|
| - // Push root onto stack.
|
| - if (root.children.length > 0) {
|
| - nodeStack.add(root);
|
| - childIndexStack.add(0);
|
| - }
|
| -
|
| - while (nodeStack.length > 0) {
|
| - var lastIndex = nodeStack.length - 1;
|
| - // Pop parent from stack.
|
| - var parent = nodeStack[lastIndex];
|
| - var childIndex = childIndexStack[lastIndex];
|
| -
|
| - // Read child node.
|
| - assert(childIndex < parent.children.length);
|
| - var node = _readFunctionTrieNode(data);
|
| - parent.children[childIndex++] = node;
|
| -
|
| - // If parent still has children, update child index.
|
| - if (childIndex < parent.children.length) {
|
| - childIndexStack[lastIndex] = childIndex;
|
| - } else {
|
| - // Finished processing parent node.
|
| - nodeStack.removeLast();
|
| - childIndexStack.removeLast();
|
| - }
|
| -
|
| - // If node has children, push onto stack.
|
| - if (node.children.length > 0) {
|
| - nodeStack.add(node);
|
| - childIndexStack.add(0);
|
| - }
|
| - }
|
| -
|
| - return root;
|
| - }
|
| -
|
| - int approximateMillisecondsForCount(count) {
|
| - return (count * samplePeriod) ~/ Duration.MICROSECONDS_PER_MILLISECOND;
|
| - }
|
| -
|
| - double approximateSecondsForCount(count) {
|
| - return (count * samplePeriod) / Duration.MICROSECONDS_PER_SECOND;
|
| - }
|
| -}
|
|
|