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 _recordCallerAndCalleesInner(CodeCallTreeNode caller, |
| 46 CodeCallTreeNode callee) { |
| 47 if (caller != null) { |
| 48 caller.profileCode._recordCallee(callee.profileCode, callee.count); |
| 49 callee.profileCode._recordCaller(caller.profileCode, callee.count); |
| 50 } |
| 51 |
| 52 for (var child in callee.children) { |
| 53 _recordCallerAndCalleesInner(callee, child); |
| 54 } |
| 55 } |
| 56 |
| 57 _recordCallerAndCallees() { |
| 58 for (var child in root.children) { |
| 59 _recordCallerAndCalleesInner(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 /// Predicate filter function. Returns true if path from root to [node] and all |
| 151 /// of [node]'s children should be added to the filtered tree. |
| 152 typedef bool FunctionCallTreeNodeFilter(FunctionCallTreeNode node); |
| 153 |
| 154 /// Build a filter version of a FunctionCallTree. |
| 155 class _FilteredFunctionCallTreeBuilder { |
| 156 /// The filter. |
| 157 final FunctionCallTreeNodeFilter filter; |
| 158 /// The unfiltered tree. |
| 159 final FunctionCallTree _unfilteredTree; |
| 160 /// The filtered tree (construct by [build]). |
| 161 final FunctionCallTree filtered; |
| 162 final List _currentPath = []; |
| 163 |
| 164 /// Construct a filtered tree builder using [filter] and [tree]. |
| 165 _FilteredFunctionCallTreeBuilder(this.filter, FunctionCallTree tree) |
| 166 : _unfilteredTree = tree, |
| 167 filtered = |
| 168 new FunctionCallTree( |
| 169 tree.inclusive, |
| 170 new FunctionCallTreeNode( |
| 171 tree.root.profileFunction, |
| 172 tree.root.count)); |
| 173 |
| 174 /// Build the filtered tree. |
| 175 build() { |
| 176 assert(filtered != null); |
| 177 assert(filter != null); |
| 178 assert(_unfilteredTree != null); |
| 179 _descend(_unfilteredTree.root); |
| 180 } |
| 181 |
| 182 FunctionCallTreeNode _findFunctionInChildren(FunctionCallTreeNode current, |
| 183 FunctionCallTreeNode needle) { |
| 184 for (var child in current.children) { |
| 185 if (child.profileFunction == needle.profileFunction) { |
| 186 return child; |
| 187 } |
| 188 } |
| 189 return null; |
| 190 } |
| 191 |
| 192 /// Add all nodes in [_currentPath]. |
| 193 FunctionCallTreeNode _addCurrentPath() { |
| 194 FunctionCallTreeNode current = filtered.root; |
| 195 // Tree root is always the first element of the current path. |
| 196 assert(_unfilteredTree.root == _currentPath[0]); |
| 197 // Assert that unfiltered tree's root and filtered tree's root are different
. |
| 198 assert(_unfilteredTree.root != current); |
| 199 for (var i = 1; i < _currentPath.length; i++) { |
| 200 // toAdd is from the unfiltered tree. |
| 201 var toAdd = _currentPath[i]; |
| 202 // See if we already have a node for toAdd in the filtered tree. |
| 203 var child = _findFunctionInChildren(current, toAdd); |
| 204 if (child == null) { |
| 205 // New node. |
| 206 child = new FunctionCallTreeNode(toAdd.profileFunction, toAdd.count); |
| 207 current.children.add(child); |
| 208 } |
| 209 current = child; |
| 210 assert(current.count == toAdd.count); |
| 211 } |
| 212 return current; |
| 213 } |
| 214 |
| 215 /// Starting at [current] append [next] and all of [next]'s sub-trees |
| 216 _appendTree(FunctionCallTreeNode current, FunctionCallTreeNode next) { |
| 217 if (next == null) { |
| 218 return; |
| 219 } |
| 220 var child = _findFunctionInChildren(current, next); |
| 221 if (child == null) { |
| 222 child = new FunctionCallTreeNode(next.profileFunction, next.count); |
| 223 current.children.add(child); |
| 224 } |
| 225 current = child; |
| 226 for (var nextChild in next.children) { |
| 227 _appendTree(current, nextChild); |
| 228 } |
| 229 } |
| 230 |
| 231 /// Add path from root to [child], [child], and all of [child]'s sub-trees |
| 232 /// to filtered tree. |
| 233 _addTree(FunctionCallTreeNode child) { |
| 234 var current = _addCurrentPath(); |
| 235 _appendTree(current, child); |
| 236 } |
| 237 |
| 238 /// Descend further into the tree. [current] is from the unfiltered tree. |
| 239 _descend(FunctionCallTreeNode current) { |
| 240 if (current == null) { |
| 241 return; |
| 242 } |
| 243 _currentPath.add(current); |
| 244 |
| 245 if (filter(current)) { |
| 246 // Filter matched. |
| 247 if (current.children.length == 0) { |
| 248 // Have no children. Add this path. |
| 249 _addTree(null); |
| 250 } else { |
| 251 // Add all child trees. |
| 252 for (var child in current.children) { |
| 253 _addTree(child); |
| 254 } |
| 255 } |
| 256 } else { |
| 257 // Did not match, descend to each child. |
| 258 for (var child in current.children) { |
| 259 _descend(child); |
| 260 } |
| 261 } |
| 262 |
| 263 var last = _currentPath.removeLast(); |
| 264 assert(current == last); |
| 265 } |
| 266 } |
| 267 |
132 class FunctionCallTree { | 268 class FunctionCallTree { |
133 final bool inclusive; | 269 final bool inclusive; |
134 final FunctionCallTreeNode root; | 270 final FunctionCallTreeNode root; |
135 FunctionCallTree(this.inclusive, this.root) { | 271 FunctionCallTree(this.inclusive, this.root) { |
136 _setFunctionPercentage(null, root); | 272 _setFunctionPercentage(null, root); |
137 } | 273 } |
138 | 274 |
| 275 FunctionCallTree filtered(FunctionCallTreeNodeFilter filter) { |
| 276 var treeFilter = new _FilteredFunctionCallTreeBuilder(filter, this); |
| 277 treeFilter.build(); |
| 278 _setFunctionPercentage(null, treeFilter.filtered.root); |
| 279 return treeFilter.filtered; |
| 280 } |
| 281 |
139 void _setFunctionPercentage(FunctionCallTreeNode parent, | 282 void _setFunctionPercentage(FunctionCallTreeNode parent, |
140 FunctionCallTreeNode node) { | 283 FunctionCallTreeNode node) { |
141 assert(node != null); | 284 assert(node != null); |
142 var parentPercentage = 1.0; | 285 var parentPercentage = 1.0; |
143 var parentCount = node.count; | 286 var parentCount = node.count; |
144 if (parent != null) { | 287 if (parent != null) { |
145 parentPercentage = parent._percentage; | 288 parentPercentage = parent._percentage; |
146 parentCount = parent.count; | 289 parentCount = parent.count; |
147 } | 290 } |
148 if (inclusive) { | 291 if (inclusive) { |
149 node._percentage = parentPercentage * (node.count / parentCount); | 292 node._percentage = parentPercentage * (node.count / parentCount); |
150 } else { | 293 } else { |
151 node._percentage = (node.count / parentCount); | 294 node._percentage = (node.count / parentCount); |
152 } | 295 } |
153 for (var child in node.children) { | 296 for (var child in node.children) { |
154 _setFunctionPercentage(node, child); | 297 _setFunctionPercentage(node, child); |
155 } | 298 } |
156 } | 299 } |
| 300 |
| 301 _markFunctionCallsInner(FunctionCallTreeNode caller, |
| 302 FunctionCallTreeNode callee) { |
| 303 if (caller != null) { |
| 304 caller.profileFunction._recordCallee(callee.profileFunction, callee.count)
; |
| 305 callee.profileFunction._recordCaller(caller.profileFunction, callee.count)
; |
| 306 } |
| 307 for (var child in callee.children) { |
| 308 _markFunctionCallsInner(callee, child); |
| 309 } |
| 310 } |
| 311 |
| 312 _markFunctionCalls() { |
| 313 for (var child in root.children) { |
| 314 _markFunctionCallsInner(null, child); |
| 315 } |
| 316 } |
157 } | 317 } |
158 | 318 |
159 class CodeTick { | 319 class CodeTick { |
160 final int exclusiveTicks; | 320 final int exclusiveTicks; |
161 final int inclusiveTicks; | 321 final int inclusiveTicks; |
162 CodeTick(this.exclusiveTicks, this.inclusiveTicks); | 322 CodeTick(this.exclusiveTicks, this.inclusiveTicks); |
163 } | 323 } |
164 | 324 |
165 class InlineIntervalTick { | 325 class InlineIntervalTick { |
166 final int startAddress; | 326 final int startAddress; |
(...skipping 12 matching lines...) Expand all Loading... |
179 double normalizedExclusiveTicks = 0.0; | 339 double normalizedExclusiveTicks = 0.0; |
180 double normalizedInclusiveTicks = 0.0; | 340 double normalizedInclusiveTicks = 0.0; |
181 final addressTicks = new Map<int, CodeTick>(); | 341 final addressTicks = new Map<int, CodeTick>(); |
182 final intervalTicks = new Map<int, InlineIntervalTick>(); | 342 final intervalTicks = new Map<int, InlineIntervalTick>(); |
183 String formattedInclusiveTicks = ''; | 343 String formattedInclusiveTicks = ''; |
184 String formattedExclusiveTicks = ''; | 344 String formattedExclusiveTicks = ''; |
185 String formattedExclusivePercent = ''; | 345 String formattedExclusivePercent = ''; |
186 String formattedCpuTime = ''; | 346 String formattedCpuTime = ''; |
187 String formattedOnStackTime = ''; | 347 String formattedOnStackTime = ''; |
188 final Set<String> attributes = new Set<String>(); | 348 final Set<String> attributes = new Set<String>(); |
| 349 final Map<ProfileCode, int> callers = new Map<ProfileCode, int>(); |
| 350 final Map<ProfileCode, int> callees = new Map<ProfileCode, int>(); |
189 | 351 |
190 void _processTicks(List<String> profileTicks) { | 352 void _processTicks(List<String> profileTicks) { |
191 assert(profileTicks != null); | 353 assert(profileTicks != null); |
192 assert((profileTicks.length % 3) == 0); | 354 assert((profileTicks.length % 3) == 0); |
193 for (var i = 0; i < profileTicks.length; i += 3) { | 355 for (var i = 0; i < profileTicks.length; i += 3) { |
194 var address = int.parse(profileTicks[i], radix:16); | 356 var address = int.parse(profileTicks[i], radix:16); |
195 var exclusive = int.parse(profileTicks[i + 1]); | 357 var exclusive = int.parse(profileTicks[i + 1]); |
196 var inclusive = int.parse(profileTicks[i + 2]); | 358 var inclusive = int.parse(profileTicks[i + 2]); |
197 var tick = new CodeTick(exclusive, inclusive); | 359 var tick = new CodeTick(exclusive, inclusive); |
198 addressTicks[address] = tick; | 360 addressTicks[address] = tick; |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
255 profile.approximateMillisecondsForCount(inclusiveTicks)); | 417 profile.approximateMillisecondsForCount(inclusiveTicks)); |
256 | 418 |
257 formattedInclusiveTicks = | 419 formattedInclusiveTicks = |
258 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' | 420 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' |
259 '($inclusiveTicks)'; | 421 '($inclusiveTicks)'; |
260 | 422 |
261 formattedExclusiveTicks = | 423 formattedExclusiveTicks = |
262 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' | 424 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' |
263 '($exclusiveTicks)'; | 425 '($exclusiveTicks)'; |
264 } | 426 } |
| 427 |
| 428 _recordCaller(ProfileCode caller, int count) { |
| 429 var r = callers[caller]; |
| 430 if (r == null) { |
| 431 r = 0; |
| 432 } |
| 433 callers[caller] = r + count; |
| 434 } |
| 435 |
| 436 _recordCallee(ProfileCode callee, int count) { |
| 437 var r = callees[callee]; |
| 438 if (r == null) { |
| 439 r = 0; |
| 440 } |
| 441 callees[callee] = r + count; |
| 442 } |
265 } | 443 } |
266 | 444 |
267 class ProfileFunction { | 445 class ProfileFunction { |
268 final CpuProfile profile; | 446 final CpuProfile profile; |
269 final ServiceFunction function; | 447 final ServiceFunction function; |
270 // List of compiled code objects containing this function. | 448 // List of compiled code objects containing this function. |
271 final List<ProfileCode> profileCodes = new List<ProfileCode>(); | 449 final List<ProfileCode> profileCodes = new List<ProfileCode>(); |
| 450 final Map<ProfileFunction, int> callers = new Map<ProfileFunction, int>(); |
| 451 final Map<ProfileFunction, int> callees = new Map<ProfileFunction, int>(); |
| 452 |
272 // Absolute ticks: | 453 // Absolute ticks: |
273 int exclusiveTicks = 0; | 454 int exclusiveTicks = 0; |
274 int inclusiveTicks = 0; | 455 int inclusiveTicks = 0; |
275 | 456 |
276 // Global percentages: | 457 // Global percentages: |
277 double normalizedExclusiveTicks = 0.0; | 458 double normalizedExclusiveTicks = 0.0; |
278 double normalizedInclusiveTicks = 0.0; | 459 double normalizedInclusiveTicks = 0.0; |
279 | 460 |
280 String formattedInclusiveTicks = ''; | 461 String formattedInclusiveTicks = ''; |
281 String formattedExclusiveTicks = ''; | 462 String formattedExclusiveTicks = ''; |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
349 } else if (function.kind == FunctionKind.kNative) { | 530 } else if (function.kind == FunctionKind.kNative) { |
350 attribs.add('native'); | 531 attribs.add('native'); |
351 } else if (function.kind.isSynthetic()) { | 532 } else if (function.kind.isSynthetic()) { |
352 attribs.add('synthetic'); | 533 attribs.add('synthetic'); |
353 } else { | 534 } else { |
354 attribs.add('dart'); | 535 attribs.add('dart'); |
355 } | 536 } |
356 } | 537 } |
357 | 538 |
358 ProfileFunction.fromMap(this.profile, this.function, Map data) { | 539 ProfileFunction.fromMap(this.profile, this.function, Map data) { |
| 540 function.profile = this; |
359 for (var codeIndex in data['codes']) { | 541 for (var codeIndex in data['codes']) { |
360 var profileCode = profile.codes[codeIndex]; | 542 var profileCode = profile.codes[codeIndex]; |
361 profileCodes.add(profileCode); | 543 profileCodes.add(profileCode); |
362 } | 544 } |
363 profileCodes.sort(_sortCodes); | 545 profileCodes.sort(_sortCodes); |
364 | 546 |
365 if (hasOptimizedCode()) { | 547 if (hasOptimizedCode()) { |
366 attributes.add('optimized'); | 548 attributes.add('optimized'); |
367 } | 549 } |
368 if (hasUnoptimizedCode()) { | 550 if (hasUnoptimizedCode()) { |
(...skipping 21 matching lines...) Expand all Loading... |
390 profile.approximateMillisecondsForCount(inclusiveTicks)); | 572 profile.approximateMillisecondsForCount(inclusiveTicks)); |
391 | 573 |
392 formattedInclusiveTicks = | 574 formattedInclusiveTicks = |
393 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' | 575 '${Utils.formatPercent(inclusiveTicks, profile.sampleCount)} ' |
394 '($inclusiveTicks)'; | 576 '($inclusiveTicks)'; |
395 | 577 |
396 formattedExclusiveTicks = | 578 formattedExclusiveTicks = |
397 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' | 579 '${Utils.formatPercent(exclusiveTicks, profile.sampleCount)} ' |
398 '($exclusiveTicks)'; | 580 '($exclusiveTicks)'; |
399 } | 581 } |
| 582 |
| 583 _recordCaller(ProfileFunction caller, int count) { |
| 584 var r = callers[caller]; |
| 585 if (r == null) { |
| 586 r = 0; |
| 587 } |
| 588 callers[caller] = r + count; |
| 589 } |
| 590 |
| 591 _recordCallee(ProfileFunction callee, int count) { |
| 592 var r = callees[callee]; |
| 593 if (r == null) { |
| 594 r = 0; |
| 595 } |
| 596 callees[callee] = r + count; |
| 597 } |
400 } | 598 } |
401 | 599 |
402 | 600 |
403 class CpuProfile { | 601 class CpuProfile { |
404 final double MICROSECONDS_PER_SECOND = 1000000.0; | 602 final double MICROSECONDS_PER_SECOND = 1000000.0; |
405 final double displayThreshold = 0.0002; // 0.02%. | 603 final double displayThreshold = 0.0002; // 0.02%. |
406 | 604 |
407 Isolate isolate; | 605 Isolate isolate; |
408 | 606 |
409 int sampleCount = 0; | 607 int sampleCount = 0; |
410 int samplePeriod = 0; | 608 int samplePeriod = 0; |
411 double sampleRate = 0.0; | 609 double sampleRate = 0.0; |
412 | 610 |
413 int stackDepth = 0; | 611 int stackDepth = 0; |
414 | 612 |
415 double timeSpan = 0.0; | 613 double timeSpan = 0.0; |
416 | 614 |
417 final Map<String, List> tries = <String, List>{}; | 615 final Map<String, List> tries = <String, List>{}; |
418 final List<ProfileCode> codes = new List<ProfileCode>(); | 616 final List<ProfileCode> codes = new List<ProfileCode>(); |
| 617 bool _builtCodeCalls = false; |
419 final List<ProfileFunction> functions = new List<ProfileFunction>(); | 618 final List<ProfileFunction> functions = new List<ProfileFunction>(); |
| 619 bool _builtFunctionCalls = false; |
420 | 620 |
421 CodeCallTree loadCodeTree(String name) { | 621 CodeCallTree loadCodeTree(String name) { |
422 if (name == 'inclusive') { | 622 if (name == 'inclusive') { |
423 return _loadCodeTree(true, tries['inclusiveCodeTrie']); | 623 return _loadCodeTree(true, tries['inclusiveCodeTrie']); |
424 } else { | 624 } else { |
425 return _loadCodeTree(false, tries['exclusiveCodeTrie']); | 625 return _loadCodeTree(false, tries['exclusiveCodeTrie']); |
426 } | 626 } |
427 } | 627 } |
428 | 628 |
429 FunctionCallTree loadFunctionTree(String name) { | 629 FunctionCallTree loadFunctionTree(String name) { |
430 if (name == 'inclusive') { | 630 if (name == 'inclusive') { |
431 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']); | 631 return _loadFunctionTree(true, tries['inclusiveFunctionTrie']); |
432 } else { | 632 } else { |
433 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']); | 633 return _loadFunctionTree(false, tries['exclusiveFunctionTrie']); |
434 } | 634 } |
435 } | 635 } |
436 | 636 |
437 void clear() { | 637 buildCodeCallerAndCallees() { |
| 638 if (_builtCodeCalls) { |
| 639 return; |
| 640 } |
| 641 _builtCodeCalls = true; |
| 642 var tree = loadCodeTree('inclusive'); |
| 643 tree._recordCallerAndCallees(); |
| 644 } |
| 645 |
| 646 buildFunctionCallerAndCallees() { |
| 647 if (_builtFunctionCalls) { |
| 648 return; |
| 649 } |
| 650 _builtFunctionCalls = true; |
| 651 var tree = loadFunctionTree('inclusive'); |
| 652 tree._markFunctionCalls(); |
| 653 } |
| 654 |
| 655 clear() { |
438 sampleCount = 0; | 656 sampleCount = 0; |
439 samplePeriod = 0; | 657 samplePeriod = 0; |
440 sampleRate = 0.0; | 658 sampleRate = 0.0; |
441 stackDepth = 0; | 659 stackDepth = 0; |
442 timeSpan = 0.0; | 660 timeSpan = 0.0; |
443 codes.clear(); | 661 codes.clear(); |
444 functions.clear(); | 662 functions.clear(); |
445 tries.clear(); | 663 tries.clear(); |
| 664 _builtCodeCalls = false; |
| 665 _builtFunctionCalls = false; |
446 } | 666 } |
447 | 667 |
448 void load(Isolate isolate, ServiceMap profile) { | 668 load(Isolate isolate, ServiceMap profile) { |
449 clear(); | 669 clear(); |
450 if ((isolate == null) || (profile == null)) { | 670 if ((isolate == null) || (profile == null)) { |
451 return; | 671 return; |
452 } | 672 } |
453 | 673 |
454 this.isolate = isolate; | 674 this.isolate = isolate; |
455 isolate.resetCachedProfileData(); | 675 isolate.resetCachedProfileData(); |
456 | 676 |
457 sampleCount = profile['sampleCount']; | 677 sampleCount = profile['sampleCount']; |
458 samplePeriod = profile['samplePeriod']; | 678 samplePeriod = profile['samplePeriod']; |
(...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
657 int approximateMillisecondsForCount(count) { | 877 int approximateMillisecondsForCount(count) { |
658 var MICROSECONDS_PER_MILLISECOND = 1000.0; | 878 var MICROSECONDS_PER_MILLISECOND = 1000.0; |
659 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; | 879 return (count * samplePeriod) ~/ MICROSECONDS_PER_MILLISECOND; |
660 } | 880 } |
661 | 881 |
662 double approximateSecondsForCount(count) { | 882 double approximateSecondsForCount(count) { |
663 var MICROSECONDS_PER_SECOND = 1000000.0; | 883 var MICROSECONDS_PER_SECOND = 1000000.0; |
664 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; | 884 return (count * samplePeriod) / MICROSECONDS_PER_SECOND; |
665 } | 885 } |
666 } | 886 } |
OLD | NEW |