| 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 a572279651bfbc9955ad15093a3343bacd6f571d..7e1443fc71d1e9af4d9199233c8af71f5a1c689d 100644
|
| --- a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
|
| +++ b/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
|
| @@ -4,8 +4,9 @@
|
|
|
| part of cpu_profiler;
|
|
|
| -abstract class CallTreeNode {
|
| - final List<CallTreeNode> children;
|
| +abstract class CallTreeNode<NodeT extends M.CallTreeNode>
|
| + implements M.CallTreeNode {
|
| + final List<NodeT> children;
|
| final int count;
|
| double get percentage => _percentage;
|
| double _percentage = 0.0;
|
| @@ -18,7 +19,8 @@ abstract class CallTreeNode {
|
| CallTreeNode(this.children, this.count);
|
| }
|
|
|
| -class CodeCallTreeNode extends CallTreeNode {
|
| +class CodeCallTreeNode extends CallTreeNode<CodeCallTreeNode>
|
| + implements M.CodeCallTreeNode {
|
| final ProfileCode profileCode;
|
|
|
| Object get profileData => profileCode;
|
| @@ -32,14 +34,15 @@ class CodeCallTreeNode extends CallTreeNode {
|
| }
|
| }
|
|
|
| -class CallTree {
|
| +class CallTree<NodeT extends CallTreeNode> {
|
| final bool inclusive;
|
| - final CallTreeNode root;
|
| + final NodeT root;
|
|
|
| CallTree(this.inclusive, this.root);
|
| }
|
|
|
| -class CodeCallTree extends CallTree {
|
| +class CodeCallTree extends CallTree<CodeCallTreeNode>
|
| + implements M.CodeCallTree {
|
| CodeCallTree(bool inclusive, CodeCallTreeNode root)
|
| : super(inclusive, root) {
|
| _setCodePercentage(null, root);
|
| @@ -305,7 +308,7 @@ class _FilteredCodeCallTreeBuilder extends _FilteredCallTreeBuilder {
|
| }
|
| }
|
|
|
| -class FunctionCallTree extends CallTree {
|
| +class FunctionCallTree extends CallTree implements M.FunctionCallTree {
|
| FunctionCallTree(bool inclusive, FunctionCallTreeNode root)
|
| : super(inclusive, root) {
|
| _setFunctionPercentage(null, root);
|
| @@ -370,7 +373,7 @@ class InlineIntervalTick {
|
| InlineIntervalTick(this.startAddress);
|
| }
|
|
|
| -class ProfileCode {
|
| +class ProfileCode implements M.ProfileCode {
|
| final CpuProfile profile;
|
| final Code code;
|
| int exclusiveTicks;
|
| @@ -488,7 +491,7 @@ class ProfileCode {
|
| }
|
| }
|
|
|
| -class ProfileFunction {
|
| +class ProfileFunction implements M.ProfileFunction {
|
| final CpuProfile profile;
|
| final ServiceFunction function;
|
| // List of compiled code objects containing this function.
|
| @@ -640,9 +643,10 @@ class ProfileFunction {
|
|
|
|
|
| // TODO(johnmccutchan): Rename to SampleProfile
|
| -class CpuProfile {
|
| - final double MICROSECONDS_PER_SECOND = 1000000.0;
|
| - final double displayThreshold = 0.0002; // 0.02%.
|
| +class CpuProfile extends M.SampleProfile {
|
| + static const double MICROSECONDS_PER_SECOND = 1000000.0;
|
| + static const double MICROSECONDS_PER_MILLISECOND = 1000.0;
|
| + static const double displayThreshold = 0.0002; // 0.02%.
|
|
|
| Isolate isolate;
|
|
|
| @@ -707,44 +711,83 @@ class CpuProfile {
|
| _builtFunctionCalls = false;
|
| }
|
|
|
| - load(Isolate isolate, ServiceMap profile) {
|
| - clear();
|
| - if ((isolate == null) || (profile == null)) {
|
| - return;
|
| - }
|
| + Future load(Isolate isolate, ServiceMap profile) async {
|
| + await loadProgress(isolate, profile).last;
|
| + }
|
|
|
| - this.isolate = isolate;
|
| - isolate.resetCachedProfileData();
|
| + static Future sleep([Duration duration = const Duration(microseconds: 0)]) {
|
| + final Completer completer = new Completer();
|
| + new Timer(duration, () => completer.complete() );
|
| + return completer.future;
|
| + }
|
|
|
| - sampleCount = profile['sampleCount'];
|
| - samplePeriod = profile['samplePeriod'];
|
| - sampleRate = (MICROSECONDS_PER_SECOND / samplePeriod);
|
| - stackDepth = profile['stackDepth'];
|
| - timeSpan = profile['timeSpan'];
|
| + Stream<double> loadProgress(Isolate isolate, ServiceMap profile) {
|
| + var progress = new StreamController<double>.broadcast();
|
|
|
| - // Process code table.
|
| - for (var codeRegion in profile['codes']) {
|
| - Code code = codeRegion['code'];
|
| - assert(code != null);
|
| - codes.add(new ProfileCode.fromMap(this, code, codeRegion));
|
| - }
|
| + (() 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 ((isolate == null) || (profile == null)) {
|
| + return;
|
| + }
|
|
|
| - // Process function table.
|
| - for (var profileFunction in profile['functions']) {
|
| - ServiceFunction function = profileFunction['function'];
|
| - assert(function != null);
|
| - functions.add(
|
| - new ProfileFunction.fromMap(this, function, profileFunction));
|
| - }
|
| + this.isolate = isolate;
|
| + isolate.resetCachedProfileData();
|
| +
|
| + sampleCount = profile['sampleCount'];
|
| + samplePeriod = profile['samplePeriod'];
|
| + sampleRate = (MICROSECONDS_PER_SECOND / samplePeriod);
|
| + stackDepth = profile['stackDepth'];
|
| + timeSpan = profile['timeSpan'];
|
|
|
| - 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']);
|
| + 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.
|
| @@ -918,12 +961,10 @@ class CpuProfile {
|
| }
|
|
|
| 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;
|
| }
|
| }
|
|
|