[flutter_tool] Send build timing to analytics (#34049)
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 9216797..62f4475 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:crypto/crypto.dart'; import 'package:meta/meta.dart'; import '../android/android_sdk.dart'; @@ -352,6 +353,7 @@ timeout: timeoutConfiguration.slowOperation, multilineOutput: true, ); + final Stopwatch sw = Stopwatch()..start(); final int exitCode = await runCommandAndStreamOutput( <String>[fs.file(gradle).absolute.path, 'build'], workingDirectory: project.android.hostAppGradleRoot.path, @@ -359,6 +361,7 @@ environment: _gradleEnv, ); status.stop(); + flutterUsage.sendTiming('build', 'gradle-v1', Duration(milliseconds: sw.elapsedMilliseconds)); if (exitCode != 0) throwToolExit('Gradle build failed: $exitCode', exitCode: exitCode); @@ -366,6 +369,25 @@ printStatus('Built ${fs.path.relative(project.android.gradleAppOutV1File.path)}.'); } +String _hex(List<int> bytes) { + final StringBuffer result = StringBuffer(); + for (int part in bytes) + result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); + return result.toString(); +} + +String _calculateSha(File file) { + final Stopwatch sw = Stopwatch()..start(); + final List<int> bytes = file.readAsBytesSync(); + printTrace('calculateSha: reading file took ${sw.elapsedMilliseconds}us'); + flutterUsage.sendTiming('build', 'apk-sha-read', Duration(milliseconds: sw.elapsedMilliseconds)); + sw.reset(); + final String sha = _hex(sha1.convert(bytes).bytes); + printTrace('calculateSha: computing sha took ${sw.elapsedMilliseconds}us'); + flutterUsage.sendTiming('build', 'apk-sha-calc', Duration(milliseconds: sw.elapsedMilliseconds)); + return sha; +} + Future<void> _buildGradleProjectV2( FlutterProject flutterProject, String gradle, @@ -439,30 +461,35 @@ command.add(assembleTask); bool potentialAndroidXFailure = false; - final int exitCode = await runCommandAndStreamOutput( - command, - workingDirectory: flutterProject.android.hostAppGradleRoot.path, - allowReentrantFlutter: true, - environment: _gradleEnv, - // TODO(mklim): if AndroidX warnings are no longer required, this - // mapFunction and all its associated variabled can be replaced with just - // `filter: ndkMessagefilter`. - mapFunction: (String line) { - final bool isAndroidXPluginWarning = androidXPluginWarningRegex.hasMatch(line); - if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) { - potentialAndroidXFailure = true; - } - // Always print the full line in verbose mode. - if (logger.isVerbose) { - return line; - } else if (isAndroidXPluginWarning || !ndkMessageFilter.hasMatch(line)) { - return null; - } + final Stopwatch sw = Stopwatch()..start(); + int exitCode = 1; + try { + exitCode = await runCommandAndStreamOutput( + command, + workingDirectory: flutterProject.android.hostAppGradleRoot.path, + allowReentrantFlutter: true, + environment: _gradleEnv, + // TODO(mklim): if AndroidX warnings are no longer required, this + // mapFunction and all its associated variabled can be replaced with just + // `filter: ndkMessagefilter`. + mapFunction: (String line) { + final bool isAndroidXPluginWarning = androidXPluginWarningRegex.hasMatch(line); + if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) { + potentialAndroidXFailure = true; + } + // Always print the full line in verbose mode. + if (logger.isVerbose) { + return line; + } else if (isAndroidXPluginWarning || !ndkMessageFilter.hasMatch(line)) { + return null; + } - return line; - }, - ); - status.stop(); + return line; + }, + ); + } finally { + status.stop(); + } if (exitCode != 0) { if (potentialAndroidXFailure) { @@ -478,6 +505,7 @@ } throwToolExit('Gradle task $assembleTask failed with exit code $exitCode', exitCode: exitCode); } + flutterUsage.sendTiming('build', 'gradle-v2', Duration(milliseconds: sw.elapsedMilliseconds)); if (!isBuildingBundle) { final File apkFile = _findApkFile(project, buildInfo); @@ -488,7 +516,7 @@ printTrace('calculateSha: ${project.apkDirectory}/app.apk'); final File apkShaFile = project.apkDirectory.childFile('app.apk.sha1'); - apkShaFile.writeAsStringSync(calculateSha(apkFile)); + apkShaFile.writeAsStringSync(_calculateSha(apkFile)); String appSize; if (buildInfo.mode == BuildMode.debug) {
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index 418f685..4b46edb 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -15,6 +15,7 @@ import '../globals.dart'; import '../macos/xcode.dart'; import '../project.dart'; +import '../usage.dart'; import 'context.dart'; import 'file_system.dart'; import 'fingerprint.dart'; @@ -194,7 +195,9 @@ // } final SnapshotType snapshotType = SnapshotType(platform, buildMode); - final int genSnapshotExitCode = await _timedStep('snapshot(CompileTime)', () => genSnapshot.run( + final int genSnapshotExitCode = + await _timedStep('snapshot(CompileTime)', 'aot-snapshot', + () => genSnapshot.run( snapshotType: snapshotType, additionalArgs: genSnapshotArgs, iosArch: iosArch, @@ -281,7 +284,9 @@ final String depfilePath = fs.path.join(outputPath, 'kernel_compile.d'); final KernelCompiler kernelCompiler = await kernelCompilerFactory.create(flutterProject); - final CompilerOutput compilerOutput = await _timedStep('frontend(CompileTime)', () => kernelCompiler.compile( + final CompilerOutput compilerOutput = + await _timedStep('frontend(CompileTime)', 'aot-kernel', + () => kernelCompiler.compile( sdkRoot: artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, mode: buildMode), mainPath: mainPath, packagesPath: packagesPath, @@ -323,12 +328,13 @@ /// 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 { + Future<T> _timedStep<T>(String marker, String analyticsVar, FutureOr<T> Function() action) async { final Stopwatch sw = Stopwatch()..start(); final T value = await action(); if (reportTimings) { printStatus('$marker: ${sw.elapsedMilliseconds} ms.'); } + flutterUsage.sendTiming('build', analyticsVar, Duration(milliseconds: sw.elapsedMilliseconds)); return value; } }
diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart index d0eea9c..1b6d2c3 100644 --- a/packages/flutter_tools/lib/src/base/utils.dart +++ b/packages/flutter_tools/lib/src/base/utils.dart
@@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:math' show Random, max; -import 'package:crypto/crypto.dart'; import 'package:intl/intl.dart'; import '../convert.dart'; @@ -53,24 +52,6 @@ return botDetector.isRunningOnBot; } -String hex(List<int> bytes) { - final StringBuffer result = StringBuffer(); - for (int part in bytes) - result.write('${part < 16 ? '0' : ''}${part.toRadixString(16)}'); - return result.toString(); -} - -String calculateSha(File file) { - final Stopwatch stopwatch = Stopwatch()..start(); - final List<int> bytes = file.readAsBytesSync(); - printTrace('calculateSha: reading file took ${stopwatch.elapsedMicroseconds}us'); - stopwatch.reset(); - final String sha = hex(sha1.convert(bytes).bytes); - stopwatch.stop(); - printTrace('calculateSha: computing sha took ${stopwatch.elapsedMicroseconds}us'); - return sha; -} - /// Convert `foo_bar` to `fooBar`. String camelCase(String str) { int index = str.indexOf('_');
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_build.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_build.dart index 00d95fa..376920e 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_build.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_build.dart
@@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:meta/meta.dart'; import '../asset.dart'; @@ -12,11 +14,20 @@ import '../bundle.dart'; import '../convert.dart'; import '../devfs.dart'; +import '../globals.dart'; import '../project.dart'; +import '../usage.dart'; import 'fuchsia_pm.dart'; import 'fuchsia_sdk.dart'; +Future<void> _timedBuildStep(String name, Future<void> Function() action) async { + final Stopwatch sw = Stopwatch()..start(); + await action(); + printTrace('$name: ${sw.elapsedMilliseconds} ms.'); + flutterUsage.sendTiming('build', name, Duration(milliseconds: sw.elapsedMilliseconds)); +} + // Building a Fuchsia package has a few steps: // 1. Do the custom kernel compile using the kernel compiler from the Fuchsia // SDK. This produces .dilp files (among others) and a manifest file. @@ -32,10 +43,13 @@ outDir.createSync(recursive: true); } - await fuchsiaSdk.fuchsiaKernelCompiler.build( - fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo); - await _buildAssets(fuchsiaProject, target, buildInfo); - await _buildPackage(fuchsiaProject, target, buildInfo); + await _timedBuildStep('fuchsia-kernel-compile', + () => fuchsiaSdk.fuchsiaKernelCompiler.build( + fuchsiaProject: fuchsiaProject, target: target, buildInfo: buildInfo)); + await _timedBuildStep('fuchsia-build-assets', + () => _buildAssets(fuchsiaProject, target, buildInfo)); + await _timedBuildStep('fuchsia-build-package', + () => _buildPackage(fuchsiaProject, target, buildInfo)); } Future<void> _buildAssets(
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index 59a0153..a5cf7c0 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -24,6 +24,7 @@ import '../macos/xcode.dart'; import '../project.dart'; import '../services.dart'; +import '../usage.dart'; import 'code_signing.dart'; import 'xcodeproj.dart'; @@ -374,7 +375,7 @@ buildCommands.add('SCRIPT_OUTPUT_STREAM_FILE=${scriptOutputPipeFile.absolute.path}'); } - final Stopwatch buildStopwatch = Stopwatch()..start(); + final Stopwatch sw = Stopwatch()..start(); initialBuildStatus = logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.fastOperation); final RunResult buildResult = await runAsync( buildCommands, @@ -387,11 +388,11 @@ buildSubStatus = null; initialBuildStatus?.cancel(); initialBuildStatus = null; - buildStopwatch.stop(); printStatus( 'Xcode build done.'.padRight(kDefaultStatusPadding + 1) - + '${getElapsedAsSeconds(buildStopwatch.elapsed).padLeft(5)}', + + '${getElapsedAsSeconds(sw.elapsed).padLeft(5)}', ); + flutterUsage.sendTiming('build', 'xcode-ios', Duration(milliseconds: sw.elapsedMilliseconds)); // Run -showBuildSettings again but with the exact same parameters as the build. final Map<String, String> buildSettings = parseXcodeBuildSettings(runCheckedSync(
diff --git a/packages/flutter_tools/lib/src/linux/build_linux.dart b/packages/flutter_tools/lib/src/linux/build_linux.dart index 9010095..2043731 100644 --- a/packages/flutter_tools/lib/src/linux/build_linux.dart +++ b/packages/flutter_tools/lib/src/linux/build_linux.dart
@@ -13,6 +13,7 @@ import '../convert.dart'; import '../globals.dart'; import '../project.dart'; +import '../usage.dart'; /// Builds the Linux project through the Makefile. Future<void> buildLinux(LinuxProject linuxProject, BuildInfo buildInfo, {String target = 'lib/main.dart'}) async { @@ -37,6 +38,8 @@ ..createSync(recursive: true) ..writeAsStringSync(buffer.toString()); + // Invoke make. + final Stopwatch sw = Stopwatch()..start(); final Process process = await processManager.start(<String>[ 'make', '-C', @@ -63,4 +66,5 @@ if (result != 0) { throwToolExit('Build process failed'); } + flutterUsage.sendTiming('build', 'make-linux', Duration(milliseconds: sw.elapsedMilliseconds)); }
diff --git a/packages/flutter_tools/lib/src/macos/build_macos.dart b/packages/flutter_tools/lib/src/macos/build_macos.dart index 12d2787..50159b2 100644 --- a/packages/flutter_tools/lib/src/macos/build_macos.dart +++ b/packages/flutter_tools/lib/src/macos/build_macos.dart
@@ -12,6 +12,7 @@ import '../globals.dart'; import '../ios/xcodeproj.dart'; import '../project.dart'; +import '../usage.dart'; import 'cocoapod_utils.dart'; /// Builds the macOS project through xcode build. @@ -37,6 +38,7 @@ config = 'Release'; } // Run build script provided by application. + final Stopwatch sw = Stopwatch()..start(); final Process process = await processManager.start(<String>[ '/usr/bin/env', 'xcrun', @@ -69,4 +71,5 @@ if (result != 0) { throwToolExit('Build process failed'); } + flutterUsage.sendTiming('build', 'xcode-macos', Duration(milliseconds: sw.elapsedMilliseconds)); }
diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index 90b7ea3..3816377 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart
@@ -13,12 +13,14 @@ import '../bundle.dart'; import '../globals.dart'; import '../project.dart'; +import '../usage.dart'; /// The [WebCompilationProxy] instance. WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>(); Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo buildInfo) async { final Status status = logger.startProgress('Compiling $target for the Web...', timeout: null); + final Stopwatch sw = Stopwatch()..start(); final Directory outputDir = fs.directory(getWebBuildDirectory()) ..createSync(recursive: true); bool result; @@ -55,6 +57,11 @@ if (result == false) { throwToolExit('Failed to compile $target for the Web.'); } + String buildName = 'ddc'; + if (buildInfo.isRelease) { + buildName = 'dart2js'; + } + flutterUsage.sendTiming('build', buildName, Duration(milliseconds: sw.elapsedMilliseconds)); } /// An indirection on web compilation.
diff --git a/packages/flutter_tools/lib/src/windows/build_windows.dart b/packages/flutter_tools/lib/src/windows/build_windows.dart index 1d08075..781b095 100644 --- a/packages/flutter_tools/lib/src/windows/build_windows.dart +++ b/packages/flutter_tools/lib/src/windows/build_windows.dart
@@ -13,6 +13,7 @@ import '../convert.dart'; import '../globals.dart'; import '../project.dart'; +import '../usage.dart'; import 'msbuild_utils.dart'; import 'visual_studio.dart'; @@ -48,6 +49,7 @@ final String configuration = buildInfo.isDebug ? 'Debug' : 'Release'; final String solutionPath = windowsProject.solutionFile.path; + final Stopwatch sw = Stopwatch()..start(); // Run the script with a relative path to the project using the enclosing // directory as the workingDirectory, to avoid hitting the limit on command // lengths in batch scripts if the absolute path to the project is long. @@ -78,4 +80,5 @@ if (result != 0) { throwToolExit('Build process failed'); } + flutterUsage.sendTiming('build', 'vs_build', Duration(milliseconds: sw.elapsedMilliseconds)); }