Chromium Code Reviews| 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 359117128c674a4ef626cfa580dd5d2fa0209996..4393b1299c6ae19f045fc74ef8a3e6b55679612f 100644 |
| --- a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart |
| +++ b/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart |
| @@ -41,6 +41,24 @@ class CodeCallTree { |
| _setCodePercentage(node, child); |
| } |
| } |
| + |
| + _markCodeCallsInner(CodeCallTreeNode caller, |
| + CodeCallTreeNode callee) { |
| + if (caller != null) { |
| + caller.profileCode._noteCallee(callee.profileCode, callee.count); |
| + callee.profileCode._noteCaller(caller.profileCode, callee.count); |
| + } |
| + |
| + for (var child in callee.children) { |
| + _markCodeCallsInner(callee, child); |
| + } |
| + } |
| + |
| + _markCodeCalls() { |
|
turnidge
2015/04/07 21:24:31
What does "marking" mean here? Is there another n
Cutch
2015/04/14 21:45:15
Went with _recordCallerAndCallees.
The class is n
|
| + for (var child in root.children) { |
| + _markCodeCallsInner(null, child); |
| + } |
| + } |
| } |
| class FunctionCallTreeNodeCode { |
| @@ -129,6 +147,108 @@ class FunctionCallTreeNode { |
| } |
| } |
| +typedef bool FunctionCallTreeNodeFilter(FunctionCallTreeNode node); |
| + |
| +class _FilteredTreeBuilder { |
|
turnidge
2015/04/07 21:24:31
Can you make some of the functions in this class p
Cutch
2015/04/14 21:45:15
Done.
|
| + final FunctionCallTreeNodeFilter filter; |
| + final FunctionCallTree tree; |
| + final FunctionCallTree filtered; |
| + final List currentPath = []; |
| + |
| + _FilteredTreeBuilder(this.filter, FunctionCallTree tree) |
| + : tree = tree, |
| + filtered = |
| + new FunctionCallTree( |
| + tree.inclusive, |
| + new FunctionCallTreeNode( |
| + tree.root.profileFunction, |
| + tree.root.count)); |
| + |
| + FunctionCallTreeNode findFunctionInChildren(FunctionCallTreeNode current, |
| + FunctionCallTreeNode needle) { |
| + for (var child in current.children) { |
| + if (child.profileFunction == needle.profileFunction) { |
| + return child; |
| + } |
| + } |
| + return null; |
| + } |
| + |
| + FunctionCallTreeNode addCurrentPath() { |
| + FunctionCallTreeNode current = filtered.root; |
| + // Tree root is always the first element of the current path. |
| + assert(tree.root == currentPath[0]); |
| + // Assert that tree and filtered are different. |
| + assert(tree.root != current); |
| + for (var i = 1; i < currentPath.length; i++) { |
| + var toAdd = currentPath[i]; |
| + var child = findFunctionInChildren(current, toAdd); |
| + if (child == null) { |
| + child = new FunctionCallTreeNode(toAdd.profileFunction, toAdd.count); |
| + current.children.add(child); |
| + } |
| + current = child; |
| + assert(current.count == toAdd.count); |
| + } |
| + return current; |
| + } |
| + |
| + appendTree(FunctionCallTreeNode current, FunctionCallTreeNode next) { |
| + if (next == null) { |
| + return; |
| + } |
| + var child = findFunctionInChildren(current, next); |
| + if (child == null) { |
| + child = new FunctionCallTreeNode(next.profileFunction, next.count); |
| + current.children.add(child); |
| + } |
| + current = child; |
| + for (var nextChild in next.children) { |
| + appendTree(current, nextChild); |
| + } |
| + } |
| + |
| + addTree(FunctionCallTreeNode child) { |
| + var current = addCurrentPath(); |
| + appendTree(current, child); |
| + } |
| + |
| + descend(FunctionCallTreeNode 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); |
| + } |
| + |
| + build() { |
| + assert(filtered != null); |
| + assert(filter != null); |
| + assert(tree != null); |
| + descend(tree.root); |
| + } |
| +} |
| + |
| class FunctionCallTree { |
| final bool inclusive; |
| final FunctionCallTreeNode root; |
| @@ -136,6 +256,13 @@ class FunctionCallTree { |
| _setFunctionPercentage(null, root); |
| } |
| + FunctionCallTree filtered(FunctionCallTreeNodeFilter filter) { |
| + var treeFilter = new _FilteredTreeBuilder(filter, this); |
| + treeFilter.build(); |
| + _setFunctionPercentage(null, treeFilter.filtered.root); |
| + return treeFilter.filtered; |
| + } |
| + |
| void _setFunctionPercentage(FunctionCallTreeNode parent, |
| FunctionCallTreeNode node) { |
| assert(node != null); |
| @@ -154,6 +281,23 @@ class FunctionCallTree { |
| _setFunctionPercentage(node, child); |
| } |
| } |
| + |
| + _markFunctionCallsInner(FunctionCallTreeNode caller, |
| + FunctionCallTreeNode callee) { |
| + if (caller != null) { |
| + caller.profileFunction._noteCallee(callee.profileFunction, callee.count); |
| + callee.profileFunction._noteCaller(caller.profileFunction, callee.count); |
| + } |
| + for (var child in callee.children) { |
| + _markFunctionCallsInner(callee, child); |
| + } |
| + } |
| + |
| + _markFunctionCalls() { |
| + for (var child in root.children) { |
| + _markFunctionCallsInner(null, child); |
| + } |
| + } |
| } |
| class CodeTick { |
| @@ -186,6 +330,8 @@ class ProfileCode { |
| 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<String> profileTicks) { |
| assert(profileTicks != null); |
| @@ -262,6 +408,22 @@ class ProfileCode { |
| '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' |
| '($exclusiveTicks)'; |
| } |
| + |
| + _noteCaller(ProfileCode caller, int count) { |
|
turnidge
2015/04/07 21:24:31
Maybe _addCaller and _addCallee?
Cutch
2015/04/14 21:45:15
Done.
|
| + var r = callers[caller]; |
| + if (r == null) { |
| + r = 0; |
| + } |
| + callers[caller] = r + count; |
| + } |
| + |
| + _noteCallee(ProfileCode callee, int count) { |
| + var r = callees[callee]; |
| + if (r == null) { |
| + r = 0; |
| + } |
| + callees[callee] = r + count; |
| + } |
| } |
| class ProfileFunction { |
| @@ -269,6 +431,9 @@ class ProfileFunction { |
| 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; |
| @@ -356,6 +521,7 @@ class ProfileFunction { |
| } |
| 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); |
| @@ -397,6 +563,22 @@ class ProfileFunction { |
| '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' |
| '($exclusiveTicks)'; |
| } |
| + |
| + _noteCaller(ProfileFunction caller, int count) { |
| + var r = callers[caller]; |
| + if (r == null) { |
| + r = 0; |
| + } |
| + callers[caller] = r + count; |
| + } |
| + |
| + _noteCallee(ProfileFunction callee, int count) { |
| + var r = callees[callee]; |
| + if (r == null) { |
| + r = 0; |
| + } |
| + callees[callee] = r + count; |
| + } |
| } |
| @@ -416,7 +598,9 @@ class CpuProfile { |
| final Map<String, List> tries = <String, List>{}; |
| final List<ProfileCode> codes = new List<ProfileCode>(); |
| + bool _markedCodeCalls = false; |
| final List<ProfileFunction> functions = new List<ProfileFunction>(); |
| + bool _markedFunctionCalls = false; |
| CodeCallTree loadCodeTree(String name) { |
| if (name == 'inclusive') { |
| @@ -434,7 +618,25 @@ class CpuProfile { |
| } |
| } |
| - void clear() { |
| + markCodeCalls() { |
| + if (_markedCodeCalls) { |
| + return; |
| + } |
| + _markedCodeCalls = true; |
| + var tree = loadCodeTree('inclusive'); |
| + tree._markCodeCalls(); |
| + } |
| + |
| + markFunctionCalls() { |
| + if (_markedFunctionCalls) { |
| + return; |
| + } |
| + _markedFunctionCalls = true; |
| + var tree = loadFunctionTree('inclusive'); |
| + tree._markFunctionCalls(); |
| + } |
| + |
| + clear() { |
| sampleCount = 0; |
| samplePeriod = 0; |
| sampleRate = 0.0; |
| @@ -443,9 +645,11 @@ class CpuProfile { |
| codes.clear(); |
| functions.clear(); |
| tries.clear(); |
| + _markedCodeCalls = false; |
| + _markedFunctionCalls = false; |
| } |
| - void load(Isolate isolate, ServiceMap profile) { |
| + load(Isolate isolate, ServiceMap profile) { |
| clear(); |
| if ((isolate == null) || (profile == null)) { |
| return; |