Remove android stack_size_test (#153695)

This test expects to run on 32-bit hardware, but the devicelab no longer
has 32-bit Android hardware. In particular, the test contains hand-coded
32-bit arm assembly for reading the stack pointer from Dart code via
`dart:ffi`.

This test was added in response to a framework change that caused stack
frames to be larger than expected while building widgets, leading apps
to crash with StackOverflow exceptions unexpectedly. Since then (>3
years ago) this test has not prevented any similar issue, so I believe
deleting it rather than fixing it is a better use of resources.
This commit is contained in:
Zachary Anderson 2024-08-19 12:25:35 -07:00 committed by GitHub
parent 3a6053208b
commit 88123277fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 0 additions and 239 deletions

View file

@ -2116,17 +2116,6 @@ targets:
["devicelab", "android", "linux", "pixel", "7pro"]
task_name: android_semantics_integration_test
# linux mokey test
- name: Linux_mokey android_stack_size_test
recipe: devicelab/devicelab_drone
presubmit: false
bringup: true # https://github.com/flutter/flutter/issues/148085
timeout: 60
properties:
tags: >
["devicelab", "android", "linux", "mokey"]
task_name: android_stack_size_test
# linux mokey benchmark
- name: Linux_mokey android_view_scroll_perf__timeline_summary
recipe: devicelab/devicelab_drone

View file

@ -27,7 +27,6 @@ const String kHeavyGridViewRouteName = '/heavy_gridview';
const String kRasterCacheUseMemory = '/raster_cache_use_memory';
const String kShaderMaskCacheRouteName = '/shader_mask_cache';
const String kSimpleScrollRouteName = '/simple_scroll';
const String kStackSizeRouteName = '/stack_size';
const String kAnimationWithMicrotasksRouteName = '/animation_with_microtasks';
const String kAnimatedImageRouteName = '/animated_image';
const String kOpacityPeepholeRouteName = '/opacity_peephole';
@ -62,5 +61,3 @@ const String kGradientPerfStaticConsistentRouteName = '$kGradientPerfRouteName/s
const String kScrollableName = '/macrobenchmark_listview';
const String kOpacityScrollableName = '$kOpacityPeepholeRouteName/listview';
const String kGradientPerfScrollableName = '$kGradientPerfRouteName/listview';
const String kStackSizeKey = 'stack_size';

View file

@ -41,7 +41,6 @@ import 'src/shader_mask_cache.dart';
import 'src/simple_animation.dart';
import 'src/simple_scroll.dart';
import 'src/sliders.dart';
import 'src/stack_size.dart';
import 'src/text.dart';
import 'src/very_long_picture_scrolling.dart';
@ -83,7 +82,6 @@ class MacrobenchmarksApp extends StatelessWidget {
kRasterCacheUseMemory: (BuildContext context) => const RasterCacheUseMemory(),
kShaderMaskCacheRouteName: (BuildContext context) => const ShaderMaskCachePage(),
kSimpleScrollRouteName: (BuildContext context) => const SimpleScroll(),
kStackSizeRouteName: (BuildContext context) => const StackSizePage(),
kAnimationWithMicrotasksRouteName: (BuildContext context) => const AnimationWithMicrotasks(),
kAnimatedImageRouteName: (BuildContext context) => const AnimatedImagePage(),
kOpacityPeepholeRouteName: (BuildContext context) => const OpacityPeepholePage(),
@ -279,13 +277,6 @@ class HomePage extends StatelessWidget {
Navigator.pushNamed(context, kLargeImageChangerRouteName);
},
),
ElevatedButton(
key: const Key(kStackSizeRouteName),
child: const Text('Stack Size'),
onPressed: () {
Navigator.pushNamed(context, kStackSizeRouteName);
},
),
ElevatedButton(
key: const Key(kAnimationWithMicrotasksRouteName),
child: const Text('Animation With Microtasks'),

View file

@ -1,129 +0,0 @@
// 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:ffi' as ffi;
import 'dart:io' as io;
import 'package:flutter/material.dart';
import '../common.dart';
typedef GetStackPointerCallback = int Function();
// c interop function:
// void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
typedef CMmap = ffi.Pointer<ffi.Void> Function(
ffi.Pointer<ffi.Void>, ffi.IntPtr, ffi.Int32, ffi.Int32, ffi.Int32, ffi.IntPtr);
typedef DartMmap = ffi.Pointer<ffi.Void> Function(
ffi.Pointer<ffi.Void>, int, int, int, int, int);
final DartMmap mmap = ffi.DynamicLibrary.process().lookupFunction<CMmap, DartMmap>('mmap');
// c interop function:
// int mprotect(void* addr, size_t len, int prot);
typedef CMprotect = ffi.Int32 Function(ffi.Pointer<ffi.Void>, ffi.IntPtr, ffi.Int32);
typedef DartMprotect = int Function(ffi.Pointer<ffi.Void>, int, int);
final DartMprotect mprotect = ffi.DynamicLibrary.process()
.lookupFunction<CMprotect, DartMprotect>('mprotect');
const int kProtRead = 1;
const int kProtWrite = 2;
const int kProtExec = 4;
const int kMapPrivate = 0x02;
const int kMapJit = 0x0;
const int kMapAnon = 0x20;
const int kMemorySize = 16;
const int kInvalidFileDescriptor = -1;
const int kkFileMappingOffset = 0;
const int kMemoryStartingIndex = 0;
const int kExitCodeSuccess = 0;
final GetStackPointerCallback getStackPointer = () {
// Makes sure we are running on an Android arm64 device.
if (!io.Platform.isAndroid) {
throw 'This benchmark test can only be run on Android arm devices.';
}
final io.ProcessResult result = io.Process.runSync('getprop', <String>['ro.product.cpu.abi']);
if (result.exitCode != 0) {
throw 'Failed to retrieve CPU information.';
}
if (!result.stdout.toString().contains('armeabi')) {
throw 'This benchmark test can only be run on Android arm devices.';
}
// Creates a block of memory to store the assembly code.
final ffi.Pointer<ffi.Void> region = mmap(ffi.nullptr, kMemorySize, kProtRead | kProtWrite,
kMapPrivate | kMapAnon | kMapJit, kInvalidFileDescriptor, kkFileMappingOffset);
if (region == ffi.nullptr) {
throw 'Failed to acquire memory for the test.';
}
// Writes the assembly code into the memory block. This assembly code returns
// the memory address of the stack pointer.
region.cast<ffi.Uint8>().asTypedList(kMemorySize).setAll(
kMemoryStartingIndex,
<int>[
// "mov r0, sp" in machine code: 0D00A0E1.
0x0d, 0x00, 0xa0, 0xe1,
// "bx lr" in machine code: 1EFF2FE1.
0x1e, 0xff, 0x2f, 0xe1,
]
);
// Makes sure the memory block is executable.
if (mprotect(region, kMemorySize, kProtRead | kProtExec) != kExitCodeSuccess) {
throw 'Failed to write executable code to the memory.';
}
return region
.cast<ffi.NativeFunction<ffi.IntPtr Function()>>()
.asFunction<int Function()>();
}();
class StackSizePage extends StatelessWidget {
const StackSizePage({super.key});
@override
Widget build(BuildContext context) {
return const Material(
child: Column(
children: <Widget>[
SizedBox(
width: 200,
height: 100,
child: ParentWidget(),
),
],
),
);
}
}
class ParentWidget extends StatelessWidget {
const ParentWidget({super.key});
@override
Widget build(BuildContext context) {
final int myStackSize = getStackPointer();
return ChildWidget(parentStackSize: myStackSize);
}
}
class ChildWidget extends StatelessWidget {
const ChildWidget({required this.parentStackSize, super.key});
final int parentStackSize;
@override
Widget build(BuildContext context) {
final int myStackSize = getStackPointer();
// Captures the stack size difference between parent widget and child widget
// during the rendering pipeline, i.e. one layer of stateless widget.
return Text(
'${parentStackSize - myStackSize}',
key: const ValueKey<String>(kStackSizeKey),
);
}
}

View file

@ -1,38 +0,0 @@
// 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:convert' show JsonEncoder;
import 'package:file/file.dart';
import 'package:flutter_driver/flutter_driver.dart';
import 'package:macrobenchmarks/common.dart';
import 'package:path/path.dart' as path;
import 'package:test/test.dart' hide TypeMatcher, isInstanceOf;
import 'util.dart';
const JsonEncoder _prettyEncoder = JsonEncoder.withIndent(' ');
void main() {
test('stack_size', () async {
late int stackSizeInBytes;
await runDriverTestForRoute(kStackSizeRouteName, (FlutterDriver driver) async {
final String stackSize = await driver.getText(find.byValueKey(kStackSizeKey));
expect(stackSize.isNotEmpty, isTrue);
stackSizeInBytes = int.parse(stackSize);
});
expect(stackSizeInBytes > 0, isTrue);
await fs.directory(testOutputsDirectory).create(recursive: true);
final File file = fs.file(path.join(testOutputsDirectory, 'stack_size.json'));
await file.writeAsString(_encodeJson(<String, dynamic>{
'stack_size': stackSizeInBytes,
}));
}, timeout: Timeout.none);
}
String _encodeJson(Map<String, dynamic> jsonObject) {
return _prettyEncoder.convert(jsonObject);
}

View file

@ -1,12 +0,0 @@
// 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 'package:flutter_devicelab/framework/devices.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/perf_tests.dart';
Future<void> main() async {
deviceOperatingSystem = DeviceOperatingSystem.androidArm;
await task(createStackSizeTest());
}

View file

@ -353,43 +353,6 @@ TaskFunction createSlidersPerfTest() {
).run;
}
TaskFunction createStackSizeTest() {
final String testDirectory =
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks';
const String testTarget = 'test_driver/run_app.dart';
const String testDriver = 'test_driver/stack_size_perf_test.dart';
return () {
return inDirectory<TaskResult>(testDirectory, () async {
final Device device = await devices.workingDevice;
await device.unlock();
final String deviceId = device.deviceId;
await flutter('packages', options: <String>['get']);
await flutter('drive', options: <String>[
'--no-android-gradle-daemon',
'-v',
'--verbose-system-logs',
'--profile',
'-t', testTarget,
'--driver', testDriver,
'-d',
deviceId,
]);
final Map<String, dynamic> data = json.decode(
file('${_testOutputDirectory(testDirectory)}/stack_size.json').readAsStringSync(),
) as Map<String, dynamic>;
final Map<String, dynamic> result = <String, dynamic>{
'stack_size_per_nesting_level': data['stack_size'],
};
return TaskResult.success(
result,
benchmarkScoreKeys: result.keys.toList(),
);
});
};
}
TaskFunction createFullscreenTextfieldPerfTest() {
return PerfTest(
'${flutterDirectory.path}/dev/benchmarks/macrobenchmarks',