Add payload linker namespace test
Bug: 261110181
Test: atest MicrodroidTestApp
Change-Id: Id95c91a1ac75f7df5ae6e38980c626f82cdf5248
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index bd5b180..72a0090 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -132,6 +132,8 @@
private OptionalLong mKernelStartedNanoTime = OptionalLong.empty();
private OptionalLong mInitStartedNanoTime = OptionalLong.empty();
private OptionalLong mPayloadStartedNanoTime = OptionalLong.empty();
+ private StringBuilder mConsoleOutput = new StringBuilder();
+ private StringBuilder mLogOutput = new StringBuilder();
private void processBootEvents(String log) {
if (!mVcpuStartedNanoTime.isPresent()) {
@@ -149,44 +151,50 @@
}
}
- private void logVmOutputAndMonitorBootEvents(String tag,
+ private void logVmOutputAndMonitorBootEvents(
+ String tag,
InputStream vmOutputStream,
String name,
+ StringBuilder result,
boolean monitorEvents) {
new Thread(
- () -> {
- try {
- BufferedReader reader =
- new BufferedReader(new InputStreamReader(vmOutputStream));
- String line;
- while ((line = reader.readLine()) != null
- && !Thread.interrupted()) {
- if (monitorEvents) processBootEvents(line);
- Log.i(tag, name + ": " + line);
- }
- } catch (Exception e) {
- Log.w(tag, name, e);
- }
- }).start();
+ () -> {
+ try {
+ BufferedReader reader =
+ new BufferedReader(
+ new InputStreamReader(vmOutputStream));
+ String line;
+ while ((line = reader.readLine()) != null
+ && !Thread.interrupted()) {
+ if (monitorEvents) processBootEvents(line);
+ Log.i(tag, name + ": " + line);
+ result.append(line + "\n");
+ }
+ } catch (Exception e) {
+ Log.w(tag, name, e);
+ }
+ })
+ .start();
}
- private void logVmOutputAndMonitorBootEvents(String tag,
- InputStream vmOutputStream,
- String name) {
- logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, true);
+ private void logVmOutputAndMonitorBootEvents(
+ String tag, InputStream vmOutputStream, String name, StringBuilder result) {
+ logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, result, true);
}
/** Copy output from the VM to logcat. This is helpful when things go wrong. */
- protected void logVmOutput(String tag, InputStream vmOutputStream, String name) {
- logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, false);
+ protected void logVmOutput(
+ String tag, InputStream vmOutputStream, String name, StringBuilder result) {
+ logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, result, false);
}
public void runToFinish(String logTag, VirtualMachine vm)
throws VirtualMachineException, InterruptedException {
vm.setCallback(mExecutorService, this);
vm.run();
- logVmOutputAndMonitorBootEvents(logTag, vm.getConsoleOutput(), "Console");
- logVmOutput(logTag, vm.getLogOutput(), "Log");
+ logVmOutputAndMonitorBootEvents(
+ logTag, vm.getConsoleOutput(), "Console", mConsoleOutput);
+ logVmOutput(logTag, vm.getLogOutput(), "Log", mLogOutput);
mExecutorService.awaitTermination(300, TimeUnit.SECONDS);
}
@@ -206,6 +214,14 @@
return mPayloadStartedNanoTime;
}
+ public String getConsoleOutput() {
+ return mConsoleOutput.toString();
+ }
+
+ public String getLogOutput() {
+ return mLogOutput.toString();
+ }
+
protected void forceStop(VirtualMachine vm) {
try {
vm.stop();
@@ -248,14 +264,20 @@
public final OptionalLong initStartedNanoTime;
public final OptionalLong payloadStartedNanoTime;
- BootResult(boolean payloadStarted,
+ public final String consoleOutput;
+ public final String logOutput;
+
+ BootResult(
+ boolean payloadStarted,
int deathReason,
long apiCallNanoTime,
long endToEndNanoTime,
OptionalLong vcpuStartedNanoTime,
OptionalLong kernelStartedNanoTime,
OptionalLong initStartedNanoTime,
- OptionalLong payloadStartedNanoTime) {
+ OptionalLong payloadStartedNanoTime,
+ String consoleOutput,
+ String logOutput) {
this.apiCallNanoTime = apiCallNanoTime;
this.payloadStarted = payloadStarted;
this.deathReason = deathReason;
@@ -264,6 +286,8 @@
this.kernelStartedNanoTime = kernelStartedNanoTime;
this.initStartedNanoTime = initStartedNanoTime;
this.payloadStartedNanoTime = payloadStartedNanoTime;
+ this.consoleOutput = consoleOutput;
+ this.logOutput = logOutput;
}
private long getVcpuStartedNanoTime() {
@@ -332,7 +356,9 @@
listener.getVcpuStartedNanoTime(),
listener.getKernelStartedNanoTime(),
listener.getInitStartedNanoTime(),
- listener.getPayloadStartedNanoTime());
+ listener.getPayloadStartedNanoTime(),
+ listener.getConsoleOutput(),
+ listener.getLogOutput());
}
/** Execute a command. Returns stdout. */
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 4dc9489..edb4759 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -23,6 +23,8 @@
jni_libs: [
"MicrodroidTestNativeLib",
"MicrodroidIdleNativeLib",
+ "MicrodroidEmptyNativeLib",
+ "MicrodroidPrivateLinkingNativeLib",
],
jni_uses_platform_apis: true,
use_embedded_native_libs: true,
@@ -62,3 +64,21 @@
header_libs: ["vm_payload_headers"],
stl: "libc++_static",
}
+
+// An empty payload missing AVmPayload_main
+cc_library_shared {
+ name: "MicrodroidEmptyNativeLib",
+ srcs: ["src/native/emptybinary.cpp"],
+ stl: "none",
+}
+
+// A payload which tries to link against libselinux, one of private libraries
+cc_library_shared {
+ name: "MicrodroidPrivateLinkingNativeLib",
+ srcs: ["src/native/idlebinary.cpp"],
+ header_libs: ["vm_payload_headers"],
+ // HACK: linking against "libselinux" will embed libselinux.so into the apk
+ // link against a stub to prevent libselinux.so from being embedded
+ shared_libs: ["libselinux#latest"],
+ stl: "libc++_static",
+}
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index fa8be93..95b4760 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -828,6 +828,50 @@
VirtualMachineCallback.STOP_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR);
}
+ // Checks whether microdroid_launcher started but payload failed. reason must be recorded in the
+ // console output.
+ private void assertThatPayloadFailsDueTo(VirtualMachine vm, String reason) throws Exception {
+ final CompletableFuture<Boolean> payloadStarted = new CompletableFuture<>();
+ final CompletableFuture<Integer> exitCodeFuture = new CompletableFuture<>();
+ VmEventListener listener =
+ new VmEventListener() {
+ @Override
+ public void onPayloadStarted(VirtualMachine vm) {
+ payloadStarted.complete(true);
+ }
+
+ @Override
+ public void onPayloadFinished(VirtualMachine vm, int exitCode) {
+ exitCodeFuture.complete(exitCode);
+ }
+ };
+ listener.runToFinish(TAG, vm);
+
+ assertThat(payloadStarted.getNow(false)).isTrue();
+ assertThat(exitCodeFuture.getNow(0)).isNotEqualTo(0);
+ assertThat(listener.getConsoleOutput()).contains(reason);
+ }
+
+ @Test
+ public void bootFailsWhenBinaryIsMissingEntryFunction() throws Exception {
+ VirtualMachineConfig.Builder builder =
+ newVmConfigBuilder().setPayloadBinaryPath("MicrodroidEmptyNativeLib.so");
+ VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_missing_entry", normalConfig);
+
+ assertThatPayloadFailsDueTo(vm, "Failed to find entrypoint");
+ }
+
+ @Test
+ public void bootFailsWhenBinaryTriesToLinkAgainstPrivateLibs() throws Exception {
+ VirtualMachineConfig.Builder builder =
+ newVmConfigBuilder().setPayloadBinaryPath("MicrodroidPrivateLinkingNativeLib.so");
+ VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
+ VirtualMachine vm = forceCreateNewVirtualMachine("test_vm_private_linking", normalConfig);
+
+ assertThatPayloadFailsDueTo(vm, "Failed to dlopen");
+ }
+
@Test
public void sameInstancesShareTheSameVmObject() throws Exception {
VirtualMachineConfig config =
diff --git a/tests/testapk/src/native/emptybinary.cpp b/tests/testapk/src/native/emptybinary.cpp
new file mode 100644
index 0000000..15f3f4d
--- /dev/null
+++ b/tests/testapk/src/native/emptybinary.cpp
@@ -0,0 +1 @@
+// a binary without AVmPayload_main