Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3003)

Unified Diff: runtime/observatory/lib/src/cpu_profile/cpu_profile.dart

Issue 965593002: Improved profiler view and inclusive profile tree (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
index 99744f6029952257f37e8723dac377569ce17cba..ead56dd2e1dd7b3312c35f93b9e21d5e68176263 100644
--- a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
+++ b/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
@@ -4,27 +4,155 @@
part of cpu_profiler;
-class CodeTrieNode {
+class CodeCallTreeNode {
final ProfileCode profileCode;
final int count;
- final children = new List<CodeTrieNode>();
- CodeTrieNode(this.profileCode, this.count);
+ double get percentage => _percentage;
+ double _percentage = 0.0;
+ final children = new List<CodeCallTreeNode>();
+ final Set<String> attributes = new Set<String>();
+ CodeCallTreeNode(this.profileCode, this.count) {
+ attributes.addAll(profileCode.attributes);
+ }
+}
+
+class CodeCallTree {
+ final bool inclusive;
+ final CodeCallTreeNode root;
+ CodeCallTree(this.inclusive, this.root) {
+ _setCodePercentage(null, root);
+ }
+
+ _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);
+ }
+ }
}
-class FunctionTrieNodeCode {
+class FunctionCallTreeNodeCode {
final ProfileCode code;
final int ticks;
- FunctionTrieNodeCode(this.code, this.ticks);
+ FunctionCallTreeNodeCode(this.code, this.ticks);
}
-class FunctionTrieNode {
+class FunctionCallTreeNode {
final ProfileFunction profileFunction;
final int count;
- final children = new List<FunctionTrieNode>();
- final codes = new List<FunctionTrieNodeCode>();
+ double get percentage => _percentage;
+ double _percentage = 0.0;
+ final children = new List<FunctionCallTreeNode>();
+ final Set<String> attributes = new Set<String>();
+ final codes = new List<FunctionCallTreeNodeCode>();
int _totalCodeTicks = 0;
int get totalCodesTicks => _totalCodeTicks;
- FunctionTrieNode(this.profileFunction, this.count);
+
+ // 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 == 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 == CodeKind.Stub) {
+ continue;
+ }
+ // If the code's function isn't this function.
+ if (profileCode.code.function != profileFunction.function) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ setCodeAttributes() {
+ if (hasOptimizedCode()) {
+ attributes.add('optimized');
+ }
+ if (hasUnoptimizedCode()) {
+ attributes.add('unoptimized');
+ }
+ if (isInlined()) {
+ attributes.add('inlined');
+ }
+ }
+
+ FunctionCallTreeNode(this.profileFunction, this.count) {
+ profileFunction._addKindBasedAttributes(attributes);
+ }
+}
+
+class FunctionCallTree {
+ final bool inclusive;
+ final FunctionCallTreeNode root;
+ FunctionCallTree(this.inclusive, this.root) {
+ _setFunctionPercentage(null, root);
+ }
+
+ 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);
+ }
+ }
}
class CodeTick {
@@ -47,10 +175,16 @@ class ProfileCode {
final Code code;
int exclusiveTicks;
int inclusiveTicks;
+ 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>();
void _processTicks(List<String> profileTicks) {
assert(profileTicks != null);
@@ -82,13 +216,43 @@ class ProfileCode {
code.profile = this;
+ if (code.isDartCode) {
+ if (code.isOptimized) {
+ attributes.add('optimized');
+ } else {
+ attributes.add('unoptimized');
+ }
+ }
+ if (code.isDartCode) {
+ attributes.add('dart');
+ } else if (code.kind == CodeKind.Tag) {
+ attributes.add('tag');
+ } else if (code.kind == CodeKind.Native) {
+ attributes.add('native');
+ }
inclusiveTicks = int.parse(data['inclusiveTicks']);
exclusiveTicks = int.parse(data['exclusiveTicks']);
+
+ normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
+
+ normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
+
var ticks = data['ticks'];
if (ticks != null) {
_processTicks(ticks);
}
+ 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)';
@@ -104,14 +268,20 @@ class ProfileFunction {
final ServiceFunction function;
// List of compiled code objects containing this function.
final List<ProfileCode> profileCodes = new List<ProfileCode>();
-
// Absolute ticks:
int exclusiveTicks = 0;
int inclusiveTicks = 0;
// Global percentages:
- double globalExclusiveTicks = 0.0;
- double globalInclusiveTicks = 0.0;
+ double normalizedExclusiveTicks = 0.0;
+ double normalizedInclusiveTicks = 0.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) {
@@ -123,6 +293,67 @@ class ProfileFunction {
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 == 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 == 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 == FunctionKind.kTag) {
+ attribs.add('tag');
+ } else if (function.kind == FunctionKind.kStub) {
+ attribs.add('dart');
+ attribs.add('stub');
+ } else if (function.kind == FunctionKind.kNative) {
+ attribs.add('native');
+ } else if (function.kind.isSynthetic()) {
+ attribs.add('synthetic');
+ } else {
+ attribs.add('dart');
+ }
+ }
+
ProfileFunction.fromMap(this.profile, this.function, Map data) {
for (var codeIndex in data['codes']) {
var profileCode = profile.codes[codeIndex];
@@ -130,11 +361,40 @@ class ProfileFunction {
}
profileCodes.sort(_sortCodes);
+ if (hasOptimizedCode()) {
+ attributes.add('optimized');
+ }
+ if (hasUnoptimizedCode()) {
+ attributes.add('unoptimized');
+ }
+ if (isInlined()) {
+ attributes.add('inlined');
+ }
+ _addKindBasedAttributes(attributes);
exclusiveTicks = int.parse(data['exclusiveTicks']);
inclusiveTicks = int.parse(data['inclusiveTicks']);
- globalExclusiveTicks = exclusiveTicks / profile.sampleCount;
- globalInclusiveTicks = inclusiveTicks / profile.sampleCount;
+ normalizedExclusiveTicks = exclusiveTicks / profile.sampleCount;
+ normalizedInclusiveTicks = inclusiveTicks / profile.sampleCount;
+
+ 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)';
}
}
@@ -153,8 +413,10 @@ class CpuProfile {
double timeSpan = 0.0;
- CodeTrieNode codeTrieRoot;
- FunctionTrieNode functionTrieRoot;
+ final Map<String, CodeCallTree> codeTrees =
+ <String, CodeCallTree>{};
+ final Map<String, FunctionCallTree> functionTrees =
+ <String, FunctionCallTree>{};
final List<ProfileCode> codes = new List<ProfileCode>();
final List<ProfileFunction> functions = new List<ProfileFunction>();
@@ -165,8 +427,8 @@ class CpuProfile {
sampleRate = 0.0;
stackDepth = 0;
timeSpan = 0.0;
- codeTrieRoot = null;
- functionTrieRoot = null;
+ codeTrees.clear();
+ functionTrees.clear();
codes.clear();
functions.clear();
}
@@ -202,15 +464,27 @@ class CpuProfile {
new ProfileFunction.fromMap(this, function, profileFunction));
}
- // Process code trie.
+ // Process code trees.
var exclusiveCodeTrie = profile['exclusiveCodeTrie'];
- assert(exclusiveCodeTrie != null);
- codeTrieRoot = _processCodeTrie(exclusiveCodeTrie);
+ if (exclusiveCodeTrie != null) {
+ codeTrees['exclusive'] = _loadCodeTree(false, exclusiveCodeTrie);
+ }
+ var inclusiveCodeTrie = profile['inclusiveCodeTrie'];
+ if (inclusiveCodeTrie != null) {
+ codeTrees['inclusive'] = _loadCodeTree(true, inclusiveCodeTrie);
+ }
- // Process function trie.
+ // Process function trees.
var exclusiveFunctionTrie = profile['exclusiveFunctionTrie'];
- assert(exclusiveFunctionTrie != null);
- functionTrieRoot = _processFunctionTrie(exclusiveFunctionTrie);
+ if (exclusiveFunctionTrie != null) {
+ functionTrees['exclusive'] =
+ _loadFunctionTree(false, exclusiveFunctionTrie);
+ }
+ var inclusiveFunctionTrie = profile['inclusiveFunctionTrie'];
+ if (inclusiveFunctionTrie != null) {
+ functionTrees['inclusive'] =
+ _loadFunctionTree(true, inclusiveFunctionTrie);
+ }
}
// Data shared across calls to _read*TrieNode.
@@ -225,7 +499,7 @@ class CpuProfile {
// [2] child node count
// Reading the trie is done by recursively reading the tree depth-first
// pre-order.
- CodeTrieNode _processCodeTrie(List<int> data) {
+ CodeCallTree _loadCodeTree(bool inclusive, List<int> data) {
// Setup state shared across calls to _readTrieNode.
_trieDataCursor = 0;
_trieData = data;
@@ -237,10 +511,11 @@ class CpuProfile {
return null;
}
// Read the tree, returns the root node.
- return _readCodeTrieNode();
+ var root = _readCodeTrieNode();
+ return new CodeCallTree(inclusive, root);
}
- CodeTrieNode _readCodeTrieNode() {
+ CodeCallTreeNode _readCodeTrieNode() {
// Read index into code table.
var index = _trieData[_trieDataCursor++];
// Lookup code object.
@@ -248,7 +523,7 @@ class CpuProfile {
// Frame counter.
var count = _trieData[_trieDataCursor++];
// Create node.
- var node = new CodeTrieNode(code, count);
+ var node = new CodeCallTreeNode(code, count);
// Number of children.
var children = _trieData[_trieDataCursor++];
// Recursively read child nodes.
@@ -259,7 +534,7 @@ class CpuProfile {
return node;
}
- FunctionTrieNode _processFunctionTrie(List<int> data) {
+ FunctionCallTree _loadFunctionTree(bool inclusive, List<int> data) {
// Setup state shared across calls to _readTrieNode.
_trieDataCursor = 0;
_trieData = data;
@@ -271,10 +546,11 @@ class CpuProfile {
return null;
}
// Read the tree, returns the root node.
- return _readFunctionTrieNode();
+ var root = _readFunctionTrieNode();
+ return new FunctionCallTree(inclusive, root);
}
- FunctionTrieNode _readFunctionTrieNode() {
+ FunctionCallTreeNode _readFunctionTrieNode() {
// Read index into function table.
var index = _trieData[_trieDataCursor++];
// Lookup function object.
@@ -282,7 +558,7 @@ class CpuProfile {
// Frame counter.
var count = _trieData[_trieDataCursor++];
// Create node.
- var node = new FunctionTrieNode(function, count);
+ var node = new FunctionCallTreeNode(function, count);
// Number of code index / count pairs.
var codeCount = _trieData[_trieDataCursor++];
var totalCodeTicks = 0;
@@ -291,8 +567,9 @@ class CpuProfile {
var code = codes[codeIndex];
var codeTicks = _trieData[_trieDataCursor++];
totalCodeTicks += codeTicks;
- var nodeCode = new FunctionTrieNodeCode(code, codeTicks);
+ var nodeCode = new FunctionCallTreeNodeCode(code, codeTicks);
node.codes.add(nodeCode);
+ node.setCodeAttributes();
}
node._totalCodeTicks = totalCodeTicks;
// Number of children.
@@ -305,6 +582,11 @@ class CpuProfile {
return node;
}
+ int approximateMillisecondsForCount(count) {
+ var MICROSECONDS_PER_MILLISECOND = 1000.0;
+ return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND;
+ }
+
double approximateSecondsForCount(count) {
var MICROSECONDS_PER_SECOND = 1000000.0;
return (count * samplePeriod) / MICROSECONDS_PER_SECOND;
« no previous file with comments | « runtime/observatory/lib/src/app/view_model.dart ('k') | runtime/observatory/lib/src/elements/class_tree.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698