Merge "Load crashkernel using kexec"
diff --git a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
index 6956ab1..7b2cad9 100644
--- a/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
+++ b/.prebuilt_info/prebuilt_info_pvmfw_pvmfw_img.asciipb
@@ -1,6 +1,6 @@
 drops {
   android_build_drop {
-    build_id: "8819384"
+    build_id: "8964254"
     target: "u-boot_pvmfw"
     source_file: "pvmfw.img"
   }
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index 770f489..946bc5b 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -102,6 +102,7 @@
 
         let config_path = parameters.config_path.as_deref().unwrap_or(DEFAULT_VM_CONFIG_PATH);
         let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
+            name: String::from("Compos"),
             apk: Some(apk_fd),
             idsig: Some(idsig_fd),
             instanceImage: Some(instance_fd),
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index 955b350..cc13ae5 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -379,6 +379,7 @@
             }
 
             VirtualMachineAppConfig appConfig = getConfig().toParcel();
+            appConfig.name = mName;
 
             // Fill the idsig file by hashing the apk
             service.createOrUpdateIdsigFile(
diff --git a/libs/vmconfig/src/lib.rs b/libs/vmconfig/src/lib.rs
index 607b347..7ca8272 100644
--- a/libs/vmconfig/src/lib.rs
+++ b/libs/vmconfig/src/lib.rs
@@ -33,6 +33,8 @@
 /// Configuration for a particular VM to be started.
 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
 pub struct VmConfig {
+    /// The name of VM.
+    pub name: Option<String>,
     /// The filename of the kernel image, if any.
     pub kernel: Option<PathBuf>,
     /// The filename of the initial ramdisk for the kernel, if any.
@@ -91,6 +93,7 @@
         } else {
             0
         };
+
         Ok(VirtualMachineRawConfig {
             kernel: maybe_open_parcel_file(&self.kernel, false)?,
             initrd: maybe_open_parcel_file(&self.initrd, false)?,
diff --git a/pvmfw/pvmfw.img b/pvmfw/pvmfw.img
index b360bda..7cc8009 100644
--- a/pvmfw/pvmfw.img
+++ b/pvmfw/pvmfw.img
Binary files differ
diff --git a/rialto/tests/test.rs b/rialto/tests/test.rs
index fb6a1ad..8a78861 100644
--- a/rialto/tests/test.rs
+++ b/rialto/tests/test.rs
@@ -54,6 +54,7 @@
     let log = android_log_fd()?;
 
     let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
+        name: String::from("RialtoTest"),
         kernel: None,
         initrd: None,
         params: None,
diff --git a/tests/hostside/Android.bp b/tests/hostside/Android.bp
index 5ce19bd..d77f3de 100644
--- a/tests/hostside/Android.bp
+++ b/tests/hostside/Android.bp
@@ -15,6 +15,7 @@
     static_libs: [
         "MicrodroidHostTestHelper",
         "compatibility-host-util",
+        "cts-statsd-atom-host-test-utils",
     ],
     per_testcase_directory: true,
     data: [
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
index 76bfafb..5ac36a0 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
@@ -25,13 +25,19 @@
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.junit.Assume.assumeTrue;
 
+import android.cts.statsdatom.lib.ConfigUtils;
+import android.cts.statsdatom.lib.ReportUtils;
+
 import com.android.compatibility.common.util.CddTest;
+import com.android.os.AtomsProto;
+import com.android.os.StatsLog;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.TestDescription;
 import com.android.tradefed.result.TestResult;
@@ -472,6 +478,89 @@
     }
 
     @Test
+    public void testTelemetryPushedAtoms() throws Exception {
+        // Reset statsd config and report before the test
+        ConfigUtils.removeConfig(getDevice());
+        ReportUtils.clearReports(getDevice());
+
+        // Setup statsd config
+        int[] atomIds = {
+            AtomsProto.Atom.VM_CREATION_REQUESTED_FIELD_NUMBER,
+            AtomsProto.Atom.VM_BOOTED_FIELD_NUMBER,
+            AtomsProto.Atom.VM_EXITED_FIELD_NUMBER,
+        };
+        ConfigUtils.uploadConfigForPushedAtoms(getDevice(), PACKAGE_NAME, atomIds);
+
+        // Create VM with microdroid
+        final String configPath = "assets/vm_config_apex.json"; // path inside the APK
+        final String cid =
+                startMicrodroid(
+                        getDevice(),
+                        getBuild(),
+                        APK_NAME,
+                        PACKAGE_NAME,
+                        configPath,
+                        /* debug */ true,
+                        minMemorySize(),
+                        Optional.of(NUM_VCPUS),
+                        Optional.of(CPU_AFFINITY));
+
+        // Check VmCreationRequested atom and clear the statsd report
+        List<StatsLog.EventMetricData> data;
+        data = ReportUtils.getEventMetricDataList(getDevice());
+        assertEquals(1, data.size());
+        assertEquals(
+                AtomsProto.Atom.VM_CREATION_REQUESTED_FIELD_NUMBER,
+                data.get(0).getAtom().getPushedCase().getNumber());
+        AtomsProto.VmCreationRequested atomVmCreationRequested =
+                data.get(0).getAtom().getVmCreationRequested();
+        assertEquals(
+                AtomsProto.VmCreationRequested.Hypervisor.PKVM,
+                atomVmCreationRequested.getHypervisor());
+        assertFalse(atomVmCreationRequested.getIsProtected());
+        assertTrue(atomVmCreationRequested.getCreationSucceeded());
+        assertEquals(0, atomVmCreationRequested.getBinderExceptionCode());
+        assertEquals("VmRunApp", atomVmCreationRequested.getVmIdentifier());
+        assertEquals(
+                AtomsProto.VmCreationRequested.ConfigType.VIRTUAL_MACHINE_APP_CONFIG,
+                atomVmCreationRequested.getConfigType());
+        assertEquals(NUM_VCPUS, atomVmCreationRequested.getNumCpus());
+        assertEquals(CPU_AFFINITY, atomVmCreationRequested.getCpuAffinity());
+        assertEquals(minMemorySize(), atomVmCreationRequested.getMemoryMib());
+        assertEquals(
+                "com.android.art:com.android.compos:com.android.sdkext",
+                atomVmCreationRequested.getApexes());
+
+        // Boot VM with microdroid
+        adbConnectToMicrodroid(getDevice(), cid);
+        waitForBootComplete();
+
+        // Check VmBooted atom and clear the statsd report
+        data = ReportUtils.getEventMetricDataList(getDevice());
+        assertEquals(1, data.size());
+        assertEquals(
+                AtomsProto.Atom.VM_BOOTED_FIELD_NUMBER,
+                data.get(0).getAtom().getPushedCase().getNumber());
+        AtomsProto.VmBooted atomVmBooted = data.get(0).getAtom().getVmBooted();
+        assertEquals("VmRunApp", atomVmBooted.getVmIdentifier());
+
+        // Shutdown VM with microdroid
+        shutdownMicrodroid(getDevice(), cid);
+        // TODO: make sure the VM is completely shut down while 'vm stop' command running.
+        Thread.sleep(1000);
+
+        // Check VmExited atom and clear the statsd report
+        data = ReportUtils.getEventMetricDataList(getDevice());
+        assertEquals(1, data.size());
+        assertEquals(
+                AtomsProto.Atom.VM_EXITED_FIELD_NUMBER,
+                data.get(0).getAtom().getPushedCase().getNumber());
+        AtomsProto.VmExited atomVmExited = data.get(0).getAtom().getVmExited();
+        assertEquals("VmRunApp", atomVmExited.getVmIdentifier());
+        assertEquals(AtomsProto.VmExited.DeathReason.KILLED, atomVmExited.getDeathReason());
+    }
+
+    @Test
     @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C/1-3"})
     public void testMicrodroidBoots() throws Exception {
         final String configPath = "assets/vm_config.json"; // path inside the APK
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 29a74ca..60912ea 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -25,6 +25,7 @@
     use_embedded_native_libs: true,
     // We only support 64-bit ABI, but CTS demands all APKs to be multi-ABI.
     compile_multilib: "both",
+    min_sdk_version: "33",
 }
 
 // TODO(jiyong): make this a binary, not a shared library
diff --git a/tests/testapk/AndroidManifest.xml b/tests/testapk/AndroidManifest.xml
index bc955d2..9c8b2d5 100644
--- a/tests/testapk/AndroidManifest.xml
+++ b/tests/testapk/AndroidManifest.xml
@@ -16,6 +16,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.microdroid.test">
     <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+    <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
     <application>
         <uses-library android:name="android.system.virtualmachine" android:required="false" />
     </application>
diff --git a/tests/testapk/assets/vm_config_apex.json b/tests/testapk/assets/vm_config_apex.json
new file mode 100644
index 0000000..0f100aa
--- /dev/null
+++ b/tests/testapk/assets/vm_config_apex.json
@@ -0,0 +1,25 @@
+{
+  "os": {
+    "name": "microdroid"
+  },
+  "task": {
+    "type": "microdroid_launcher",
+    "command": "MicrodroidTestNativeLib.so",
+    "args": [
+      "hello",
+      "microdroid"
+    ]
+  },
+  "apexes": [
+    {
+      "name": "com.android.art"
+    },
+    {
+      "name": "com.android.compos"
+    },
+    {
+      "name": "com.android.sdkext"
+    }
+  ],
+  "export_tombstones": true
+}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 22b8a94..8eb5497 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -17,6 +17,9 @@
 
 /** Configuration for running an App in a VM */
 parcelable VirtualMachineAppConfig {
+    /** Name of VM */
+    String name;
+
     /** Main APK */
     ParcelFileDescriptor apk;
 
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index 83a81a0..d11de03 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -19,6 +19,9 @@
 
 /** Raw configuration for running a VM. */
 parcelable VirtualMachineRawConfig {
+    /** Name of VM */
+    String name;
+
     /** The kernel image, if any. */
     @nullable ParcelFileDescriptor kernel;
 
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index bba75ac..5f4b7a7 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -14,6 +14,7 @@
 
 //! Implementation of the AIDL interface of the VirtualizationService.
 
+use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
 use crate::composite::make_composite_image;
 use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmInstance, VmState};
 use crate::payload::add_microdroid_images;
@@ -48,11 +49,10 @@
 use binder_common::rpc_server::run_rpc_server_with_factory;
 use disk::QcowFile;
 use idsig::{HashAlgorithm, V4Signature};
-use log::{debug, error, info, warn, trace};
+use log::{debug, error, info, warn};
 use microdroid_payload_config::VmPayloadConfig;
 use rustutils::system_properties;
 use semver::VersionReq;
-use statslog_virtualization_rust::vm_creation_requested::{stats_write, Hypervisor};
 use std::convert::TryInto;
 use std::ffi::CStr;
 use std::fs::{create_dir, File, OpenOptions};
@@ -131,23 +131,7 @@
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
         let mut is_protected = false;
         let ret = self.create_vm_internal(config, console_fd, log_fd, &mut is_protected);
-        match ret {
-            Ok(_) => {
-                let ok_status = Status::ok();
-                write_vm_creation_stats(
-                    is_protected,
-                    /*creation_succeeded*/ true,
-                    ok_status.exception_code() as i32,
-                );
-            }
-            Err(ref e) => {
-                write_vm_creation_stats(
-                    is_protected,
-                    /*creation_succeeded*/ false,
-                    e.exception_code() as i32,
-                );
-            }
-        }
+        write_vm_creation_stats(config, is_protected, &ret);
         ret
     }
 
@@ -468,6 +452,7 @@
         // Actually start the VM.
         let crosvm_config = CrosvmConfig {
             cid,
+            name: config.name.clone(),
             bootloader: maybe_clone_file(&config.bootloader)?,
             kernel: maybe_clone_file(&config.kernel)?,
             initrd: maybe_clone_file(&config.initrd)?,
@@ -506,16 +491,6 @@
     }
 }
 
-/// Write the stats of VMCreation to statsd
-fn write_vm_creation_stats(is_protected: bool, creation_succeeded: bool, exception_code: i32) {
-    match stats_write(Hypervisor::Pkvm, is_protected, creation_succeeded, exception_code) {
-        Err(e) => {
-            warn!("statslog_rust failed with error: {}", e);
-        }
-        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
-    }
-}
-
 /// Waits for incoming connections from VM. If a new connection is made, stores the stream in the
 /// corresponding `VmInstance`.
 fn handle_stream_connection_from_vm(state: Arc<Mutex<State>>) -> Result<()> {
@@ -649,6 +624,7 @@
         vm_config.memoryMib = config.memoryMib;
     }
 
+    vm_config.name = config.name.clone();
     vm_config.protectedVm = config.protectedVm;
     vm_config.numCpus = config.numCpus;
     vm_config.cpuAffinity = config.cpuAffinity.clone();
@@ -1003,7 +979,7 @@
 }
 
 /// Converts a `&ParcelFileDescriptor` to a `File` by cloning the file.
-fn clone_file(file: &ParcelFileDescriptor) -> Result<File, Status> {
+pub fn clone_file(file: &ParcelFileDescriptor) -> Result<File, Status> {
     file.as_ref().try_clone().map_err(|e| {
         Status::new_exception_str(
             ExceptionCode::BAD_PARCELABLE,
@@ -1069,6 +1045,8 @@
             })?;
             let stream = vm.stream.lock().unwrap().take();
             vm.callbacks.notify_payload_started(cid, stream);
+
+            write_vm_booted_stats(vm.requester_uid as i32, &vm.name);
             Ok(())
         } else {
             error!("notifyPayloadStarted is called from an unknown CID {}", cid);
diff --git a/virtualizationservice/src/atom.rs b/virtualizationservice/src/atom.rs
new file mode 100644
index 0000000..feaa72a
--- /dev/null
+++ b/virtualizationservice/src/atom.rs
@@ -0,0 +1,179 @@
+// Copyright 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.
+
+//! Functions for creating and collecting atoms.
+
+use crate::aidl::clone_file;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+    DeathReason::DeathReason, IVirtualMachine::IVirtualMachine,
+    VirtualMachineAppConfig::VirtualMachineAppConfig, VirtualMachineConfig::VirtualMachineConfig,
+};
+use android_system_virtualizationservice::binder::{Status, Strong};
+use anyhow::{anyhow, Result};
+use binder::ThreadState;
+use log::{trace, warn};
+use microdroid_payload_config::VmPayloadConfig;
+use statslog_virtualization_rust::{vm_booted, vm_creation_requested, vm_exited};
+use zip::ZipArchive;
+
+fn get_vm_payload_config(config: &VirtualMachineAppConfig) -> Result<VmPayloadConfig> {
+    let apk = config.apk.as_ref().ok_or_else(|| anyhow!("APK is none"))?;
+    let apk_file = clone_file(apk)?;
+    let mut apk_zip = ZipArchive::new(&apk_file)?;
+    let config_file = apk_zip.by_name(&config.configPath)?;
+    let vm_payload_config: VmPayloadConfig = serde_json::from_reader(config_file)?;
+    Ok(vm_payload_config)
+}
+
+/// Write the stats of VMCreation to statsd
+pub fn write_vm_creation_stats(
+    config: &VirtualMachineConfig,
+    is_protected: bool,
+    ret: &binder::Result<Strong<dyn IVirtualMachine>>,
+) {
+    let creation_succeeded;
+    let binder_exception_code;
+    match ret {
+        Ok(_) => {
+            creation_succeeded = true;
+            binder_exception_code = Status::ok().exception_code() as i32;
+        }
+        Err(ref e) => {
+            creation_succeeded = false;
+            binder_exception_code = e.exception_code() as i32;
+        }
+    }
+
+    let vm_identifier;
+    let config_type;
+    let num_cpus;
+    let cpu_affinity;
+    let memory_mib;
+    let apexes;
+    match config {
+        VirtualMachineConfig::AppConfig(config) => {
+            vm_identifier = &config.name;
+            config_type = vm_creation_requested::ConfigType::VirtualMachineAppConfig;
+            num_cpus = config.numCpus;
+            cpu_affinity = config.cpuAffinity.clone().unwrap_or_default();
+            memory_mib = config.memoryMib;
+
+            let vm_payload_config = get_vm_payload_config(config);
+            if let Ok(vm_payload_config) = vm_payload_config {
+                apexes = vm_payload_config
+                    .apexes
+                    .iter()
+                    .map(|x| x.name.clone())
+                    .collect::<Vec<String>>()
+                    .join(":");
+            } else {
+                apexes = "INFO: Can't get VmPayloadConfig".into();
+            }
+        }
+        VirtualMachineConfig::RawConfig(config) => {
+            vm_identifier = &config.name;
+            config_type = vm_creation_requested::ConfigType::VirtualMachineRawConfig;
+            num_cpus = config.numCpus;
+            cpu_affinity = config.cpuAffinity.clone().unwrap_or_default();
+            memory_mib = config.memoryMib;
+            apexes = String::new();
+        }
+    }
+
+    let vm_creation_requested = vm_creation_requested::VmCreationRequested {
+        uid: ThreadState::get_calling_uid() as i32,
+        vm_identifier,
+        hypervisor: vm_creation_requested::Hypervisor::Pkvm,
+        is_protected,
+        creation_succeeded,
+        binder_exception_code,
+        config_type,
+        num_cpus,
+        cpu_affinity: &cpu_affinity,
+        memory_mib,
+        apexes: &apexes,
+        // TODO(seungjaeyoo) Fill information about task_profile
+        // TODO(seungjaeyoo) Fill information about disk_image for raw config
+    };
+
+    match vm_creation_requested.stats_write() {
+        Err(e) => {
+            warn!("statslog_rust failed with error: {}", e);
+        }
+        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    }
+}
+
+/// Write the stats of VM boot to statsd
+pub fn write_vm_booted_stats(uid: i32, vm_identifier: &String) {
+    let vm_booted = vm_booted::VmBooted { uid, vm_identifier };
+    match vm_booted.stats_write() {
+        Err(e) => {
+            warn!("statslog_rust failed with error: {}", e);
+        }
+        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    }
+}
+
+/// Write the stats of VM exit to statsd
+pub fn write_vm_exited_stats(uid: i32, vm_identifier: &String, reason: DeathReason) {
+    let vm_exited = vm_exited::VmExited {
+        uid,
+        vm_identifier,
+        death_reason: match reason {
+            DeathReason::INFRASTRUCTURE_ERROR => vm_exited::DeathReason::InfrastructureError,
+            DeathReason::KILLED => vm_exited::DeathReason::Killed,
+            DeathReason::UNKNOWN => vm_exited::DeathReason::Unknown,
+            DeathReason::SHUTDOWN => vm_exited::DeathReason::Shutdown,
+            DeathReason::ERROR => vm_exited::DeathReason::Error,
+            DeathReason::REBOOT => vm_exited::DeathReason::Reboot,
+            DeathReason::CRASH => vm_exited::DeathReason::Crash,
+            DeathReason::PVM_FIRMWARE_PUBLIC_KEY_MISMATCH => {
+                vm_exited::DeathReason::PvmFirmwarePublicKeyMismatch
+            }
+            DeathReason::PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED => {
+                vm_exited::DeathReason::PvmFirmwareInstanceImageChanged
+            }
+            DeathReason::BOOTLOADER_PUBLIC_KEY_MISMATCH => {
+                vm_exited::DeathReason::BootloaderPublicKeyMismatch
+            }
+            DeathReason::BOOTLOADER_INSTANCE_IMAGE_CHANGED => {
+                vm_exited::DeathReason::BootloaderInstanceImageChanged
+            }
+            DeathReason::MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE => {
+                vm_exited::DeathReason::MicrodroidFailedToConnectToVirtualizationService
+            }
+            DeathReason::MICRODROID_PAYLOAD_HAS_CHANGED => {
+                vm_exited::DeathReason::MicrodroidPayloadHasChanged
+            }
+            DeathReason::MICRODROID_PAYLOAD_VERIFICATION_FAILED => {
+                vm_exited::DeathReason::MicrodroidPayloadVerificationFailed
+            }
+            DeathReason::MICRODROID_INVALID_PAYLOAD_CONFIG => {
+                vm_exited::DeathReason::MicrodroidInvalidPayloadConfig
+            }
+            DeathReason::MICRODROID_UNKNOWN_RUNTIME_ERROR => {
+                vm_exited::DeathReason::MicrodroidUnknownRuntimeError
+            }
+            DeathReason::HANGUP => vm_exited::DeathReason::Hangup,
+            _ => vm_exited::DeathReason::Unknown,
+        },
+    };
+    match vm_exited.stats_write() {
+        Err(e) => {
+            warn!("statslog_rust failed with error: {}", e);
+        }
+        Ok(_) => trace!("statslog_rust succeeded for virtualization service"),
+    }
+}
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index a7e82da..29d2fe7 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -15,6 +15,7 @@
 //! Functions for running instances of `crosvm`.
 
 use crate::aidl::VirtualMachineCallbacks;
+use crate::atom::write_vm_exited_stats;
 use crate::Cid;
 use anyhow::{anyhow, bail, Context, Error};
 use command_fds::CommandFdExt;
@@ -70,6 +71,7 @@
 #[derive(Debug)]
 pub struct CrosvmConfig {
     pub cid: Cid,
+    pub name: String,
     pub bootloader: Option<File>,
     pub kernel: Option<File>,
     pub initrd: Option<File>,
@@ -170,6 +172,8 @@
     pub vm_state: Mutex<VmState>,
     /// The CID assigned to the VM for vsock communication.
     pub cid: Cid,
+    /// The name of the VM.
+    pub name: String,
     /// Whether the VM is a protected VM.
     pub protected: bool,
     /// Directory of temporary files used by the VM while it is running.
@@ -204,10 +208,12 @@
     ) -> Result<VmInstance, Error> {
         validate_config(&config)?;
         let cid = config.cid;
+        let name = config.name.clone();
         let protected = config.protected;
         Ok(VmInstance {
             vm_state: Mutex::new(VmState::NotStarted { config }),
             cid,
+            name,
             protected,
             temporary_directory,
             requester_uid,
@@ -261,7 +267,10 @@
             };
 
         self.handle_ramdump().unwrap_or_else(|e| error!("Error handling ramdump: {}", e));
-        self.callbacks.callback_on_died(self.cid, death_reason(&result, &failure_reason));
+
+        let death_reason = death_reason(&result, &failure_reason);
+        self.callbacks.callback_on_died(self.cid, death_reason);
+        write_vm_exited_stats(self.requester_uid as i32, &self.name, death_reason);
 
         // Delete temporary files.
         if let Err(e) = remove_dir_all(&self.temporary_directory) {
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 3b0adb9..93a5966 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -15,6 +15,7 @@
 //! Android VirtualizationService
 
 mod aidl;
+mod atom;
 mod composite;
 mod crosvm;
 mod payload;
diff --git a/vm/src/main.rs b/vm/src/main.rs
index c421b04..ee0e2e6 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -40,6 +40,10 @@
 enum Opt {
     /// Run a virtual machine with a config in APK
     RunApp {
+        /// Name of VM
+        #[structopt(long)]
+        name: Option<String>,
+
         /// Path to VM Payload APK
         #[structopt(parse(from_os_str))]
         apk: PathBuf,
@@ -102,6 +106,10 @@
     },
     /// Run a virtual machine
     Run {
+        /// Name of VM
+        #[structopt(long)]
+        name: Option<String>,
+
         /// Path to VM config JSON
         #[structopt(parse(from_os_str))]
         config: PathBuf,
@@ -195,6 +203,7 @@
 
     match opt {
         Opt::RunApp {
+            name,
             apk,
             idsig,
             instance,
@@ -211,6 +220,7 @@
             task_profiles,
             extra_idsigs,
         } => command_run_app(
+            name,
             service.as_ref(),
             &apk,
             &idsig,
@@ -228,8 +238,9 @@
             task_profiles,
             &extra_idsigs,
         ),
-        Opt::Run { config, daemonize, cpus, cpu_affinity, task_profiles, console, log } => {
+        Opt::Run { name, config, daemonize, cpus, cpu_affinity, task_profiles, console, log } => {
             command_run(
+                name,
                 service.as_ref(),
                 &config,
                 daemonize,
diff --git a/vm/src/run.rs b/vm/src/run.rs
index 9bd7863..05a9390 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -35,6 +35,7 @@
 /// Run a VM from the given APK, idsig, and config.
 #[allow(clippy::too_many_arguments)]
 pub fn command_run_app(
+    name: Option<String>,
     service: &dyn IVirtualizationService,
     apk: &Path,
     idsig: &Path,
@@ -91,6 +92,7 @@
     let extra_idsig_fds = extra_idsig_files?.into_iter().map(ParcelFileDescriptor::new).collect();
 
     let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
+        name: name.unwrap_or_else(|| String::from("VmRunApp")),
         apk: apk_fd.into(),
         idsig: idsig_fd.into(),
         extraIdsigs: extra_idsig_fds,
@@ -117,6 +119,7 @@
 /// Run a VM from the given configuration file.
 #[allow(clippy::too_many_arguments)]
 pub fn command_run(
+    name: Option<String>,
     service: &dyn IVirtualizationService,
     config_path: &Path,
     daemonize: bool,
@@ -136,6 +139,11 @@
     if let Some(cpus) = cpus {
         config.numCpus = cpus as i32;
     }
+    if let Some(name) = name {
+        config.name = name;
+    } else {
+        config.name = String::from("VmRun");
+    }
     config.cpuAffinity = cpu_affinity;
     config.taskProfiles = task_profiles;
     run(
diff --git a/vmbase/example/tests/test.rs b/vmbase/example/tests/test.rs
index fd6eb8c..58fffff 100644
--- a/vmbase/example/tests/test.rs
+++ b/vmbase/example/tests/test.rs
@@ -48,7 +48,9 @@
         File::open(VMBASE_EXAMPLE_PATH)
             .with_context(|| format!("Failed to open VM image {}", VMBASE_EXAMPLE_PATH))?,
     );
+
     let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
+        name: String::from("VmBaseTest"),
         kernel: None,
         initrd: None,
         params: None,
diff --git a/vmbase/exceptions_panic.S b/vmbase/exceptions_panic.S
index 6f73da8..4a3f2db 100644
--- a/vmbase/exceptions_panic.S
+++ b/vmbase/exceptions_panic.S
@@ -22,8 +22,8 @@
  */
 
 .macro exception_panic
-	mov	x0, 0x80400000
-	add	x0, x0, 9
+	mov	x0, 0x84000000
+	movk	x0, 9
 	mov	x1, 0
 	mov	x2, 0
 	mov	x3, 0