mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Migrate tracing to null safety (#92849)
This commit is contained in:
parent
9038fac960
commit
f4d1bfedd2
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user