Export tombstones by default for debuggable VMs

VM config's export_tombstones is now optional. Default is true for
debuggable VMs, false for non-debuggable VMs.

Bug: 250165198
Test: atest MicrodroidHostTestCases
Change-Id: I818bcf3446b5bf7f560487265d82cd08f97c277e
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index a890770..d40dcba 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -514,24 +514,7 @@
         vmInfo.mProcess.destroy();
     }
 
-    private boolean isTombstoneGenerated(String configPath, String... crashCommand)
-            throws Exception {
-        // Note this test relies on logcat values being printed by tombstone_transmit on
-        // and the reeceiver on host (virtualization_service)
-        mMicrodroidDevice =
-                MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
-                        .debugLevel("full")
-                        .memoryMib(minMemorySize())
-                        .numCpus(NUM_VCPUS)
-                        .build(getAndroidDevice());
-        mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
-        mMicrodroidDevice.enableAdbRoot();
-
-        CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
-        microdroid.run(crashCommand);
-
-        // check until microdroid is shut down
-        CommandRunner android = new CommandRunner(getDevice());
+    private void waitForCrosvmExit(CommandRunner android) throws Exception {
         // TODO: improve crosvm exit check. b/258848245
         android.runWithTimeout(
                 15000,
@@ -540,8 +523,12 @@
                 "1",
                 "-e",
                 "'virtualizationmanager::crosvm.*exited with status exit status:'");
+    }
 
-        // Check that tombstone is received (from host logcat)
+    private boolean isTombstoneReceivedFromHostLogcat() throws Exception {
+        // Note this method relies on logcat values being printed by the receiver on host
+        // userspace crash log: virtualizationservice/src/aidl.rs
+        // kernel ramdump log: virtualizationmanager/src/crosvm.rs
         String ramdumpRegex =
                 "Received [0-9]+ bytes from guest & wrote to tombstone file|"
                         + "Ramdump \"[^ ]+/ramdump\" sent to tombstoned";
@@ -561,11 +548,32 @@
         return !result.trim().isEmpty();
     }
 
+    private boolean isTombstoneGeneratedWithCmd(String configPath, String... crashCommand)
+            throws Exception {
+        mMicrodroidDevice =
+                MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+                        .debugLevel("full")
+                        .memoryMib(minMemorySize())
+                        .numCpus(NUM_VCPUS)
+                        .build(getAndroidDevice());
+        mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
+        mMicrodroidDevice.enableAdbRoot();
+
+        CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
+        microdroid.run(crashCommand);
+
+        // check until microdroid is shut down
+        CommandRunner android = new CommandRunner(getDevice());
+        waitForCrosvmExit(android);
+
+        return isTombstoneReceivedFromHostLogcat();
+    }
+
     @Test
     public void testTombstonesAreGeneratedUponUserspaceCrash() throws Exception {
         assertThat(
-                        isTombstoneGenerated(
-                                "assets/vm_config_crash.json",
+                        isTombstoneGeneratedWithCmd(
+                                "assets/vm_config.json",
                                 "kill",
                                 "-SIGSEGV",
                                 "$(pidof microdroid_launcher)"))
@@ -575,8 +583,8 @@
     @Test
     public void testTombstonesAreNotGeneratedIfNotExportedUponUserspaceCrash() throws Exception {
         assertThat(
-                        isTombstoneGenerated(
-                                "assets/vm_config_crash_no_tombstone.json",
+                        isTombstoneGeneratedWithCmd(
+                                "assets/vm_config_no_tombstone.json",
                                 "kill",
                                 "-SIGSEGV",
                                 "$(pidof microdroid_launcher)"))
@@ -589,15 +597,45 @@
         assumeFalse("Cuttlefish is not supported", isCuttlefish());
         assumeFalse("Skipping test because ramdump is disabled on user build", isUserBuild());
         assertThat(
-                        isTombstoneGenerated(
-                                "assets/vm_config_crash.json",
-                                "echo",
-                                "c",
-                                ">",
-                                "/proc/sysrq-trigger"))
+                        isTombstoneGeneratedWithCmd(
+                                "assets/vm_config.json", "echo", "c", ">", "/proc/sysrq-trigger"))
                 .isTrue();
     }
 
+    private boolean isTombstoneGeneratedWithCrashPayload(boolean debuggable) throws Exception {
+        // we can't use microdroid builder as it wants ADB connection (debuggable)
+        CommandRunner android = new CommandRunner(getDevice());
+
+        android.run("rm", "-rf", TEST_ROOT + "*");
+        android.run("mkdir", "-p", TEST_ROOT + "*");
+
+        final String apkPath = getPathForPackage(PACKAGE_NAME);
+        final String idsigPath = TEST_ROOT + "idsig";
+        final String instanceImgPath = TEST_ROOT + "instance.img";
+        android.run(
+                VIRT_APEX + "bin/vm",
+                "run-app",
+                "--payload-binary-name",
+                "MicrodroidCrashNativeLib.so",
+                "--debug",
+                debuggable ? "full" : "none",
+                apkPath,
+                idsigPath,
+                instanceImgPath);
+
+        return isTombstoneReceivedFromHostLogcat();
+    }
+
+    @Test
+    public void testTombstonesAreGeneratedWithCrashPayload() throws Exception {
+        assertThat(isTombstoneGeneratedWithCrashPayload(true /* debuggable */)).isTrue();
+    }
+
+    @Test
+    public void testTombstonesAreNotGeneratedWithCrashPayloadWhenNonDebuggable() throws Exception {
+        assertThat(isTombstoneGeneratedWithCrashPayload(false /* debuggable */)).isFalse();
+    }
+
     @Test
     public void testTelemetryPushedAtoms() throws Exception {
         // Reset statsd config and report before the test
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 5f9b915..d8f0e11 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -26,6 +26,7 @@
         "MicrodroidEmptyNativeLib",
         "MicrodroidExitNativeLib",
         "MicrodroidPrivateLinkingNativeLib",
+        "MicrodroidCrashNativeLib",
     ],
     jni_uses_platform_apis: true,
     use_embedded_native_libs: true,
@@ -92,3 +93,11 @@
     shared_libs: ["libselinux#latest"],
     stl: "libc++_static",
 }
+
+// A payload that crashes immediately on start
+cc_library_shared {
+    name: "MicrodroidCrashNativeLib",
+    srcs: ["src/native/crashbinary.cpp"],
+    header_libs: ["vm_payload_headers"],
+    stl: "libc++_static",
+}
diff --git a/tests/testapk/assets/vm_config_crash.json b/tests/testapk/assets/vm_config_crash.json
deleted file mode 100644
index 3ec34a3..0000000
--- a/tests/testapk/assets/vm_config_crash.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-    "os": {
-      "name": "microdroid"
-    },
-    "task": {
-      "type": "microdroid_launcher",
-      "command": "MicrodroidIdleNativeLib.so"
-    },
-    "export_tombstones": true
-  }
diff --git a/tests/testapk/assets/vm_config_crash_no_tombstone.json b/tests/testapk/assets/vm_config_crash_no_tombstone.json
deleted file mode 100644
index 9678e38..0000000
--- a/tests/testapk/assets/vm_config_crash_no_tombstone.json
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-    "os": {
-      "name": "microdroid"
-    },
-    "task": {
-      "type": "microdroid_launcher",
-      "command": "MicrodroidIdleNativeLib.so"
-    },
-    "export_tombstones": false
-  }
diff --git a/tests/testapk/assets/vm_config_no_tombstone.json b/tests/testapk/assets/vm_config_no_tombstone.json
new file mode 100644
index 0000000..97e764d
--- /dev/null
+++ b/tests/testapk/assets/vm_config_no_tombstone.json
@@ -0,0 +1,10 @@
+{
+  "os": {
+    "name": "microdroid"
+  },
+  "task": {
+    "type": "microdroid_launcher",
+    "command": "MicrodroidTestNativeLib.so"
+  },
+  "export_tombstones": false
+}
diff --git a/tests/testapk/src/native/crashbinary.cpp b/tests/testapk/src/native/crashbinary.cpp
new file mode 100644
index 0000000..27f10ec
--- /dev/null
+++ b/tests/testapk/src/native/crashbinary.cpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2023 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.
+ */
+
+// A binary killing itself by SIGABRT.
+#include <stdlib.h>
+#include <unistd.h>
+extern "C" int AVmPayload_main() {
+    abort();
+}