Unverified Commit 01e3496a authored by Vyacheslav Egorov's avatar Vyacheslav Egorov Committed by GitHub

Introduce --report-timings flag for flutter build aot command. (#30032)

This flag makes flutter build aot report timings for substeps (e.g.
frontend compilation and gen_snapshot) in a machine readable form.
parent a39b5542
...@@ -70,6 +70,13 @@ class GenSnapshot { ...@@ -70,6 +70,13 @@ class GenSnapshot {
} }
class AOTSnapshotter { class AOTSnapshotter {
AOTSnapshotter({this.reportTimings = false});
/// If true then AOTSnapshotter would report timings for individual building
/// steps (Dart front-end parsing and snapshot generation) in a stable
/// machine readable form. See [AOTSnapshotter._timedStep].
final bool reportTimings;
/// Builds an architecture-specific ahead-of-time compiled snapshot of the specified script. /// Builds an architecture-specific ahead-of-time compiled snapshot of the specified script.
Future<int> build({ Future<int> build({
@required TargetPlatform platform, @required TargetPlatform platform,
...@@ -197,11 +204,11 @@ class AOTSnapshotter { ...@@ -197,11 +204,11 @@ class AOTSnapshotter {
} }
final SnapshotType snapshotType = SnapshotType(platform, buildMode); final SnapshotType snapshotType = SnapshotType(platform, buildMode);
final int genSnapshotExitCode = await genSnapshot.run( final int genSnapshotExitCode = await _timedStep('gen_snapshot', () => genSnapshot.run(
snapshotType: snapshotType, snapshotType: snapshotType,
additionalArgs: genSnapshotArgs, additionalArgs: genSnapshotArgs,
iosArch: iosArch, iosArch: iosArch,
); ));
if (genSnapshotExitCode != 0) { if (genSnapshotExitCode != 0) {
printError('Dart snapshot generator failed with exit code $genSnapshotExitCode'); printError('Dart snapshot generator failed with exit code $genSnapshotExitCode');
return genSnapshotExitCode; return genSnapshotExitCode;
...@@ -309,7 +316,7 @@ class AOTSnapshotter { ...@@ -309,7 +316,7 @@ class AOTSnapshotter {
final String depfilePath = fs.path.join(outputPath, 'kernel_compile.d'); final String depfilePath = fs.path.join(outputPath, 'kernel_compile.d');
final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject); final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject);
final CompilerOutput compilerOutput = await kernelCompiler.compile( final CompilerOutput compilerOutput = await _timedStep('frontend', () => kernelCompiler.compile(
sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath), sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath),
mainPath: mainPath, mainPath: mainPath,
packagesPath: packagesPath, packagesPath: packagesPath,
...@@ -323,7 +330,7 @@ class AOTSnapshotter { ...@@ -323,7 +330,7 @@ class AOTSnapshotter {
aot: true, aot: true,
trackWidgetCreation: trackWidgetCreation, trackWidgetCreation: trackWidgetCreation,
targetProductVm: buildMode == BuildMode.release, targetProductVm: buildMode == BuildMode.release,
); ));
// Write path to frontend_server, since things need to be re-generated when that changes. // Write path to frontend_server, since things need to be re-generated when that changes.
final String frontendPath = artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk); final String frontendPath = artifacts.getArtifactPath(Artifact.frontendServerSnapshotForEngineDartSdk);
...@@ -345,6 +352,20 @@ class AOTSnapshotter { ...@@ -345,6 +352,20 @@ class AOTSnapshotter {
String _getPackagePath(PackageMap packageMap, String package) { String _getPackagePath(PackageMap packageMap, String package) {
return fs.path.dirname(fs.path.fromUri(packageMap.map[package])); return fs.path.dirname(fs.path.fromUri(packageMap.map[package]));
} }
/// This method is used to measure duration of an action and emit it into
/// verbose output from flutter_tool for other tools (e.g. benchmark runner)
/// to find.
/// Important: external performance tracking tools expect format of this
/// output to be stable.
Future<T> _timedStep<T>(String marker, FutureOr<T> Function() action) async {
final Stopwatch sw = Stopwatch()..start();
final T value = await action();
if (reportTimings) {
printStatus('$marker(RunTime): ${sw.elapsedMilliseconds} ms.');
}
return value;
}
} }
class JITSnapshotter { class JITSnapshotter {
......
...@@ -33,6 +33,11 @@ class BuildAotCommand extends BuildSubCommand { ...@@ -33,6 +33,11 @@ class BuildAotCommand extends BuildSubCommand {
defaultsTo: false, defaultsTo: false,
help: 'Compile to a *.so file (requires NDK when building for Android).', help: 'Compile to a *.so file (requires NDK when building for Android).',
) )
..addFlag('report-timings',
negatable: false,
defaultsTo: false,
help: 'Report timing information about build steps in machine readable form,',
)
..addMultiOption('ios-arch', ..addMultiOption('ios-arch',
splitCommas: true, splitCommas: true,
defaultsTo: defaultIOSArchs.map<String>(getNameForIOSArch), defaultsTo: defaultIOSArchs.map<String>(getNameForIOSArch),
...@@ -73,9 +78,10 @@ class BuildAotCommand extends BuildSubCommand { ...@@ -73,9 +78,10 @@ class BuildAotCommand extends BuildSubCommand {
); );
} }
final String outputPath = argResults['output-dir'] ?? getAotBuildDirectory(); final String outputPath = argResults['output-dir'] ?? getAotBuildDirectory();
final bool reportTimings = argResults['report-timings'];
try { try {
String mainPath = findMainDartFile(targetFile); String mainPath = findMainDartFile(targetFile);
final AOTSnapshotter snapshotter = AOTSnapshotter(); final AOTSnapshotter snapshotter = AOTSnapshotter(reportTimings: reportTimings);
// Compile to kernel. // Compile to kernel.
mainPath = await snapshotter.compileKernel( mainPath = await snapshotter.compileKernel(
......
...@@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/build.dart'; ...@@ -13,6 +13,7 @@ import 'package:flutter_tools/src/base/build.dart';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/process.dart'; import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/version.dart'; import 'package:flutter_tools/src/version.dart';
...@@ -87,9 +88,11 @@ void main() { ...@@ -87,9 +88,11 @@ void main() {
_FakeGenSnapshot genSnapshot; _FakeGenSnapshot genSnapshot;
MemoryFileSystem fs; MemoryFileSystem fs;
AOTSnapshotter snapshotter; AOTSnapshotter snapshotter;
AOTSnapshotter snapshotterWithTimings;
MockAndroidSdk mockAndroidSdk; MockAndroidSdk mockAndroidSdk;
MockArtifacts mockArtifacts; MockArtifacts mockArtifacts;
MockXcode mockXcode; MockXcode mockXcode;
BufferLogger bufferLogger;
setUp(() async { setUp(() async {
fs = MemoryFileSystem(); fs = MemoryFileSystem();
...@@ -105,9 +108,11 @@ void main() { ...@@ -105,9 +108,11 @@ void main() {
genSnapshot = _FakeGenSnapshot(); genSnapshot = _FakeGenSnapshot();
snapshotter = AOTSnapshotter(); snapshotter = AOTSnapshotter();
snapshotterWithTimings = AOTSnapshotter(reportTimings: true);
mockAndroidSdk = MockAndroidSdk(); mockAndroidSdk = MockAndroidSdk();
mockArtifacts = MockArtifacts(); mockArtifacts = MockArtifacts();
mockXcode = MockXcode(); mockXcode = MockXcode();
bufferLogger = BufferLogger();
for (BuildMode mode in BuildMode.values) { for (BuildMode mode in BuildMode.values) {
when(mockArtifacts.getArtifactPath(Artifact.snapshotDart, any, mode)).thenReturn(kSnapshotDart); when(mockArtifacts.getArtifactPath(Artifact.snapshotDart, any, mode)).thenReturn(kSnapshotDart);
} }
...@@ -119,6 +124,7 @@ void main() { ...@@ -119,6 +124,7 @@ void main() {
FileSystem: () => fs, FileSystem: () => fs,
GenSnapshot: () => genSnapshot, GenSnapshot: () => genSnapshot,
Xcode: () => mockXcode, Xcode: () => mockXcode,
Logger: () => bufferLogger,
}; };
testUsingContext('iOS debug AOT snapshot is invalid', () async { testUsingContext('iOS debug AOT snapshot is invalid', () async {
...@@ -491,6 +497,36 @@ void main() { ...@@ -491,6 +497,36 @@ void main() {
]); ]);
}, overrides: contextOverrides); }, overrides: contextOverrides);
testUsingContext('reports timing', () async {
fs.file('main.dill').writeAsStringSync('binary magic');
final String outputPath = fs.path.join('build', 'foo');
fs.directory(outputPath).createSync(recursive: true);
genSnapshot.outputs = <String, String>{
fs.path.join(outputPath, 'vm_snapshot_data'): '',
fs.path.join(outputPath, 'isolate_snapshot_data'): '',
fs.path.join(outputPath, 'vm_snapshot_instr'): '',
fs.path.join(outputPath, 'isolate_snapshot_instr'): '',
};
final RunResult successResult = RunResult(ProcessResult(1, 0, '', ''), <String>['command name', 'arguments...']);
when(xcode.cc(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
when(xcode.clang(any)).thenAnswer((_) => Future<RunResult>.value(successResult));
final int genSnapshotExitCode = await snapshotterWithTimings.build(
platform: TargetPlatform.android_arm,
buildMode: BuildMode.release,
mainPath: 'main.dill',
packagesPath: '.packages',
outputPath: outputPath,
buildSharedLibrary: false,
);
expect(genSnapshotExitCode, 0);
expect(genSnapshot.callCount, 1);
expect(bufferLogger.statusText, matches(RegExp(r'gen_snapshot\(RunTime\): \d+ ms.')));
}, overrides: contextOverrides);
}); });
group('Snapshotter - JIT', () { group('Snapshotter - JIT', () {
......
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