mirror of
https://github.com/flutter/flutter
synced 2024-09-29 21:14:10 +00:00
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:
parent
3a6053208b
commit
88123277fc
11
.ci.yaml
11
.ci.yaml
|
@ -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
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
|
@ -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',
|
||||
|
|
Loading…
Reference in a new issue