mirror of
https://github.com/flutter/flutter
synced 2024-08-25 02:46:31 +00:00
[flutter_tools] Catch rpc error in render frame with raster stats (#144190)
Fixes https://github.com/flutter/flutter/issues/143010. This is intended to be cherrypicked into the 3.19 and 3.20 releases. Long-term, we should deprecate this feature: https://github.com/flutter/flutter/issues/144191
This commit is contained in:
parent
47b0ef8127
commit
f89b4f151e
|
@ -714,23 +714,30 @@ abstract class ResidentHandlers {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
final List<FlutterView> views = await device!.vmService!.getFlutterViews();
|
final List<FlutterView> views = await device!.vmService!.getFlutterViews();
|
||||||
for (final FlutterView view in views) {
|
try {
|
||||||
final Map<String, Object?>? rasterData =
|
for (final FlutterView view in views) {
|
||||||
await device.vmService!.renderFrameWithRasterStats(
|
final Map<String, Object?>? rasterData =
|
||||||
viewId: view.id,
|
await device.vmService!.renderFrameWithRasterStats(
|
||||||
uiIsolateId: view.uiIsolate!.id,
|
viewId: view.id,
|
||||||
);
|
uiIsolateId: view.uiIsolate!.id,
|
||||||
if (rasterData != null) {
|
);
|
||||||
final File tempFile = globals.fsUtils.getUniqueFile(
|
if (rasterData != null) {
|
||||||
globals.fs.currentDirectory,
|
final File tempFile = globals.fsUtils.getUniqueFile(
|
||||||
'flutter_jank_metrics',
|
globals.fs.currentDirectory,
|
||||||
'json',
|
'flutter_jank_metrics',
|
||||||
);
|
'json',
|
||||||
tempFile.writeAsStringSync(jsonEncode(rasterData), flush: true);
|
);
|
||||||
logger.printStatus('Wrote jank metrics to ${tempFile.absolute.path}');
|
tempFile.writeAsStringSync(jsonEncode(rasterData), flush: true);
|
||||||
} else {
|
logger.printStatus('Wrote jank metrics to ${tempFile.absolute.path}');
|
||||||
logger.printWarning('Unable to get jank metrics.');
|
} else {
|
||||||
|
logger.printWarning('Unable to get jank metrics.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} on vm_service.RPCError catch (err) {
|
||||||
|
if (err.code != RPCErrorCodes.kServerError || !err.message.contains('Raster status not supported on Impeller backend')) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
logger.printWarning('Unable to get jank metrics for Impeller renderer');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -48,13 +48,13 @@ const FakeVmServiceRequest failingCreateDevFSRequest = FakeVmServiceRequest(
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'fsName': 'test',
|
'fsName': 'test',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
);
|
);
|
||||||
|
|
||||||
const FakeVmServiceRequest failingDeleteDevFSRequest = FakeVmServiceRequest(
|
const FakeVmServiceRequest failingDeleteDevFSRequest = FakeVmServiceRequest(
|
||||||
method: '_deleteDevFS',
|
method: '_deleteDevFS',
|
||||||
args: <String, dynamic>{'fsName': 'test'},
|
args: <String, dynamic>{'fsName': 'test'},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
);
|
);
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
|
|
@ -264,14 +264,14 @@ void main() {
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kListViewsMethod,
|
method: kListViewsMethod,
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'streamCancel',
|
method: 'streamCancel',
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'streamId': 'Isolate',
|
'streamId': 'Isolate',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
], httpAddress: Uri.parse('http://localhost:1234'));
|
], httpAddress: Uri.parse('http://localhost:1234'));
|
||||||
|
|
||||||
|
@ -340,14 +340,14 @@ void main() {
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kListViewsMethod,
|
method: kListViewsMethod,
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'streamCancel',
|
method: 'streamCancel',
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'streamId': 'Isolate',
|
'streamId': 'Isolate',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
], httpAddress: Uri.parse('http://localhost:5678'));
|
], httpAddress: Uri.parse('http://localhost:5678'));
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,18 @@ final FakeVmServiceRequest listViews = FakeVmServiceRequest(
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const FakeVmServiceRequest renderFrameRasterStats = FakeVmServiceRequest(
|
||||||
|
method: kRenderFrameWithRasterStatsMethod,
|
||||||
|
args: <String, Object>{
|
||||||
|
'viewId': 'a',
|
||||||
|
'isolateId': '1',
|
||||||
|
},
|
||||||
|
error: FakeRPCError(
|
||||||
|
code: RPCErrorCodes.kServerError,
|
||||||
|
error: 'Raster status not supported on Impeller backend',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const FakeVmServiceRequest setAssetBundlePath = FakeVmServiceRequest(
|
const FakeVmServiceRequest setAssetBundlePath = FakeVmServiceRequest(
|
||||||
method: '_flutter.setAssetBundlePath',
|
method: '_flutter.setAssetBundlePath',
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
|
|
|
@ -333,6 +333,32 @@ void main() {
|
||||||
Usage: () => TestUsage(),
|
Usage: () => TestUsage(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
testUsingContext('ResidentRunner can handle an RPC exception from debugFrameJankMetrics', () => testbed.run(() async {
|
||||||
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||||
|
listViews,
|
||||||
|
listViews,
|
||||||
|
listViews,
|
||||||
|
renderFrameRasterStats,
|
||||||
|
]);
|
||||||
|
final Completer<DebugConnectionInfo> futureConnectionInfo = Completer<DebugConnectionInfo>.sync();
|
||||||
|
final Completer<void> futureAppStart = Completer<void>.sync();
|
||||||
|
unawaited(residentRunner.attach(
|
||||||
|
appStartedCompleter: futureAppStart,
|
||||||
|
connectionInfoCompleter: futureConnectionInfo,
|
||||||
|
enableDevTools: true,
|
||||||
|
));
|
||||||
|
await futureAppStart.future;
|
||||||
|
|
||||||
|
final bool result = await residentRunner.debugFrameJankMetrics();
|
||||||
|
expect(result, true);
|
||||||
|
expect((globals.flutterUsage as TestUsage).events, isEmpty);
|
||||||
|
expect(fakeAnalytics.sentEvents, isEmpty);
|
||||||
|
expect(fakeVmServiceHost?.hasRemainingExpectations, false);
|
||||||
|
expect((globals.logger as BufferLogger).warningText, contains('Unable to get jank metrics for Impeller renderer'));
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
Usage: () => TestUsage(),
|
||||||
|
}));
|
||||||
|
|
||||||
testUsingContext('ResidentRunner fails its operation if the device initialization is not complete', () => testbed.run(() async {
|
testUsingContext('ResidentRunner fails its operation if the device initialization is not complete', () => testbed.run(() async {
|
||||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||||
listViews,
|
listViews,
|
||||||
|
|
|
@ -973,7 +973,7 @@ void main() {
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kHotRestartServiceName,
|
method: kHotRestartServiceName,
|
||||||
// Failed response,
|
// Failed response,
|
||||||
errorCode: RPCErrorCodes.kInternalError,
|
error: FakeRPCError(code: RPCErrorCodes.kInternalError),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
setupMocks();
|
setupMocks();
|
||||||
|
|
|
@ -1125,7 +1125,7 @@ void main() {
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'ext.dwds.screenshot',
|
method: 'ext.dwds.screenshot',
|
||||||
// Failed response,
|
// Failed response,
|
||||||
errorCode: RPCErrorCodes.kInternalError,
|
error: FakeRPCError(code: RPCErrorCodes.kInternalError),
|
||||||
),
|
),
|
||||||
FakeVmServiceRequest(
|
FakeVmServiceRequest(
|
||||||
method: 'ext.flutter.debugAllowBanner',
|
method: 'ext.flutter.debugAllowBanner',
|
||||||
|
@ -1165,7 +1165,7 @@ void main() {
|
||||||
'enabled': 'true',
|
'enabled': 'true',
|
||||||
},
|
},
|
||||||
// Failed response,
|
// Failed response,
|
||||||
errorCode: RPCErrorCodes.kInternalError,
|
error: const FakeRPCError(code: RPCErrorCodes.kInternalError),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
|
@ -140,7 +140,7 @@ void main() {
|
||||||
...vmServiceSetup,
|
...vmServiceSetup,
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'getVMTimeline',
|
method: 'getVMTimeline',
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'setVMTimelineFlags',
|
method: 'setVMTimelineFlags',
|
||||||
|
|
|
@ -301,7 +301,7 @@ void main() {
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'isolateId': '1',
|
'isolateId': '1',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -320,7 +320,7 @@ void main() {
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'isolateId': '1',
|
'isolateId': '1',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -339,7 +339,7 @@ void main() {
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'isolateId': '1',
|
'isolateId': '1',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -358,7 +358,7 @@ void main() {
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'isolateId': '1',
|
'isolateId': '1',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -377,7 +377,7 @@ void main() {
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'isolateId': '1',
|
'isolateId': '1',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -396,7 +396,7 @@ void main() {
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'isolateId': '1',
|
'isolateId': '1',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kMethodNotFound,
|
error: FakeRPCError(code: RPCErrorCodes.kMethodNotFound),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -436,26 +436,26 @@ void main() {
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'viewId': '1234',
|
'viewId': '1234',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kListViewsMethod,
|
method: kListViewsMethod,
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kScreenshotSkpMethod,
|
method: kScreenshotSkpMethod,
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'setVMTimelineFlags',
|
method: 'setVMTimelineFlags',
|
||||||
args: <String, dynamic>{
|
args: <String, dynamic>{
|
||||||
'recordedStreams': <String>['test'],
|
'recordedStreams': <String>['test'],
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'getVMTimeline',
|
method: 'getVMTimeline',
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kRenderFrameWithRasterStatsMethod,
|
method: kRenderFrameWithRasterStatsMethod,
|
||||||
|
@ -463,7 +463,7 @@ void main() {
|
||||||
'viewId': '1',
|
'viewId': '1',
|
||||||
'isolateId': '12',
|
'isolateId': '12',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -495,9 +495,13 @@ void main() {
|
||||||
testWithoutContext('getIsolateOrNull returns null if service disappears ', () async {
|
testWithoutContext('getIsolateOrNull returns null if service disappears ', () async {
|
||||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
||||||
requests: <VmServiceExpectation>[
|
requests: <VmServiceExpectation>[
|
||||||
const FakeVmServiceRequest(method: 'getIsolate', args: <String, Object>{
|
const FakeVmServiceRequest(
|
||||||
'isolateId': 'isolate/123',
|
method: 'getIsolate',
|
||||||
}, errorCode: RPCErrorCodes.kServiceDisappeared),
|
args: <String, Object>{
|
||||||
|
'isolateId': 'isolate/123',
|
||||||
|
},
|
||||||
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -702,7 +706,7 @@ void main() {
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'isolateId': '1',
|
'isolateId': '1',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
// Assume a different isolate returns.
|
// Assume a different isolate returns.
|
||||||
FakeVmServiceStreamResponse(
|
FakeVmServiceStreamResponse(
|
||||||
|
@ -734,7 +738,7 @@ void main() {
|
||||||
'streamId': 'Isolate',
|
'streamId': 'Isolate',
|
||||||
},
|
},
|
||||||
// Stream already subscribed - https://github.com/dart-lang/sdk/blob/main/runtime/vm/service/service.md#streamlisten
|
// Stream already subscribed - https://github.com/dart-lang/sdk/blob/main/runtime/vm/service/service.md#streamlisten
|
||||||
errorCode: 103,
|
error: FakeRPCError(code: 103),
|
||||||
),
|
),
|
||||||
listViewsRequest,
|
listViewsRequest,
|
||||||
FakeVmServiceRequest(
|
FakeVmServiceRequest(
|
||||||
|
@ -802,14 +806,14 @@ void main() {
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: kListViewsMethod,
|
method: kListViewsMethod,
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
const FakeVmServiceRequest(
|
const FakeVmServiceRequest(
|
||||||
method: 'streamCancel',
|
method: 'streamCancel',
|
||||||
args: <String, Object>{
|
args: <String, Object>{
|
||||||
'streamId': 'Isolate',
|
'streamId': 'Isolate',
|
||||||
},
|
},
|
||||||
errorCode: RPCErrorCodes.kServiceDisappeared,
|
error: FakeRPCError(code: RPCErrorCodes.kServiceDisappeared),
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
|
@ -39,7 +39,7 @@ class FakeVmServiceHost {
|
||||||
expect(_requests, isEmpty);
|
expect(_requests, isEmpty);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (fakeRequest.errorCode == null) {
|
if (fakeRequest.error == null) {
|
||||||
_input.add(json.encode(<String, Object?>{
|
_input.add(json.encode(<String, Object?>{
|
||||||
'jsonrpc': '2.0',
|
'jsonrpc': '2.0',
|
||||||
'id': request['id'],
|
'id': request['id'],
|
||||||
|
@ -50,8 +50,8 @@ class FakeVmServiceHost {
|
||||||
'jsonrpc': '2.0',
|
'jsonrpc': '2.0',
|
||||||
'id': request['id'],
|
'id': request['id'],
|
||||||
'error': <String, Object?>{
|
'error': <String, Object?>{
|
||||||
'code': fakeRequest.errorCode,
|
'code': fakeRequest.error!.code,
|
||||||
'message': 'error',
|
'message': fakeRequest.error!.error,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -90,12 +90,22 @@ abstract class VmServiceExpectation {
|
||||||
bool get isRequest;
|
bool get isRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeRPCError {
|
||||||
|
const FakeRPCError({
|
||||||
|
required this.code,
|
||||||
|
this.error = 'error',
|
||||||
|
});
|
||||||
|
|
||||||
|
final int code;
|
||||||
|
final String error;
|
||||||
|
}
|
||||||
|
|
||||||
class FakeVmServiceRequest implements VmServiceExpectation {
|
class FakeVmServiceRequest implements VmServiceExpectation {
|
||||||
const FakeVmServiceRequest({
|
const FakeVmServiceRequest({
|
||||||
required this.method,
|
required this.method,
|
||||||
this.args = const <String, Object?>{},
|
this.args = const <String, Object?>{},
|
||||||
this.jsonResponse,
|
this.jsonResponse,
|
||||||
this.errorCode,
|
this.error,
|
||||||
this.close = false,
|
this.close = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -106,7 +116,7 @@ class FakeVmServiceRequest implements VmServiceExpectation {
|
||||||
|
|
||||||
/// If non-null, the error code for a [vm_service.RPCError] in place of a
|
/// If non-null, the error code for a [vm_service.RPCError] in place of a
|
||||||
/// standard response.
|
/// standard response.
|
||||||
final int? errorCode;
|
final FakeRPCError? error;
|
||||||
final Map<String, Object?>? args;
|
final Map<String, Object?>? args;
|
||||||
final Map<String, Object?>? jsonResponse;
|
final Map<String, Object?>? jsonResponse;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue