mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
130 lines
4.0 KiB
Dart
130 lines
4.0 KiB
Dart
// 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 Material(
|
|
child: Column(
|
|
children: const <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),
|
|
);
|
|
}
|
|
}
|