mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
167 lines
6.5 KiB
Dart
167 lines
6.5 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert' show json;
|
|
import 'dart:io' as io;
|
|
|
|
import 'package:meta/meta.dart';
|
|
import 'package:path/path.dart' as path;
|
|
import 'package:shelf/shelf.dart';
|
|
import 'package:shelf/shelf_io.dart' as shelf_io;
|
|
import 'package:shelf_static/shelf_static.dart';
|
|
|
|
import 'package:flutter_devicelab/framework/framework.dart';
|
|
import 'package:flutter_devicelab/framework/utils.dart';
|
|
|
|
/// The port number used by the local benchmark server.
|
|
const int benchmarkServerPort = 9999;
|
|
|
|
Future<TaskResult> runWebBenchmark({ @required bool useCanvasKit }) async {
|
|
final String macrobenchmarksDirectory = path.join('${flutterDirectory.path}', 'dev', 'benchmarks', 'macrobenchmarks');
|
|
return await inDirectory(macrobenchmarksDirectory, () async {
|
|
await evalFlutter('build', options: <String>[
|
|
'web',
|
|
if (useCanvasKit)
|
|
'--dart-define=FLUTTER_WEB_USE_SKIA=true',
|
|
'--profile',
|
|
'-t',
|
|
'lib/web_benchmarks.dart',
|
|
], environment: <String, String>{
|
|
'FLUTTER_WEB': 'true',
|
|
});
|
|
final Completer<List<Map<String, dynamic>>> profileData = Completer<List<Map<String, dynamic>>>();
|
|
final List<Map<String, dynamic>> collectedProfiles = <Map<String, dynamic>>[];
|
|
List<String> benchmarks;
|
|
Iterator<String> benchmarkIterator;
|
|
|
|
io.HttpServer server;
|
|
Cascade cascade = Cascade();
|
|
cascade = cascade.add((Request request) async {
|
|
if (request.requestedUri.path.endsWith('/profile-data')) {
|
|
final Map<String, dynamic> profile = json.decode(await request.readAsString()) as Map<String, dynamic>;
|
|
final String benchmarkName = profile['name'] as String;
|
|
if (benchmarkName != benchmarkIterator.current) {
|
|
profileData.completeError(Exception(
|
|
'Browser returned benchmark results from a wrong benchmark.\n'
|
|
'Requested to run bechmark ${benchmarkIterator.current}, but '
|
|
'got results for $benchmarkName.',
|
|
));
|
|
server.close();
|
|
}
|
|
collectedProfiles.add(profile);
|
|
return Response.ok('Profile received');
|
|
} else if (request.requestedUri.path.endsWith('/next-benchmark')) {
|
|
if (benchmarks == null) {
|
|
benchmarks = (json.decode(await request.readAsString()) as List<dynamic>).cast<String>();
|
|
benchmarkIterator = benchmarks.iterator;
|
|
}
|
|
if (benchmarkIterator.moveNext()) {
|
|
final String nextBenchmark = benchmarkIterator.current;
|
|
print('Launching benchmark "$nextBenchmark"');
|
|
return Response.ok(nextBenchmark);
|
|
} else {
|
|
profileData.complete(collectedProfiles);
|
|
return Response.notFound('Finished running benchmarks.');
|
|
}
|
|
} else {
|
|
return Response.notFound(
|
|
'This request is not handled by the profile-data handler.');
|
|
}
|
|
}).add(createStaticHandler(
|
|
path.join('$macrobenchmarksDirectory', 'build', 'web'),
|
|
));
|
|
|
|
server = await io.HttpServer.bind('localhost', benchmarkServerPort);
|
|
io.Process chromeProcess;
|
|
try {
|
|
shelf_io.serveRequests(server, cascade.handler);
|
|
|
|
final bool isChromeNoSandbox =
|
|
io.Platform.environment['CHROME_NO_SANDBOX'] == 'true';
|
|
|
|
final String dartToolDirectory = path.join('$macrobenchmarksDirectory/.dart_tool');
|
|
final String userDataDir = io.Directory(dartToolDirectory).createTempSync('chrome_user_data_').path;
|
|
final List<String> args = <String>[
|
|
'--user-data-dir=$userDataDir',
|
|
'http://localhost:$benchmarkServerPort/index.html',
|
|
if (isChromeNoSandbox)
|
|
'--no-sandbox',
|
|
'--window-size=1024,1024',
|
|
'--disable-extensions',
|
|
'--disable-popup-blocking',
|
|
// Indicates that the browser is in "browse without sign-in" (Guest session) mode.
|
|
'--bwsi',
|
|
'--no-first-run',
|
|
'--no-default-browser-check',
|
|
'--disable-default-apps',
|
|
'--disable-translate',
|
|
];
|
|
|
|
final bool isUncalibratedSmokeTest =
|
|
io.Platform.environment['UNCALIBRATED_SMOKE_TEST'] == 'true';
|
|
if (isUncalibratedSmokeTest) {
|
|
print('Running in headless mode because running on uncalibrated hardware.');
|
|
args.add('--headless');
|
|
// When running in headless mode Chrome exits immediately unless
|
|
// a debug port is specified.
|
|
args.add('--remote-debugging-port=${benchmarkServerPort + 1}');
|
|
}
|
|
|
|
chromeProcess = await startProcess(
|
|
_findSystemChromeExecutable(),
|
|
args,
|
|
workingDirectory: cwd,
|
|
);
|
|
|
|
bool receivedProfileData = false;
|
|
chromeProcess.exitCode.then((int exitCode) {
|
|
if (!receivedProfileData) {
|
|
profileData.completeError(Exception(
|
|
'Chrome process existed prematurely with exit code $exitCode',
|
|
));
|
|
}
|
|
});
|
|
forwardStandardStreams(chromeProcess);
|
|
|
|
print('Waiting for the benchmark to report benchmark profile.');
|
|
|
|
final String backend = useCanvasKit ? 'canvaskit' : 'html';
|
|
final Map<String, dynamic> taskResult = <String, dynamic>{};
|
|
final List<String> benchmarkScoreKeys = <String>[];
|
|
final List<Map<String, dynamic>> profiles = await profileData.future;
|
|
print('Received profile data');
|
|
receivedProfileData = true;
|
|
for (final Map<String, dynamic> profile in profiles) {
|
|
final String benchmarkName = profile['name'] as String;
|
|
final String benchmarkScoreKey = '$benchmarkName.$backend.averageDrawFrameDuration';
|
|
taskResult[benchmarkScoreKey] = profile['averageDrawFrameDuration'].toDouble(); // micros
|
|
taskResult['$benchmarkName.$backend.drawFrameDurationNoise'] = profile['drawFrameDurationNoise'].toDouble(); // micros
|
|
benchmarkScoreKeys.add(benchmarkScoreKey);
|
|
}
|
|
return TaskResult.success(taskResult, benchmarkScoreKeys: benchmarkScoreKeys);
|
|
} finally {
|
|
server.close();
|
|
chromeProcess?.kill();
|
|
}
|
|
});
|
|
}
|
|
|
|
String _findSystemChromeExecutable() {
|
|
if (io.Platform.isLinux) {
|
|
final io.ProcessResult which =
|
|
io.Process.runSync('which', <String>['google-chrome']);
|
|
|
|
if (which.exitCode != 0) {
|
|
throw Exception('Failed to locate system Chrome installation.');
|
|
}
|
|
|
|
return (which.stdout as String).trim();
|
|
} else if (io.Platform.isMacOS) {
|
|
return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome';
|
|
} else {
|
|
throw Exception('Web benchmarks cannot run on ${io.Platform.operatingSystem} yet.');
|
|
}
|
|
}
|