Commit 4da4ca89 authored by Jason Simmons's avatar Jason Simmons Committed by GitHub

Add a devicelab test that captures memory usage metrics on Android (#7120)

parent b670ce4b
// Copyright 2016 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:async';
import 'package:flutter_devicelab/tasks/perf_tests.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async {
await task(createComplexLayoutScrollMemoryTest(os: DeviceOperatingSystem.android));
}
...@@ -81,6 +81,12 @@ abstract class Device { ...@@ -81,6 +81,12 @@ abstract class Device {
/// ///
/// Assumes the device doesn't have a secure unlock pattern. /// Assumes the device doesn't have a secure unlock pattern.
Future<Null> unlock(); Future<Null> unlock();
/// Read memory statistics for a process.
Future<Map<String, dynamic>> getMemoryStats(String packageName);
/// Stop a process.
Future<Null> stop(String packageName);
} }
class AndroidDeviceDiscovery implements DeviceDiscovery { class AndroidDeviceDiscovery implements DeviceDiscovery {
...@@ -248,6 +254,20 @@ class AndroidDevice implements Device { ...@@ -248,6 +254,20 @@ class AndroidDevice implements Device {
Future<String> shellEval(String command, List<String> arguments, {Map<String, String> env}) { Future<String> shellEval(String command, List<String> arguments, {Map<String, String> env}) {
return eval(adbPath, <String>['shell', command]..addAll(arguments), env: env, canFail: false); return eval(adbPath, <String>['shell', command]..addAll(arguments), env: env, canFail: false);
} }
@override
Future<Map<String, dynamic>> getMemoryStats(String packageName) async {
String meminfo = await shellEval('dumpsys', <String>['meminfo', packageName]);
Match match = new RegExp(r'TOTAL\s+(\d+)').firstMatch(meminfo);
return <String, dynamic>{
'total_kb': int.parse(match.group(1)),
};
}
@override
Future<Null> stop(String packageName) async {
return shellExec('am', <String>['force-stop', packageName]);
}
} }
class IosDeviceDiscovery implements DeviceDiscovery { class IosDeviceDiscovery implements DeviceDiscovery {
...@@ -344,6 +364,14 @@ class IosDevice implements Device { ...@@ -344,6 +364,14 @@ class IosDevice implements Device {
@override @override
Future<Null> unlock() async {} Future<Null> unlock() async {}
@override
Future<Map<String, dynamic>> getMemoryStats(String packageName) async {
throw 'Not implemented';
}
@override
Future<Null> stop(String packageName) async {}
} }
/// Path to the `adb` executable. /// Path to the `adb` executable.
......
...@@ -28,7 +28,7 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent: false }) as ...@@ -28,7 +28,7 @@ Future<Map<String, dynamic>> runTask(String taskName, { bool silent: false }) as
if (!file(taskExecutable).existsSync()) if (!file(taskExecutable).existsSync())
throw 'Executable Dart file not found: $taskExecutable'; throw 'Executable Dart file not found: $taskExecutable';
int vmServicePort = await _findAvailablePort(); int vmServicePort = await findAvailablePort();
Process runner = await startProcess(dartBin, <String>[ Process runner = await startProcess(dartBin, <String>[
'--enable-vm-service=$vmServicePort', '--enable-vm-service=$vmServicePort',
'--no-pause-isolates-on-exit', '--no-pause-isolates-on-exit',
...@@ -118,17 +118,3 @@ Future<VMIsolate> _connectToRunnerIsolate(int vmServicePort) async { ...@@ -118,17 +118,3 @@ Future<VMIsolate> _connectToRunnerIsolate(int vmServicePort) async {
} }
} }
} }
Future<int> _findAvailablePort() async {
int port = 20000;
while (true) {
try {
ServerSocket socket =
await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, port);
await socket.close();
return port;
} catch (_) {
port++;
}
}
}
...@@ -231,10 +231,10 @@ Future<String> eval(String executable, List<String> arguments, ...@@ -231,10 +231,10 @@ Future<String> eval(String executable, List<String> arguments,
} }
Future<int> flutter(String command, Future<int> flutter(String command,
{List<String> options: const <String>[], bool canFail: false}) { {List<String> options: const <String>[], bool canFail: false, Map<String, String> env}) {
List<String> args = <String>[command]..addAll(options); List<String> args = <String>[command]..addAll(options);
return exec(path.join(flutterDirectory.path, 'bin', 'flutter'), args, return exec(path.join(flutterDirectory.path, 'bin', 'flutter'), args,
canFail: canFail); canFail: canFail, env: env);
} }
String get dartBin => String get dartBin =>
...@@ -404,3 +404,18 @@ Future<Null> runAndCaptureAsyncStacks(Future<Null> callback()) { ...@@ -404,3 +404,18 @@ Future<Null> runAndCaptureAsyncStacks(Future<Null> callback()) {
}); });
return completer.future; return completer.future;
} }
/// Return an unused TCP port number.
Future<int> findAvailablePort() async {
int port = 20000;
while (true) {
try {
ServerSocket socket =
await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, port);
await socket.close();
return port;
} catch (_) {
port++;
}
}
}
...@@ -20,6 +20,16 @@ TaskFunction createComplexLayoutScrollPerfTest({ @required DeviceOperatingSystem ...@@ -20,6 +20,16 @@ TaskFunction createComplexLayoutScrollPerfTest({ @required DeviceOperatingSystem
); );
} }
TaskFunction createComplexLayoutScrollMemoryTest({ @required DeviceOperatingSystem os }) {
return new MemoryTest(
'${flutterDirectory.path}/dev/benchmarks/complex_layout',
'test_driver/scroll_perf.dart',
'complex_layout_scroll_perf',
'com.yourcompany.complexLayout',
os: os,
);
}
TaskFunction createFlutterGalleryStartupTest({ @required DeviceOperatingSystem os }) { TaskFunction createFlutterGalleryStartupTest({ @required DeviceOperatingSystem os }) {
return new StartupTest( return new StartupTest(
'${flutterDirectory.path}/examples/flutter_gallery', '${flutterDirectory.path}/examples/flutter_gallery',
...@@ -173,3 +183,71 @@ class BuildTest { ...@@ -173,3 +183,71 @@ class BuildTest {
}); });
} }
} }
class MemoryTest {
MemoryTest(this.testDirectory, this.testTarget, this.timelineFileName, this.packageName, { this.os });
final String testDirectory;
final String testTarget;
final String timelineFileName;
final String packageName;
final DeviceOperatingSystem os;
Future<TaskResult> call() {
return inDirectory(testDirectory, () async {
Device device = await devices.workingDevice;
await device.unlock();
String deviceId = device.deviceId;
await flutter('packages', options: <String>['get']);
if (os == DeviceOperatingSystem.ios) {
// This causes an Xcode project to be created.
await flutter('build', options: <String>['ios', '--profile']);
}
int debugPort = await findAvailablePort();
await flutter('run', options: <String>[
'-v',
'--profile',
'--trace-startup', // wait for the first frame to render
'-t',
testTarget,
'-d',
deviceId,
'--debug-port',
debugPort.toString(),
]);
Map<String, dynamic> startData = await device.getMemoryStats(packageName);
await flutter('drive', options: <String>[
'-v',
'-t',
testTarget,
'-d',
deviceId,
'--use-existing-app',
//'--keep-app-running',
], env: <String, String> {
'VM_SERVICE_URL': 'http://localhost:$debugPort'
});
Map<String, dynamic> endData = await device.getMemoryStats(packageName);
Map<String, dynamic> data = <String, dynamic>{
'start_total_kb': startData['total_kb'],
'end_total_kb': endData['total_kb'],
'diff_total_kb': endData['total_kb'] - startData['total_kb'],
};
await device.stop(packageName);
return new TaskResult.success(data, benchmarkScoreKeys: <String>[
'start_total_kb',
'end_total_kb',
'diff_total_kb',
]);
});
}
}
...@@ -109,6 +109,12 @@ tasks: ...@@ -109,6 +109,12 @@ tasks:
stage: devicelab stage: devicelab
required_agent_capabilities: ["has-android-device"] required_agent_capabilities: ["has-android-device"]
complex_layout_scroll_perf__memory:
description: >
Measures memory usage of the scroll performance test.
stage: devicelab
required_agent_capabilities: ["has-android-device"]
# iOS on-device tests # iOS on-device tests
......
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