Add microdroid minimum ram test
testMinimumRequiredRAM will find minimum required RAM to boot Microdroid by bisecting. The result will be collected as a metric. Bug: 231105297 Test: atest MicrodroidBenchmarks Change-Id: Icb34a2cc2f9d906ebde661be86ac9b802288de5b
This commit is contained in:
parent
11d051fea8
commit
5d5476bba1
|
@ -0,0 +1,26 @@
|
|||
package {
|
||||
default_applicable_licenses: ["Android-Apache-2.0"],
|
||||
}
|
||||
|
||||
android_test {
|
||||
name: "MicrodroidBenchmarkApp",
|
||||
test_suites: [
|
||||
"general-tests",
|
||||
],
|
||||
srcs: ["src/java/**/*.java"],
|
||||
static_libs: [
|
||||
"androidx.test.runner",
|
||||
"androidx.test.ext.junit",
|
||||
"truth-prebuilt",
|
||||
],
|
||||
libs: ["android.system.virtualmachine"],
|
||||
jni_libs: ["MicrodroidBenchmarkNativeLib"],
|
||||
platform_apis: true,
|
||||
use_embedded_native_libs: true,
|
||||
compile_multilib: "64",
|
||||
}
|
||||
|
||||
cc_library_shared {
|
||||
name: "MicrodroidBenchmarkNativeLib",
|
||||
srcs: ["src/native/benchmarkbinary.cpp"],
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2022 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.microdroid.benchmark">
|
||||
<uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
|
||||
<application>
|
||||
<uses-library android:name="android.system.virtualmachine" android:required="false" />
|
||||
</application>
|
||||
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
|
||||
android:targetPackage="com.android.microdroid.benchmark"
|
||||
android:label="Microdroid Benchmark" />
|
||||
</manifest>
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2022 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<configuration description="Runs sample instrumentation test.">
|
||||
<option name="config-descriptor:metadata" key="component" value="security" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
|
||||
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="test-file-name" value="MicrodroidBenchmarkApp.apk" />
|
||||
</target_preparer>
|
||||
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
|
||||
<option
|
||||
name="run-command"
|
||||
value="pm grant com.android.microdroid.benchmark android.permission.MANAGE_VIRTUAL_MACHINE" />
|
||||
</target_preparer>
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
<option name="package" value="com.android.microdroid.benchmark" />
|
||||
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
|
||||
<option name="shell-timeout" value="300000" />
|
||||
<option name="test-timeout" value="300000" />
|
||||
</test>
|
||||
</configuration>
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"os": {
|
||||
"name": "microdroid"
|
||||
},
|
||||
"task": {
|
||||
"type": "microdroid_launcher",
|
||||
"command": "MicrodroidBenchmarkNativeLib.so"
|
||||
},
|
||||
"export_tombstones": true
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.microdroid.benchmark;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.TruthJUnit.assume;
|
||||
|
||||
import static org.junit.Assume.assumeNoException;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.SystemProperties;
|
||||
import android.sysprop.HypervisorProperties;
|
||||
import android.system.virtualizationservice.DeathReason;
|
||||
import android.system.virtualmachine.VirtualMachine;
|
||||
import android.system.virtualmachine.VirtualMachineCallback;
|
||||
import android.system.virtualmachine.VirtualMachineConfig;
|
||||
import android.system.virtualmachine.VirtualMachineConfig.DebugLevel;
|
||||
import android.system.virtualmachine.VirtualMachineException;
|
||||
import android.system.virtualmachine.VirtualMachineManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.Timeout;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@RunWith(Parameterized.class)
|
||||
public class MicrodroidBenchmarks {
|
||||
private static final String TAG = "MicrodroidBenchmarks";
|
||||
|
||||
@Rule public Timeout globalTimeout = Timeout.seconds(300);
|
||||
|
||||
private static final String KERNEL_VERSION = SystemProperties.get("ro.kernel.version");
|
||||
|
||||
/** Copy output from the VM to logcat. This is helpful when things go wrong. */
|
||||
private static void logVmOutput(InputStream vmOutputStream, String name) {
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
BufferedReader reader =
|
||||
new BufferedReader(new InputStreamReader(vmOutputStream));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null
|
||||
&& !Thread.interrupted()) {
|
||||
Log.i(TAG, name + ": " + line);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.w(TAG, name, e);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private static class Inner {
|
||||
public boolean mProtectedVm;
|
||||
public Context mContext;
|
||||
public VirtualMachineManager mVmm;
|
||||
public VirtualMachine mVm;
|
||||
|
||||
Inner(boolean protectedVm) {
|
||||
mProtectedVm = protectedVm;
|
||||
}
|
||||
|
||||
/** Create a new VirtualMachineConfig.Builder with the parameterized protection mode. */
|
||||
public VirtualMachineConfig.Builder newVmConfigBuilder(String payloadConfigPath) {
|
||||
return new VirtualMachineConfig.Builder(mContext, payloadConfigPath)
|
||||
.protectedVm(mProtectedVm);
|
||||
}
|
||||
}
|
||||
|
||||
@Parameterized.Parameters(name = "protectedVm={0}")
|
||||
public static Object[] protectedVmConfigs() {
|
||||
return new Object[] {false, true};
|
||||
}
|
||||
|
||||
@Parameterized.Parameter public boolean mProtectedVm;
|
||||
|
||||
private boolean mPkvmSupported = false;
|
||||
private Inner mInner;
|
||||
|
||||
private Instrumentation mInstrumentation;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
// In case when the virt APEX doesn't exist on the device, classes in the
|
||||
// android.system.virtualmachine package can't be loaded. Therefore, before using the
|
||||
// classes, check the existence of a class in the package and skip this test if not exist.
|
||||
try {
|
||||
Class.forName("android.system.virtualmachine.VirtualMachineManager");
|
||||
mPkvmSupported = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
assumeNoException(e);
|
||||
return;
|
||||
}
|
||||
if (mProtectedVm) {
|
||||
assume().withMessage("Skip where protected VMs aren't support")
|
||||
.that(HypervisorProperties.hypervisor_protected_vm_supported().orElse(false))
|
||||
.isTrue();
|
||||
} else {
|
||||
assume().withMessage("Skip where VMs aren't support")
|
||||
.that(HypervisorProperties.hypervisor_vm_supported().orElse(false))
|
||||
.isTrue();
|
||||
}
|
||||
mInner = new Inner(mProtectedVm);
|
||||
mInner.mContext = ApplicationProvider.getApplicationContext();
|
||||
mInner.mVmm = VirtualMachineManager.getInstance(mInner.mContext);
|
||||
mInstrumentation = getInstrumentation();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() throws VirtualMachineException {
|
||||
if (!mPkvmSupported) {
|
||||
return;
|
||||
}
|
||||
if (mInner == null) {
|
||||
return;
|
||||
}
|
||||
if (mInner.mVm == null) {
|
||||
return;
|
||||
}
|
||||
mInner.mVm.stop();
|
||||
mInner.mVm.delete();
|
||||
}
|
||||
|
||||
private abstract static class VmEventListener implements VirtualMachineCallback {
|
||||
private ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
|
||||
|
||||
void runToFinish(VirtualMachine vm) throws VirtualMachineException, InterruptedException {
|
||||
vm.setCallback(mExecutorService, this);
|
||||
vm.run();
|
||||
logVmOutput(vm.getConsoleOutputStream(), "Console");
|
||||
logVmOutput(vm.getLogOutputStream(), "Log");
|
||||
mExecutorService.awaitTermination(300, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
void forceStop(VirtualMachine vm) {
|
||||
try {
|
||||
vm.clearCallback();
|
||||
vm.stop();
|
||||
mExecutorService.shutdown();
|
||||
} catch (VirtualMachineException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {}
|
||||
|
||||
@Override
|
||||
public void onPayloadReady(VirtualMachine vm) {}
|
||||
|
||||
@Override
|
||||
public void onPayloadFinished(VirtualMachine vm, int exitCode) {}
|
||||
|
||||
@Override
|
||||
public void onError(VirtualMachine vm, int errorCode, String message) {}
|
||||
|
||||
@Override
|
||||
@CallSuper
|
||||
public void onDied(VirtualMachine vm, @DeathReason int reason) {
|
||||
mExecutorService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private static class BootResult {
|
||||
public final boolean payloadStarted;
|
||||
public final int deathReason;
|
||||
|
||||
BootResult(boolean payloadStarted, int deathReason) {
|
||||
this.payloadStarted = payloadStarted;
|
||||
this.deathReason = deathReason;
|
||||
}
|
||||
}
|
||||
|
||||
private BootResult tryBootVm(String vmName)
|
||||
throws VirtualMachineException, InterruptedException {
|
||||
mInner.mVm = mInner.mVmm.get(vmName); // re-load the vm before running tests
|
||||
final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
|
||||
final CompletableFuture<Integer> deathReason = new CompletableFuture<>();
|
||||
VmEventListener listener =
|
||||
new VmEventListener() {
|
||||
@Override
|
||||
public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
|
||||
payloadStarted.complete(true);
|
||||
forceStop(vm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDied(VirtualMachine vm, int reason) {
|
||||
deathReason.complete(reason);
|
||||
super.onDied(vm, reason);
|
||||
}
|
||||
};
|
||||
listener.runToFinish(mInner.mVm);
|
||||
return new BootResult(
|
||||
payloadStarted.getNow(false), deathReason.getNow(DeathReason.INFRASTRUCTURE_ERROR));
|
||||
}
|
||||
|
||||
private boolean canBootMicrodroidWithMemory(int mem)
|
||||
throws VirtualMachineException, InterruptedException, IOException {
|
||||
final int trialCount = 5;
|
||||
|
||||
// returns true if succeeded at least once.
|
||||
for (int i = 0; i < trialCount; i++) {
|
||||
VirtualMachine existingVm = mInner.mVmm.get("test_vm_minimum_memory");
|
||||
if (existingVm != null) {
|
||||
existingVm.delete();
|
||||
}
|
||||
|
||||
VirtualMachineConfig.Builder builder =
|
||||
mInner.newVmConfigBuilder("assets/vm_config.json");
|
||||
VirtualMachineConfig normalConfig =
|
||||
builder.debugLevel(DebugLevel.FULL).memoryMib(mem).build();
|
||||
mInner.mVmm.create("test_vm_minimum_memory", normalConfig);
|
||||
|
||||
if (tryBootVm("test_vm_minimum_memory").payloadStarted) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinimumRequiredRAM()
|
||||
throws VirtualMachineException, InterruptedException, IOException {
|
||||
int lo = 16, hi = 512, minimum = 0;
|
||||
boolean found = false;
|
||||
|
||||
// TODO(b/236672526): giving inefficient memory to pVM sometimes causes host crash.
|
||||
assume().withMessage("Skip on pVM. b/236672526").that(mProtectedVm).isFalse();
|
||||
|
||||
while (lo <= hi) {
|
||||
int mid = (lo + hi) / 2;
|
||||
if (canBootMicrodroidWithMemory(mid)) {
|
||||
found = true;
|
||||
minimum = mid;
|
||||
hi = mid - 1;
|
||||
} else {
|
||||
lo = mid + 1;
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(found).isTrue();
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putInt("microdroid_minimum_required_memory", minimum);
|
||||
mInstrumentation.sendStatus(0, bundle);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
|
||||
extern "C" int android_native_main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) {
|
||||
// do nothing for now; just leave it alive. good night.
|
||||
for (;;) {
|
||||
sleep(1000);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue