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