Unverified Commit f4d1bfed authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Migrate tracing to null safety (#92849)

parent 9038fac9
......@@ -2,11 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import 'base/common.dart';
......@@ -24,8 +21,8 @@ const String kFirstFrameRasterizedEventName = 'Rasterized first useful frame';
class Tracing {
Tracing({
@required this.vmService,
@required Logger logger,
required this.vmService,
required Logger logger,
}) : _logger = logger;
static const String firstUsefulFrameEventName = kFirstFrameRasterizedEventName;
......@@ -39,7 +36,7 @@ class Tracing {
}
/// Stops tracing; optionally wait for first frame.
Future<Map<String, dynamic>> stopTracingAndDownloadTimeline({
Future<Map<String, Object?>> stopTracingAndDownloadTimeline({
bool awaitFirstFrame = false,
}) async {
if (awaitFirstFrame) {
......@@ -62,9 +59,10 @@ class Tracing {
bool done = false;
final List<FlutterView> views = await vmService.getFlutterViews();
for (final FlutterView view in views) {
if (await vmService
final String? uiIsolateId = view.uiIsolate?.id;
if (uiIsolateId != null && await vmService
.flutterAlreadyPaintedFirstUsefulFrame(
isolateId: view.uiIsolate.id,
isolateId: uiIsolateId,
)) {
done = true;
break;
......@@ -80,14 +78,15 @@ class Tracing {
}
status.stop();
}
final vm_service.Response timeline = await vmService.getTimeline();
final vm_service.Response? timeline = await vmService.getTimeline();
await vmService.setTimelineFlags(<String>[]);
if (timeline == null) {
final Map<String, Object?>? timelineJson = timeline?.json;
if (timelineJson == null) {
throwToolExit(
'The device disconnected before the timeline could be retrieved.',
);
}
return timeline.json;
return timelineJson;
}
}
......@@ -95,8 +94,8 @@ class Tracing {
/// store it to `$output/start_up_info.json`.
Future<void> downloadStartupTrace(FlutterVmService vmService, {
bool awaitFirstFrame = true,
@required Logger logger,
@required Directory output,
required Logger logger,
required Directory output,
}) async {
final File traceInfoFile = output.childFile('start_up_info.json');
......@@ -110,33 +109,39 @@ Future<void> downloadStartupTrace(FlutterVmService vmService, {
final Tracing tracing = Tracing(vmService: vmService, logger: logger);
final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline(
final Map<String, Object?> timeline = await tracing.stopTracingAndDownloadTimeline(
awaitFirstFrame: awaitFirstFrame,
);
final File traceTimelineFile = output.childFile('start_up_timeline.json');
traceTimelineFile.writeAsStringSync(toPrettyJson(timeline));
int extractInstantEventTimestamp(String eventName) {
final List<Map<String, dynamic>> events =
List<Map<String, dynamic>>.from(timeline['traceEvents'] as List<dynamic>);
final Map<String, dynamic> event = events.firstWhere(
(Map<String, dynamic> event) => event['name'] == eventName, orElse: () => null,
);
return event == null ? null : (event['ts'] as int);
int? extractInstantEventTimestamp(String eventName) {
final List<Object?>? traceEvents = timeline['traceEvents'] as List<Object?>?;
if (traceEvents == null) {
return null;
}
final List<Map<String, Object?>> events = List<Map<String, Object?>>.from(traceEvents);
Map<String, Object?>? matchedEvent;
for (final Map<String, Object?> event in events) {
if (event['name'] == eventName) {
matchedEvent = event;
}
}
return matchedEvent == null ? null : (matchedEvent['ts'] as int?);
}
String message = 'No useful metrics were gathered.';
final int engineEnterTimestampMicros = extractInstantEventTimestamp(kFlutterEngineMainEnterEventName);
final int frameworkInitTimestampMicros = extractInstantEventTimestamp(kFrameworkInitEventName);
final int? engineEnterTimestampMicros = extractInstantEventTimestamp(kFlutterEngineMainEnterEventName);
final int? frameworkInitTimestampMicros = extractInstantEventTimestamp(kFrameworkInitEventName);
if (engineEnterTimestampMicros == null) {
logger.printTrace('Engine start event is missing in the timeline: $timeline');
throwToolExit('Engine start event is missing in the timeline. Cannot compute startup time.');
}
final Map<String, dynamic> traceInfo = <String, dynamic>{
final Map<String, Object?> traceInfo = <String, Object?>{
'engineEnterTimestampMicros': engineEnterTimestampMicros,
};
......@@ -147,8 +152,8 @@ Future<void> downloadStartupTrace(FlutterVmService vmService, {
}
if (awaitFirstFrame) {
final int firstFrameBuiltTimestampMicros = extractInstantEventTimestamp(kFirstFrameBuiltEventName);
final int firstFrameRasterizedTimestampMicros = extractInstantEventTimestamp(kFirstFrameRasterizedEventName);
final int? firstFrameBuiltTimestampMicros = extractInstantEventTimestamp(kFirstFrameBuiltEventName);
final int? firstFrameRasterizedTimestampMicros = extractInstantEventTimestamp(kFirstFrameRasterizedEventName);
if (firstFrameBuiltTimestampMicros == null || firstFrameRasterizedTimestampMicros == null) {
logger.printTrace('First frame events are missing in the timeline: $timeline');
throwToolExit('First frame events are missing in the timeline. Cannot compute startup time.');
......
......@@ -402,7 +402,7 @@ class FlutterView {
required this.uiIsolate,
});
factory FlutterView.parse(Map<String, Object> json) {
factory FlutterView.parse(Map<String, Object?> json) {
final Map<String, Object?>? rawIsolate = json['isolate'] as Map<String, Object?>?;
vm_service.IsolateRef? isolate;
if (rawIsolate != null) {
......@@ -822,11 +822,11 @@ class FlutterVmService {
// with cleaning up.
return <FlutterView>[];
}
final List<Object>? rawViews = response.json?['views'] as List<Object>?;
final List<Object?>? rawViews = response.json?['views'] as List<Object?>?;
final List<FlutterView> views = <FlutterView>[
if (rawViews != null)
for (final Object rawView in rawViews)
FlutterView.parse(rawView as Map<String, Object>)
for (final Map<String, Object?> rawView in rawViews.whereType<Map<String, Object?>>())
FlutterView.parse(rawView)
];
if (views.isNotEmpty || returnEarly) {
return views;
......
......@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/file_system.dart';
......@@ -90,19 +88,19 @@ void main() {
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFlutterEngineMainEnterEventName,
'ts': 0,
}),
})!,
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFrameworkInitEventName,
'ts': 1,
}),
})!,
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFirstFrameBuiltEventName,
'ts': 2,
}),
})!,
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFirstFrameRasterizedEventName,
'ts': 3,
}),
})!,
],
).toJson(),
),
......@@ -197,11 +195,11 @@ void main() {
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFlutterEngineMainEnterEventName,
'ts': 0,
}),
})!,
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFrameworkInitEventName,
'ts': 1,
}),
})!,
],
).toJson(),
),
......@@ -232,11 +230,11 @@ void main() {
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFlutterEngineMainEnterEventName,
'ts': 0,
}),
})!,
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFrameworkInitEventName,
'ts': 1,
}),
})!,
],
).toJson(),
),
......@@ -277,19 +275,19 @@ void main() {
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFlutterEngineMainEnterEventName,
'ts': 0,
}),
})!,
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFrameworkInitEventName,
'ts': 1,
}),
})!,
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFirstFrameBuiltEventName,
'ts': 2,
}),
})!,
vm_service.TimelineEvent.parse(<String, Object>{
'name': kFirstFrameRasterizedEventName,
'ts': 3,
}),
})!,
],
).toJson(),
),
......@@ -310,25 +308,25 @@ void main() {
logger: logger,
);
final Map<String, dynamic> expectedTimeline = <String, dynamic>{
final Map<String, Object> expectedTimeline = <String, Object>{
'type': 'Timeline',
'traceEvents': <dynamic>[
<String, dynamic>{
'traceEvents': <Object>[
<String, Object>{
'name': 'FlutterEngineMainEnter',
'ts': 0,
'type': 'TimelineEvent',
},
<String, dynamic>{
<String, Object>{
'name': 'Framework initialization',
'ts': 1,
'type': 'TimelineEvent',
},
<String, dynamic>{
<String, Object>{
'name': 'Widgets built first useful frame',
'ts': 2,
'type': 'TimelineEvent',
},
<String, dynamic>{
<String, Object>{
'name': 'Rasterized first useful frame',
'ts': 3,
'type': 'TimelineEvent',
......
......@@ -26,14 +26,14 @@ class FakeVmServiceHost {
), httpAddress: httpAddress, wsAddress: wsAddress);
_applyStreamListen();
_output.stream.listen((String data) {
final Map<String, Object> request = json.decode(data) as Map<String, Object>;
final Map<String, Object?> request = json.decode(data) as Map<String, Object?>;
if (_requests.isEmpty) {
throw Exception('Unexpected request: $request');
}
final FakeVmServiceRequest fakeRequest = _requests.removeAt(0) as FakeVmServiceRequest;
expect(request, isA<Map<String, Object>>()
.having((Map<String, Object> request) => request['method'], 'method', fakeRequest.method)
.having((Map<String, Object> request) => request['params'], 'args', fakeRequest.args)
expect(request, isA<Map<String, Object?>>()
.having((Map<String, Object?> request) => request['method'], 'method', fakeRequest.method)
.having((Map<String, Object?> request) => request['params'], 'args', fakeRequest.args)
);
if (fakeRequest.close) {
unawaited(_vmService.dispose());
......@@ -52,6 +52,7 @@ class FakeVmServiceHost {
'id': request['id'],
'error': <String, Object?>{
'code': fakeRequest.errorCode,
'message': 'error',
}
}));
}
......@@ -108,7 +109,7 @@ class FakeVmServiceRequest implements VmServiceExpectation {
/// standard response.
final int? errorCode;
final Map<String, Object>? args;
final Map<String, Object>? jsonResponse;
final Map<String, Object?>? jsonResponse;
@override
bool get isRequest => true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment