OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of cpu_profiler; | 5 part of cpu_profiler; |
6 | 6 |
7 class CodeCallTreeNode { | 7 class CodeCallTreeNode { |
8 final ProfileCode profileCode; | 8 final ProfileCode profileCode; |
9 final int count; | 9 final int count; |
10 double get percentage => _percentage; | 10 double get percentage => _percentage; |
(...skipping 23 matching lines...) Expand all Loading... | |
34 } | 34 } |
35 if (inclusive) { | 35 if (inclusive) { |
36 node._percentage = parentPercentage * (node.count / parentCount); | 36 node._percentage = parentPercentage * (node.count / parentCount); |
37 } else { | 37 } else { |
38 node._percentage = (node.count / parentCount); | 38 node._percentage = (node.count / parentCount); |
39 } | 39 } |
40 for (var child in node.children) { | 40 for (var child in node.children) { |
41 _setCodePercentage(node, child); | 41 _setCodePercentage(node, child); |
42 } | 42 } |
43 } | 43 } |
44 | |
45 _markCodeCallsInner(CodeCallTreeNode caller, | |
46 CodeCallTreeNode callee) { | |
47 if (caller != null) { | |
48 caller.profileCode._noteCallee(callee.profileCode, callee.count); | |
49 callee.profileCode._noteCaller(caller.profileCode, callee.count); | |
50 } | |
51 | |
52 for (var child in callee.children) { | |
53 _markCodeCallsInner(callee, child); | |
54 } | |
55 } | |
56 | |
57 _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
| |
58 for (var child in root.children) { | |
59 _markCodeCallsInner(null, child); | |
60 } | |
61 } | |
44 } | 62 } |
45 | 63 |
46 class FunctionCallTreeNodeCode { | 64 class FunctionCallTreeNodeCode { |
47 final ProfileCode code; | 65 final ProfileCode code; |
48 final int ticks; | 66 final int ticks; |
49 FunctionCallTreeNodeCode(this.code, this.ticks); | 67 FunctionCallTreeNodeCode(this.code, this.ticks); |
50 } | 68 } |
51 | 69 |
52 class FunctionCallTreeNode { | 70 class FunctionCallTreeNode { |
53 final ProfileFunction profileFunction; | 71 final ProfileFunction profileFunction; |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
122 } | 140 } |
123 if (hasUnoptimizedCode()) { | 141 if (hasUnoptimizedCode()) { |
124 attributes.add('unoptimized'); | 142 attributes.add('unoptimized'); |
125 } | 143 } |
126 if (isInlined()) { | 144 if (isInlined()) { |
127 attributes.add('inlined'); | 145 attributes.add('inlined'); |
128 } | 146 } |
129 } | 147 } |
130 } | 148 } |
131 | 149 |
150 typedef bool FunctionCallTreeNodeFilter(FunctionCallTreeNode node); | |
151 | |
152 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.
| |
153 final FunctionCallTreeNodeFilter filter; | |
154 final FunctionCallTree tree; | |
155 final FunctionCallTree filtered; | |
156 final List currentPath = []; | |
157 | |
158 _FilteredTreeBuilder(this.filter, FunctionCallTree tree) | |
159 : tree = tree, | |
160 filtered = | |
161 new FunctionCallTree( | |
162 tree.inclusive, | |
163 new FunctionCallTreeNode( | |
164 tree.root.profileFunction, | |
165 tree.root.count)); | |
166 | |
167 FunctionCallTreeNode findFunctionInChildren(FunctionCallTreeNode current, | |
168 FunctionCallTreeNode needle) { | |
169 for (var child in current.children) { | |
170 if (child.profileFunction == needle.profileFunction) { | |
171 return child; | |
172 } | |
173 } | |
174 return null; | |
175 } | |
176 | |
177 FunctionCallTreeNode addCurrentPath() { | |
178 FunctionCallTreeNode current = filtered.root; | |
179 // Tree root is always the first element of the current path. | |
180 assert(tree.root == currentPath[0]); | |
181 // Assert that tree and filtered are different. | |
182 assert(tree.root != current); | |
183 for (var i = 1; i < currentPath.length; i++) { | |
184 var toAdd = currentPath[i]; | |
185 var child = findFunctionInChildren(current, toAdd); | |
186 if (child == null) { | |
187 child = new FunctionCallTreeNode(toAdd.profileFunction, toAdd.count); | |
188 current.children.add(child); | |
189 } | |
190 current = child; | |
191 assert(current.count == toAdd.count); | |
192 } | |
193 return current; | |
194 } | |
195 | |
196 appendTree(FunctionCallTreeNode current, FunctionCallTreeNode next) { | |
197 if (next == null) { | |
198 return; | |
199 } | |
200 var child = findFunctionInChildren(current, next); | |
201 if (child == null) { | |
202 child = new FunctionCallTreeNode(next.profileFunction, next.count); | |
203 current.children.add(child); | |
204 } | |
205 current = child; | |
206 for (var nextChild in next.children) { | |
207 appendTree(current, nextChild); | |
208 } | |
209 } | |
210 | |
211 addTree(FunctionCallTreeNode child) { | |
212 var current = addCurrentPath(); | |
213 appendTree(current, child); | |
214 } | |
215 | |
216 descend(FunctionCallTreeNode current) { | |
217 if (current == null) { | |
218 return; | |
219 } | |
220 currentPath.add(current); | |
221 | |
222 if (filter(current)) { | |
223 // Filter matched. | |
224 if (current.children.length == 0) { | |
225 // Have no children. Add this path. | |
226 addTree(null); | |
227 } else { | |
228 // Add all child trees. | |
229 for (var child in current.children) { | |
230 addTree(child); | |
231 } | |
232 } | |
233 } else { | |
234 // Did not match, descend to each child. | |
235 for (var child in current.children) { | |
236 descend(child); | |
237 } | |
238 } | |
239 | |
240 var last = currentPath.removeLast(); | |
241 assert(current == last); | |
242 } | |
243 | |
244 build() { | |
245 assert(filtered != null); | |
246 assert(filter != null); | |
247 assert(tree != null); | |
248 descend(tree.root); | |
249 } | |
250 } | |
251 | |
132 class FunctionCallTree { | 252 class FunctionCallTree { |
133 final bool inclusive; | 253 final bool inclusive; |
134 final FunctionCallTreeNode root; | 254 final FunctionCallTreeNode root; |
135 FunctionCallTree(this.inclusive, this.root) { | 255 FunctionCallTree(this.inclusive, this.root) { |
136 _setFunctionPercentage(null, root); | 256 _setFunctionPercentage(null, root); |
137 } | 257 } |
138 | 258 |
259 FunctionCallTree filtered(FunctionCallTreeNodeFilter filter) { | |
260 var treeFilter = new _FilteredTreeBuilder(filter, this); | |
261 treeFilter.build(); | |
262 _setFunctionPercentage(null, treeFilter.filtered.root); | |
263 return treeFilter.filtered; | |
264 } | |
265 | |
139 void _setFunctionPercentage(FunctionCallTreeNode parent, | 266 void _setFunctionPercentage(FunctionCallTreeNode parent, |
140 FunctionCallTreeNode node) { | 267 FunctionCallTreeNode node) { |
141 assert(node != null); | 268 assert(node != null); |
142 var parentPercentage = 1.0; | 269 var parentPercentage = 1.0; |
143 var parentCount = node.count; | 270 var parentCount = node.count; |
144 if (parent != null) { | 271 if (parent != null) { |
145 parentPercentage = parent._percentage; | 272 parentPercentage = parent._percentage; |
146 parentCount = parent.count; | 273 parentCount = parent.count; |
147 } | 274 } |
148 if (inclusive) { | 275 if (inclusive) { |
149 node._percentage = parentPercentage * (node.count / parentCount); | 276 node._percentage = parentPercentage * (node.count / parentCount); |
150 } else { | 277 } else { |
151 node._percentage = (node.count / parentCount); | 278 node._percentage = (node.count / parentCount); |
152 } | 279 } |
153 for (var child in node.children) { | 280 for (var child in node.children) { |
154 _setFunctionPercentage(node, child); | 281 _setFunctionPercentage(node, child); |
155 } | 282 } |
156 } | 283 } |
284 | |
285 _markFunctionCallsInner(FunctionCallTreeNode caller, | |
286 FunctionCallTreeNode callee) { | |
287 if (caller != null) { | |
288 caller.profileFunction._noteCallee(callee.profileFunction, callee.count); | |
289 callee.profileFunction._noteCaller(caller.profileFunction, callee.count); | |
290 } | |
291 for (var child in callee.children) { | |
292 _markFunctionCallsInner(callee, child); | |
293 } | |
294 } | |
295 | |
296 _markFunctionCalls() { | |
297 for (var child in root.children) { | |
298 _markFunctionCallsInner(null, child); | |
299 } | |
300 } | |
157 } | 301 } |
158 | 302 |
159 class CodeTick { | 303 class CodeTick { |
160 final int exclusiveTicks; | 304 final int exclusiveTicks; |
161 final int inclusiveTicks; | 305 final int inclusiveTicks; |
162 CodeTick(this.exclusiveTicks, this.inclusiveTicks); | 306 CodeTick(this.exclusiveTicks, this.inclusiveTicks); |
163 } | 307 } |
164 | 308 |
165 class InlineIntervalTick { | 309 class InlineIntervalTick { |
166 final int startAddress; | 310 final int startAddress; |
(...skipping 12 matching lines...) Expand all Loading... | |
179 double normalizedExclusiveTicks = 0.0; | 323 double normalizedExclusiveTicks = 0.0; |
180 double normalizedInclusiveTicks = 0.0; | 324 double normalizedInclusiveTicks = 0.0; |
181 final addressTicks = new Map<int, CodeTick>(); | 325 final addressTicks = new Map<int, CodeTick>(); |
182 final intervalTicks = new Map<int, InlineIntervalTick>(); | 326 final intervalTicks = new Map<int, InlineIntervalTick>(); |
183 String formattedInclusiveTicks = ''; | 327 String formattedInclusiveTicks = ''; |
184 String formattedExclusiveTicks = ''; | 328 String formattedExclusiveTicks = ''; |
185 String formattedExclusivePercent = ''; | 329 String formattedExclusivePercent = ''; |
186 String formattedCpuTime = ''; | 330 String formattedCpuTime = ''; |
187 String formattedOnStackTime = ''; | 331 String formattedOnStackTime = ''; |
188 final Set<String> attributes = new Set<String>(); | 332 final Set<String> attributes = new Set<String>(); |
333 final Map<ProfileCode, int> callers = new Map<ProfileCode, int>(); | |
334 final Map<ProfileCode, int> callees = new Map<ProfileCode, int>(); | |
189 | 335 |
190 void _processTicks(List<String> profileTicks) { | 336 void _processTicks(List<String> profileTicks) { |
191 assert(profileTicks != null); | 337 assert(profileTicks != null); |
192 assert((profileTicks.length % 3) == 0); | 338 assert((profileTicks.length % 3) == 0); |
193 for (var i = 0; i < profileTicks.length; i += 3) { | 339 for (var i = 0; i < profileTicks.length; i += 3) { |
194 var address = int.parse(profileTicks[i], radix:16); | 340 var address = int.parse(profileTicks[i], radix:16); |
195 var exclusive = int.parse(profileTicks[i + 1]); | 341 var exclusive = int.parse(profileTicks[i + 1]); |
196 var inclusive = int.parse(profileTicks[i + 2]); | 342 var inclusive = int.parse(profileTicks[i + 2]); |
197 var tick = new CodeTick(exclusive, inclusive); | 343 var tick = new CodeTick(exclusive, inclusive); |
198 addressTicks[address] = tick; | 344 addressTicks[address] = tick; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
255 profile.approximateMillisecondsForCount(inclusiveTicks)); | 401 profile.approximateMillisecondsForCount(inclusiveTicks)); |
256 | 402 |
257 formattedInclusiveTicks = | 403 formattedInclusiveTicks = |
258 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' | 404 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' |
259 '($inclusiveTicks)'; | 405 '($inclusiveTicks)'; |
260 | 406 |
261 formattedExclusiveTicks = | 407 formattedExclusiveTicks = |
262 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' | 408 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' |
263 '($exclusiveTicks)'; | 409 '($exclusiveTicks)'; |
264 } | 410 } |
411 | |
412 _noteCaller(ProfileCode caller, int count) { | |
turnidge
2015/04/07 21:24:31
Maybe _addCaller and _addCallee?
Cutch
2015/04/14 21:45:15
Done.
| |
413 var r = callers[caller]; | |
414 if (r == null) { | |
415 r = 0; | |
416 } | |
417 callers[caller] = r + count; | |
418 } | |
419 | |
420 _noteCallee(ProfileCode callee, int count) { | |
421 var r = callees[callee]; | |
422 if (r == null) { | |
423 r = 0; | |
424 } | |
425 callees[callee] = r + count; | |
426 } | |
265 } | 427 } |
266 | 428 |
267 class ProfileFunction { | 429 class ProfileFunction { |
268 final CpuProfile profile; | 430 final CpuProfile profile; |
269 final ServiceFunction function; | 431 final ServiceFunction function; |
270 // List of compiled code objects containing this function. | 432 // List of compiled code objects containing this function. |
271 final List<ProfileCode> profileCodes = new List<ProfileCode>(); | 433 final List<ProfileCode> profileCodes = new List<ProfileCode>(); |
434 final Map<ProfileFunction, int> callers = new Map<ProfileFunction, int>(); | |
435 final Map<ProfileFunction, int> callees = new Map<ProfileFunction, int>(); | |
436 | |
272 // Absolute ticks: | 437 // Absolute ticks: |
273 int exclusiveTicks = 0; | 438 int exclusiveTicks = 0; |
274 int inclusiveTicks = 0; | 439 int inclusiveTicks = 0; |
275 | 440 |
276 // Global percentages: | 441 // Global percentages: |
277 double normalizedExclusiveTicks = 0.0; | 442 double normalizedExclusiveTicks = 0.0; |
278 double normalizedInclusiveTicks = 0.0; | 443 double normalizedInclusiveTicks = 0.0; |
279 | 444 |
280 String formattedInclusiveTicks = ''; | 445 String formattedInclusiveTicks = ''; |
281 String formattedExclusiveTicks = ''; | 446 String formattedExclusiveTicks = ''; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
349 } else if (function.kind == FunctionKind.kNative) { | 514 } else if (function.kind == FunctionKind.kNative) { |
350 attribs.add('native'); | 515 attribs.add('native'); |
351 } else if (function.kind.isSynthetic()) { | 516 } else if (function.kind.isSynthetic()) { |
352 attribs.add('synthetic'); | 517 attribs.add('synthetic'); |
353 } else { | 518 } else { |
354 attribs.add('dart'); | 519 attribs.add('dart'); |
355 } | 520 } |
356 } | 521 } |
357 | 522 |
358 ProfileFunction.fromMap(this.profile, this.function, Map data) { | 523 ProfileFunction.fromMap(this.profile, this.function, Map data) { |
524 function.profile = this; | |
359 for (var codeIndex in data['codes']) { | 525 for (var codeIndex in data['codes']) { |
360 var profileCode = profile.codes[codeIndex]; | 526 var profileCode = profile.codes[codeIndex]; |
361 profileCodes.add(profileCode); | 527 profileCodes.add(profileCode); |
362 } | 528 } |
363 profileCodes.sort(_sortCodes); | 529 profileCodes.sort(_sortCodes); |
364 | 530 |
365 if (hasOptimizedCode()) { | 531 if (hasOptimizedCode()) { |
366 attributes.add('optimized'); | 532 attributes.add('optimized'); |
367 } | 533 } |
368 if (hasUnoptimizedCode()) { | 534 if (hasUnoptimizedCode()) { |
(...skipping 21 matching lines...) Expand all Loading... | |
390 profile.approximateMillisecondsForCount(inclusiveTicks)); | 556 profile.approximateMillisecondsForCount(inclusiveTicks)); |
391 | 557 |
392 formattedInclusiveTicks = | 558 formattedInclusiveTicks = |
393 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' | 559 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' |
394 '($inclusiveTicks)'; | 560 '($inclusiveTicks)'; |
395 | 561 |
396 formattedExclusiveTicks = | 562 formattedExclusiveTicks = |
397 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' | 563 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' |
398 '($exclusiveTicks)'; | 564 '($exclusiveTicks)'; |
399 } | 565 } |
566 | |
567 _noteCaller(ProfileFunction caller, int count) { | |
568 var r = callers[caller]; | |
569 if (r == null) { | |
570 r = 0; | |
571 } | |
572 callers[caller] = r + count; | |
573 } | |
574 | |
575 _noteCallee(ProfileFunction callee, int count) { | |
576 var r = callees[callee]; | |
577 if (r == null) { | |
578 r = 0; | |
579 } | |
580 callees[callee] = r + count; | |
581 } | |
400 } | 582 } |
401 | 583 |
402 | 584 |
403 class CpuProfile { | 585 class CpuProfile { |
404 final double MICROSECONDS_PER_SECOND = 1000000.0; | 586 final double MICROSECONDS_PER_SECOND = 1000000.0; |
405 final double displayThreshold = 0.0002; // 0.02%. | 587 final double displayThreshold = 0.0002; // 0.02%. |
406 | 588 |
407 Isolate isolate; | 589 Isolate isolate; |
408 | 590 |
409 int sampleCount = 0; | 591 int sampleCount = 0; |
410 int samplePeriod = 0; | 592 int samplePeriod = 0; |
411 double sampleRate = 0.0; | 593 double sampleRate = 0.0; |
412 | 594 |
413 int stackDepth = 0; | 595 int stackDepth = 0; |
414 | 596 |
415 double timeSpan = 0.0; | 597 double timeSpan = 0.0; |
416 | 598 |
417 final Map<String, List> tries = <String, List>{}; | 599 final Map<String, List> tries = <String, List>{}; |
418 final List<ProfileCode> codes = new List<ProfileCode>(); | 600 final List<ProfileCode> codes = new List<ProfileCode>(); |
601 bool _markedCodeCalls = false; | |
419 final List<ProfileFunction> functions = new List<ProfileFunction>(); | 602 final List<ProfileFunction> functions = new List<ProfileFunction>(); |
603 bool _markedFunctionCalls = false; | |
420 | 604 |
421 CodeCallTree loadCodeTree(String name) { | 605 CodeCallTree loadCodeTree(String name) { |
422 if (name == 'inclusive') { | 606 if (name == 'inclusive') { |
423 return _loadCodeTree(true, tries['inclusiveCodeTrie']); | 607 return _loadCodeTree(true, tries['inclusiveCodeTrie']); |
424 } else { | 608 } else { |
425 return _loadCodeTree(false, tries['exclusiveCodeTrie']); | 609 return _loadCodeTree(false, tries['exclusiveCodeTrie']); |
426 } | 610 } |
427 } | 611 } |
428 | 612 |
429 FunctionCallTree loadFunctionTree(String name) { | 613 FunctionCallTree loadFunctionTree(String name) { |
430 if (name == 'inclusive') { | 614 if (name == 'inclusive') { |
431 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']); | 615 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']); |
432 } else { | 616 } else { |
433 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']); | 617 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']); |
434 } | 618 } |
435 } | 619 } |
436 | 620 |
437 void clear() { | 621 markCodeCalls() { |
622 if (_markedCodeCalls) { | |
623 return; | |
624 } | |
625 _markedCodeCalls = true; | |
626 var tree = loadCodeTree('inclusive'); | |
627 tree._markCodeCalls(); | |
628 } | |
629 | |
630 markFunctionCalls() { | |
631 if (_markedFunctionCalls) { | |
632 return; | |
633 } | |
634 _markedFunctionCalls = true; | |
635 var tree = loadFunctionTree('inclusive'); | |
636 tree._markFunctionCalls(); | |
637 } | |
638 | |
639 clear() { | |
438 sampleCount = 0; | 640 sampleCount = 0; |
439 samplePeriod = 0; | 641 samplePeriod = 0; |
440 sampleRate = 0.0; | 642 sampleRate = 0.0; |
441 stackDepth = 0; | 643 stackDepth = 0; |
442 timeSpan = 0.0; | 644 timeSpan = 0.0; |
443 codes.clear(); | 645 codes.clear(); |
444 functions.clear(); | 646 functions.clear(); |
445 tries.clear(); | 647 tries.clear(); |
648 _markedCodeCalls = false; | |
649 _markedFunctionCalls = false; | |
446 } | 650 } |
447 | 651 |
448 void load(Isolate isolate, ServiceMap profile) { | 652 load(Isolate isolate, ServiceMap profile) { |
449 clear(); | 653 clear(); |
450 if ((isolate == null) || (profile == null)) { | 654 if ((isolate == null) || (profile == null)) { |
451 return; | 655 return; |
452 } | 656 } |
453 | 657 |
454 this.isolate = isolate; | 658 this.isolate = isolate; |
455 isolate.resetCachedProfileData(); | 659 isolate.resetCachedProfileData(); |
456 | 660 |
457 sampleCount = profile['sampleCount']; | 661 sampleCount = profile['sampleCount']; |
458 samplePeriod = profile['samplePeriod']; | 662 samplePeriod = profile['samplePeriod']; |
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
657 int approximateMillisecondsForCount(count) { | 861 int approximateMillisecondsForCount(count) { |
658 var MICROSECONDS_PER_MILLISECOND = 1000.0; | 862 var MICROSECONDS_PER_MILLISECOND = 1000.0; |
659 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; | 863 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; |
660 } | 864 } |
661 | 865 |
662 double approximateSecondsForCount(count) { | 866 double approximateSecondsForCount(count) { |
663 var MICROSECONDS_PER_SECOND = 1000000.0; | 867 var MICROSECONDS_PER_SECOND = 1000000.0; |
664 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; | 868 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; |
665 } | 869 } |
666 } | 870 } |
OLD | NEW |