Add script for profiling startup using flutter trace on Android
This will work after the next Engine roll. Currently it requires a fix to tracing which isn't in the released engine. Next steps are to make this work on a bot, and to add iOS support. @abarth @chinmaygarde
This commit is contained in:
parent
7d4feaa71a
commit
5e70bd13f6
95
dev/profile_startup.dart
Executable file
95
dev/profile_startup.dart
Executable file
@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env dart
|
||||
// Copyright 2015 The Chromium 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:io';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
const int ITERATIONS = 3;
|
||||
|
||||
String runWithLoggingSync(List<String> cmd, {
|
||||
bool checked: true,
|
||||
String workingDirectory
|
||||
}) {
|
||||
ProcessResult results =
|
||||
Process.runSync(cmd[0], cmd.getRange(1, cmd.length).toList(), workingDirectory: workingDirectory);
|
||||
if (results.exitCode != 0) {
|
||||
String errorDescription = 'Error code ${results.exitCode} '
|
||||
'returned when attempting to run command: ${cmd.join(' ')}';
|
||||
print(errorDescription);
|
||||
if (results.stderr.length > 0)
|
||||
print('Errors logged: ${results.stderr.trim()}');
|
||||
if (checked)
|
||||
throw errorDescription;
|
||||
}
|
||||
if (results.stdout.trim().isNotEmpty)
|
||||
print(results.stdout.trim());
|
||||
return results.stdout;
|
||||
}
|
||||
|
||||
double timeToFirstFrame(trace) {
|
||||
// TODO(eseidel): Sort! Events are not guarenteed to be in timestamp order.
|
||||
List events = trace['traceEvents'];
|
||||
int firstTimeStamp = events[0]['ts'].toInt();
|
||||
var firstSwap = events.firstWhere((e) => e['name'] == 'NativeViewGLSurfaceEGL:RealSwapBuffers');
|
||||
int swapStart = firstSwap['ts'].toInt();
|
||||
int swapEnd = swapStart + firstSwap['dur'].toInt();
|
||||
return (swapEnd - firstTimeStamp) / 1000; // microseconds to milliseconds.
|
||||
}
|
||||
|
||||
Future<double> test(String tracesDir, String projectPath, int runNumber) async {
|
||||
// If we used package:path we could grab the basename of project_path
|
||||
// and include that in the trace_name.
|
||||
String tracePath = "${tracesDir}/trace_$runNumber.json";
|
||||
runWithLoggingSync([
|
||||
'flutter',
|
||||
'start',
|
||||
'--trace-startup'
|
||||
], workingDirectory: projectPath);
|
||||
await new Future.delayed(const Duration(seconds: 2), () => "");
|
||||
runWithLoggingSync([
|
||||
'flutter',
|
||||
'trace',
|
||||
'--stop',
|
||||
'--out=${tracePath}'
|
||||
], workingDirectory: projectPath);
|
||||
|
||||
JsonDecoder decoder = new JsonDecoder();
|
||||
String contents = await new File(tracePath).readAsString();
|
||||
Map data = await decoder.convert(contents);
|
||||
return timeToFirstFrame(data);
|
||||
}
|
||||
|
||||
// package:statistics has slightly nicer ones of these.
|
||||
double mean(List<double> times) {
|
||||
return times.reduce((a,b) => a + b) / times.length;
|
||||
}
|
||||
|
||||
double median(List<double> times) {
|
||||
times.sort();
|
||||
return times[times.length ~/ 2];
|
||||
}
|
||||
|
||||
main(List<String> args) async {
|
||||
// We could do much more sophisticated things if we used package:args.
|
||||
if (args.length < 1) {
|
||||
print("Usage: profile_startup.dart PROJECT_PATH\n");
|
||||
print("PROJECT_PATH required.");
|
||||
return 1;
|
||||
}
|
||||
String projectPath = args[0];
|
||||
String traces_dir = '/tmp';
|
||||
|
||||
List<double> times = [];
|
||||
print("$ITERATIONS runs using $projectPath:");
|
||||
for (var x = 0; x < ITERATIONS; x++) {
|
||||
int runNumber = x + 1;
|
||||
double time = await test(traces_dir, projectPath, runNumber);
|
||||
print(" ${runNumber.toString().padLeft(2)} $time");
|
||||
times.add(time);
|
||||
}
|
||||
print("mean: ${mean(times)}");
|
||||
print("median: ${median(times)}");
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user