Flutter tool support for automatic saving of JIT compilation trace (#25301)
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index f7d83d7..bb5e622 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -33,6 +33,18 @@ ..addOption('route', help: 'Which route to load when running the app.', ) + ..addFlag('save-compilation-trace', + hide: !verboseHelp, + negatable: false, + help: 'Save runtime compilation trace to a file.\n' + 'Compilation trace will be saved to compilation.txt when \'flutter run\' exits. ' + 'This file contains a list of Dart symbols that were compiled by the runtime JIT ' + 'compiler up to that point. This file can be used in later --dynamic builds to ' + 'precompile some code by the offline compiler, thus reducing application startup ' + 'latency at the cost of larger application package.\n' + 'This flag is only allowed when running --dynamic --profile (recommended) or ' + '--debug mode.\n' + ) ..addOption('target-platform', defaultsTo: 'default', allowed: <String>['default', 'android-arm', 'android-arm64'], @@ -318,6 +330,11 @@ } } + if (argResults['save-compilation-trace'] && + getBuildMode() != BuildMode.debug && getBuildMode() != BuildMode.dynamicProfile) + throwToolExit('Error: --save-compilation-trace is only allowed when running ' + '--dynamic --profile (recommended) or --debug mode.'); + final List<FlutterDevice> flutterDevices = devices.map<FlutterDevice>((Device device) { return FlutterDevice( device, @@ -343,6 +360,7 @@ projectRootPath: argResults['project-root'], packagesFilePath: globalResults['packages'], dillOutputPath: argResults['output-dill'], + saveCompilationTrace: argResults['save-compilation-trace'], stayResident: stayResident, ipv6: ipv6, ); @@ -355,6 +373,7 @@ applicationBinary: applicationBinaryPath == null ? null : fs.file(applicationBinaryPath), + saveCompilationTrace: argResults['save-compilation-trace'], stayResident: stayResident, ipv6: ipv6, );
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 5be4191..5b3edf2 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -426,6 +426,7 @@ this.usesTerminalUI = true, String projectRootPath, String packagesFilePath, + this.saveCompilationTrace, this.stayResident, this.ipv6, }) { @@ -440,6 +441,7 @@ final String target; final DebuggingOptions debuggingOptions; final bool usesTerminalUI; + final bool saveCompilationTrace; final bool stayResident; final bool ipv6; final Completer<int> _finished = Completer<int>(); @@ -487,6 +489,8 @@ Future<void> stop() async { _stopped = true; + if (saveCompilationTrace) + await _saveCompilationTrace(); await stopEchoingDeviceLog(); await preStop(); return stopApp(); @@ -591,6 +595,35 @@ } } + Future<void> _saveCompilationTrace() async { + if (!supportsServiceProtocol) + return; + + for (FlutterDevice device in flutterDevices) { + for (FlutterView view in device.views) { + final int index = device.views.indexOf(view); + printStatus('Saving compilation trace for ' + '${device.device.name}${index == 0 ? '' :'/Isolate$index'}...'); + + List<int> buffer; + try { + buffer = await view.uiIsolate.flutterDumpCompilationTrace(); + assert(buffer != null); + } catch (error) { + printError('Error communicating with Flutter on the device: $error'); + continue; + } + + final File outputFile = fs.currentDirectory + .childFile('compilation${index == 0 ? '' : index}.txt'); + + outputFile.parent.createSync(recursive: true); + outputFile.writeAsBytesSync(buffer); + printStatus('Compilation trace written to ${fs.path.relative(outputFile.path)}.'); + } + } + } + Future<void> _debugTogglePlatform() async { await refreshViews(); final String from = await flutterDevices[0].views[0].uiIsolate.flutterPlatformOverride();
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart index 12e3478..f85a53f 100644 --- a/packages/flutter_tools/lib/src/run_cold.dart +++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -21,12 +21,14 @@ bool usesTerminalUI = true, this.traceStartup = false, this.applicationBinary, + bool saveCompilationTrace = false, bool stayResident = true, bool ipv6 = false, }) : super(devices, target: target, debuggingOptions: debuggingOptions, usesTerminalUI: usesTerminalUI, + saveCompilationTrace: saveCompilationTrace, stayResident: stayResident, ipv6: ipv6);
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index 07484de..d03e02f 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -61,6 +61,7 @@ String projectRootPath, String packagesFilePath, this.dillOutputPath, + bool saveCompilationTrace = false, bool stayResident = true, bool ipv6 = false, }) : super(devices, @@ -69,6 +70,7 @@ usesTerminalUI: usesTerminalUI, projectRootPath: projectRootPath, packagesFilePath: packagesFilePath, + saveCompilationTrace: saveCompilationTrace, stayResident: stayResident, ipv6: ipv6);
diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart index 4e355bf..a14f27e 100644 --- a/packages/flutter_tools/lib/src/vmservice.dart +++ b/packages/flutter_tools/lib/src/vmservice.dart
@@ -1312,6 +1312,14 @@ ); } + Future<List<int>> flutterDumpCompilationTrace() async { + final Map<String, dynamic> result = + await invokeFlutterExtensionRpcRaw('ext.flutter.dumpCompilationTrace'); + if (result != null && result['value'] is List<dynamic>) + return result['value'].cast<int>(); + return null; + } + // Application control extension methods. Future<Map<String, dynamic>> flutterExit() { return invokeFlutterExtensionRpcRaw(