Unverified Commit c5251cdc authored by Stanislav Baranov's avatar Stanislav Baranov Committed by GitHub

Flutter tool support for automatic saving of JIT compilation trace (#25301)

parent 30fb97be
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:convert' show json;
import 'dart:developer' as developer;
import 'dart:io' show exit;
import 'dart:ui' show dumpCompilationTrace;
import 'package:meta/meta.dart';
......@@ -117,6 +118,14 @@ abstract class BindingBase {
name: 'exit',
callback: _exitApplication,
);
registerServiceExtension(
name: 'dumpCompilationTrace',
callback: (Map<String, String> parameters) async {
return <String, dynamic> {
'value': dumpCompilationTrace(),
};
}
);
}
assert(() {
......
......@@ -527,6 +527,15 @@ void main() {
expect(binding.frameScheduled, isFalse);
});
test('Service extensions - dumpCompilationTrace', () async {
Map<String, dynamic> result;
result = await binding.testExtension('dumpCompilationTrace', <String, String>{});
final String trace = String.fromCharCodes(result['value']);
expect(trace, contains('dart:core,Object,Object.\n'));
expect(trace, contains('package:test_api/test_api.dart,::,test\n'));
expect(trace, contains('service_extensions_test.dart,::,main\n'));
});
test('Service extensions - posttest', () async {
// See widget_inspector_test.dart for tests of the ext.flutter.inspector
// service extensions included in this count.
......@@ -539,7 +548,7 @@ void main() {
// If you add a service extension... TEST IT! :-)
// ...then increment this number.
expect(binding.extensions.length, 23 + widgetInspectorExtensionCount);
expect(binding.extensions.length, 24 + widgetInspectorExtensionCount);
expect(console, isEmpty);
debugPrint = debugPrintThrottled;
......
......@@ -33,6 +33,18 @@ abstract class RunCommandBase extends FlutterCommand {
..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 @@ class RunCommand extends RunCommandBase {
}
}
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 @@ class RunCommand extends RunCommandBase {
projectRootPath: argResults['project-root'],
packagesFilePath: globalResults['packages'],
dillOutputPath: argResults['output-dill'],
saveCompilationTrace: argResults['save-compilation-trace'],
stayResident: stayResident,
ipv6: ipv6,
);
......@@ -355,6 +373,7 @@ class RunCommand extends RunCommandBase {
applicationBinary: applicationBinaryPath == null
? null
: fs.file(applicationBinaryPath),
saveCompilationTrace: argResults['save-compilation-trace'],
stayResident: stayResident,
ipv6: ipv6,
);
......
......@@ -426,6 +426,7 @@ abstract class ResidentRunner {
this.usesTerminalUI = true,
String projectRootPath,
String packagesFilePath,
this.saveCompilationTrace,
this.stayResident,
this.ipv6,
}) {
......@@ -440,6 +441,7 @@ abstract class ResidentRunner {
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 @@ abstract class ResidentRunner {
Future<void> stop() async {
_stopped = true;
if (saveCompilationTrace)
await _saveCompilationTrace();
await stopEchoingDeviceLog();
await preStop();
return stopApp();
......@@ -591,6 +595,35 @@ abstract class ResidentRunner {
}
}
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();
......
......@@ -21,12 +21,14 @@ class ColdRunner extends ResidentRunner {
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);
......
......@@ -61,6 +61,7 @@ class HotRunner extends ResidentRunner {
String projectRootPath,
String packagesFilePath,
this.dillOutputPath,
bool saveCompilationTrace = false,
bool stayResident = true,
bool ipv6 = false,
}) : super(devices,
......@@ -69,6 +70,7 @@ class HotRunner extends ResidentRunner {
usesTerminalUI: usesTerminalUI,
projectRootPath: projectRootPath,
packagesFilePath: packagesFilePath,
saveCompilationTrace: saveCompilationTrace,
stayResident: stayResident,
ipv6: ipv6);
......
......@@ -1312,6 +1312,14 @@ class Isolate extends ServiceObjectOwner {
);
}
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(
......
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