blob: e2e258e09daf3083cecddc3fb6cb8cc772ec3edc [file] [log] [blame]
Todd Volkert454db9d2017-11-09 21:45:31 -08001// Copyright 2015 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'dart:async';
6
7import 'base/file_system.dart';
8import 'base/utils.dart';
9import 'build_info.dart';
10import 'globals.dart';
11import 'vmservice.dart';
12
13// Names of some of the Timeline events we care about.
14const String _kFlutterEngineMainEnterEventName = 'FlutterEngineMainEnter';
15const String _kFrameworkInitEventName = 'Framework initialization';
16const String _kFirstUsefulFrameEventName = 'Widgets completed first useful frame';
17
18class Tracing {
19 Tracing(this.vmService);
20
21 static Future<Tracing> connect(Uri uri) async {
22 final VMService observatory = await VMService.connect(uri);
23 return new Tracing(observatory);
24 }
25
26 final VMService vmService;
27
28 Future<Null> startTracing() async {
29 await vmService.vm.setVMTimelineFlags(<String>['Compiler', 'Dart', 'Embedder', 'GC']);
30 await vmService.vm.clearVMTimeline();
31 }
32
33 /// Stops tracing; optionally wait for first frame.
34 Future<Map<String, dynamic>> stopTracingAndDownloadTimeline({
35 bool waitForFirstFrame: false
36 }) async {
37 Map<String, dynamic> timeline;
38
39 if (!waitForFirstFrame) {
40 // Stop tracing immediately and get the timeline
41 await vmService.vm.setVMTimelineFlags(<String>[]);
42 timeline = await vmService.vm.getVMTimeline();
43 } else {
44 final Completer<Null> whenFirstFrameRendered = new Completer<Null>();
45
46 vmService.onTimelineEvent.listen((ServiceEvent timelineEvent) {
47 final List<Map<String, dynamic>> events = timelineEvent.timelineEvents;
48 for (Map<String, dynamic> event in events) {
49 if (event['name'] == _kFirstUsefulFrameEventName)
50 whenFirstFrameRendered.complete();
51 }
52 });
53
54 await whenFirstFrameRendered.future.timeout(
55 const Duration(seconds: 10),
56 onTimeout: () {
57 printError(
58 'Timed out waiting for the first frame event. Either the '
59 'application failed to start, or the event was missed because '
60 '"flutter run" took too long to subscribe to timeline events.'
61 );
62 return null;
63 }
64 );
65
66 timeline = await vmService.vm.getVMTimeline();
67
68 await vmService.vm.setVMTimelineFlags(<String>[]);
69 }
70
71 return timeline;
72 }
73}
74
75/// Download the startup trace information from the given observatory client and
76/// store it to build/start_up_info.json.
77Future<Null> downloadStartupTrace(VMService observatory) async {
78 final String traceInfoFilePath = fs.path.join(getBuildDirectory(), 'start_up_info.json');
79 final File traceInfoFile = fs.file(traceInfoFilePath);
80
81 // Delete old startup data, if any.
82 if (await traceInfoFile.exists())
83 await traceInfoFile.delete();
84
85 // Create "build" directory, if missing.
86 if (!(await traceInfoFile.parent.exists()))
87 await traceInfoFile.parent.create();
88
89 final Tracing tracing = new Tracing(observatory);
90
91 final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline(
92 waitForFirstFrame: true
93 );
94
95 int extractInstantEventTimestamp(String eventName) {
96 final List<Map<String, dynamic>> events = timeline['traceEvents'];
97 final Map<String, dynamic> event = events.firstWhere(
98 (Map<String, dynamic> event) => event['name'] == eventName, orElse: () => null
99 );
100 return event == null ? null : event['ts'];
101 }
102
103 final int engineEnterTimestampMicros = extractInstantEventTimestamp(_kFlutterEngineMainEnterEventName);
104 final int frameworkInitTimestampMicros = extractInstantEventTimestamp(_kFrameworkInitEventName);
105 final int firstFrameTimestampMicros = extractInstantEventTimestamp(_kFirstUsefulFrameEventName);
106
107 if (engineEnterTimestampMicros == null) {
108 printTrace('Engine start event is missing in the timeline: $timeline');
109 throw 'Engine start event is missing in the timeline. Cannot compute startup time.';
110 }
111
112 if (firstFrameTimestampMicros == null) {
113 printTrace('First frame event is missing in the timeline: $timeline');
114 throw 'First frame event is missing in the timeline. Cannot compute startup time.';
115 }
116
117 final int timeToFirstFrameMicros = firstFrameTimestampMicros - engineEnterTimestampMicros;
118 final Map<String, dynamic> traceInfo = <String, dynamic>{
119 'engineEnterTimestampMicros': engineEnterTimestampMicros,
120 'timeToFirstFrameMicros': timeToFirstFrameMicros,
121 };
122
123 if (frameworkInitTimestampMicros != null) {
124 traceInfo['timeToFrameworkInitMicros'] = frameworkInitTimestampMicros - engineEnterTimestampMicros;
125 traceInfo['timeAfterFrameworkInitMicros'] = firstFrameTimestampMicros - frameworkInitTimestampMicros;
126 }
127
128 traceInfoFile.writeAsStringSync(toPrettyJson(traceInfo));
129
130 printStatus('Time to first frame: ${timeToFirstFrameMicros ~/ 1000}ms.');
131 printStatus('Saved startup trace info in ${traceInfoFile.path}.');
132}