Add DevTools memory test (#55486)
diff --git a/dev/devicelab/bin/tasks/complex_layout_scroll_perf__devtools_memory.dart b/dev/devicelab/bin/tasks/complex_layout_scroll_perf__devtools_memory.dart
new file mode 100644
index 0000000..9183452
--- /dev/null
+++ b/dev/devicelab/bin/tasks/complex_layout_scroll_perf__devtools_memory.dart
@@ -0,0 +1,18 @@
+// 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 'package:flutter_devicelab/framework/adb.dart';
+import 'package:flutter_devicelab/framework/framework.dart';
+import 'package:flutter_devicelab/framework/utils.dart';
+import 'package:flutter_devicelab/tasks/perf_tests.dart';
+
+Future<void> main() async {
+ deviceOperatingSystem = DeviceOperatingSystem.android;
+ await task(DevToolsMemoryTest(
+ '${flutterDirectory.path}/dev/benchmarks/complex_layout',
+ 'test_driver/scroll_perf.dart',
+ ).run);
+}
diff --git a/dev/devicelab/lib/tasks/perf_tests.dart b/dev/devicelab/lib/tasks/perf_tests.dart
index ddb6a35..890bfd7 100644
--- a/dev/devicelab/lib/tasks/perf_tests.dart
+++ b/dev/devicelab/lib/tasks/perf_tests.dart
@@ -3,8 +3,9 @@
// found in the LICENSE file.
import 'dart:async';
-import 'dart:convert' show json;
+import 'dart:convert' show LineSplitter, json, utf8;
import 'dart:io';
+import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
@@ -692,6 +693,129 @@
}
}
+class DevToolsMemoryTest {
+ DevToolsMemoryTest(this.project, this.driverTest);
+
+ final String project;
+ final String driverTest;
+
+ Future<TaskResult> run() {
+ return inDirectory<TaskResult>(project, () async {
+ _device = await devices.workingDevice;
+ await _device.unlock();
+ await flutter('packages', options: <String>['get']);
+
+ await _launchApp();
+ if (_observatoryUri == null) {
+ return TaskResult.failure('Observatory URI not found.');
+ }
+
+ await _launchDevTools();
+
+ await flutter(
+ 'drive',
+ options: <String>[
+ '--use-existing-app', _observatoryUri,
+ '-d', _device.deviceId,
+ '--profile',
+ driverTest,
+ ],
+ );
+
+ _devToolsProcess.kill();
+ await _devToolsProcess.exitCode;
+
+ _runProcess.kill();
+ await _runProcess.exitCode;
+
+ final Map<String, dynamic> data = json.decode(
+ file('$project/$_kJsonFileName').readAsStringSync(),
+ ) as Map<String, dynamic>;
+ final List<dynamic> samples = data['samples']['data'] as List<dynamic>;
+ int maxRss = 0;
+ int maxAdbTotal = 0;
+ for (final dynamic sample in samples) {
+ maxRss = math.max(maxRss, sample['rss'] as int);
+ if (sample['adb_memoryInfo'] != null) {
+ maxAdbTotal = math.max(maxAdbTotal, sample['adb_memoryInfo']['Total'] as int);
+ }
+ }
+ return TaskResult.success(
+ <String, dynamic>{'maxRss': maxRss, 'maxAdbTotal': maxAdbTotal},
+ benchmarkScoreKeys: <String>['maxRss', 'maxAdbTotal'],
+ );
+ });
+ }
+
+ Future<void> _launchApp() async {
+ print('launching $project$driverTest on device...');
+ final String flutterPath = path.join(flutterDirectory.path, 'bin', 'flutter');
+ _runProcess = await startProcess(
+ flutterPath,
+ <String>[
+ 'run',
+ '--verbose',
+ '--profile',
+ '-d', _device.deviceId,
+ driverTest,
+ ],
+ );
+
+ // Listen for Observatory URI and forward stdout/stderr
+ final Completer<String> observatoryUri = Completer<String>();
+ _runProcess.stdout
+ .transform<String>(utf8.decoder)
+ .transform<String>(const LineSplitter())
+ .listen((String line) {
+ print('run stdout: $line');
+ final RegExpMatch match = RegExp(r'An Observatory debugger and profiler on .+ is available at: ((http|//)[a-zA-Z0-9:/=_\-\.\[\]]+)').firstMatch(line);
+ if (match != null) {
+ observatoryUri.complete(match[1]);
+ _observatoryUri = match[1];
+ }
+ }, onDone: () { observatoryUri.complete(null); });
+ _forwardStream(_runProcess.stderr, 'run stderr');
+
+ _observatoryUri = await observatoryUri.future;
+ }
+
+ Future<void> _launchDevTools() async {
+ await exec('pub', <String>[
+ 'global',
+ 'activate',
+ 'devtools',
+ ]);
+ _devToolsProcess = await startProcess(
+ 'pub',
+ <String>[
+ 'global',
+ 'run',
+ 'devtools',
+ '--vm-uri', _observatoryUri,
+ '--profile-memory', _kJsonFileName,
+ ],
+ );
+ _forwardStream(_devToolsProcess.stdout, 'devtools stdout');
+ _forwardStream(_devToolsProcess.stderr, 'devtools stderr');
+ }
+
+ void _forwardStream(Stream<List<int>> stream, String label) {
+ stream
+ .transform<String>(utf8.decoder)
+ .transform<String>(const LineSplitter())
+ .listen((String line) {
+ print('$label: $line');
+ });
+ }
+
+ Device _device;
+ String _observatoryUri;
+ Process _runProcess;
+ Process _devToolsProcess;
+
+ static const String _kJsonFileName = 'devtools_memory.json';
+}
+
enum ReportedDurationTestFlavor {
debug, profile, release
}
diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml
index e89c888..aacc1c5 100644
--- a/dev/devicelab/manifest.yaml
+++ b/dev/devicelab/manifest.yaml
@@ -285,6 +285,12 @@
stage: devicelab
required_agent_capabilities: ["mac/android"]
+ complex_layout_scroll_perf__devtools_memory:
+ description: >
+ Measures memory usage of the scroll performance test using DevTools.
+ stage: devicelab
+ required_agent_capabilities: ["linux/android"]
+
hello_world_android__compile:
description: >
Measures the APK size of Hello World.