VirtualizationService notifies to its client when ramdump is available
When a kernel panic occurs in a pVM and the ramdump is enabled there, a
ramdump file is generated.
This file should eventually be consumed by the client (the owner of the
VM) for further analysis. VirtualizationService let its client know that
a ramdump has been created and provide access to it.
Specifically, the end-to-end flow is as follows:
1) When starting a VM, an empty file is created under the VM-specific
directory (`/data/misc/virtualizationservice/<cid>/ramdump`).
2) The file becomes a backing store for a virtio-console device
(/dev/hvc3).
3) When a kernel panic occurs, the `crashdump` binary is executed and
the `/proc/vmcore` is written to `/dev/hvc3`. After the dump is done,
the VM triggers a reboot which is kills crosvm.
4) Virtualizationservice is notified with the exit of crosvm. It then
checks the size of the ramdump file. If that is not empty, it can
assume that a ramdump was occurred in the pVM.
5) Then virtualizationservice notifies the event via
`IVirtualMachineCallback.onRamdump(ParcelFileDescriptor)`, where the
parcel file descriptor is the handle to the ramdump file.
6) Client reads the ramdump file.
This change also adds `--ramdump` option to the `vm` tool to designate
the path where ramdump is saved to.
Bug: 238278104
Test: follow the steps. Automated tests will be added in a followup CL
1) Run a pVM:
adb shell /apex/com.android.virt/bin/vm run-app --debug full --mem 300 \
--ramdump /data/local/tmp/virt/myramdump \
/data/local/tmp/virt/MicrodroidDemoApp.apk \
/data/local/tmp/virt/apk.idsig /data/local/tmp/virt/instance.img \
assets/vm_config.json
2) Adb shell into the VM
adb forward tcp:8000 vsock:10:5555
adb connect localhost:8000
adb -s localhost:8000 root
adb -s localhost:8000 shell
3) Load the crashdump kernel
/system/bin/kexec \
/system/etc/microdroid_crashdump_kernel \
/system/etc/microdroid_crashdump_initrd.img \
"1 rdinit=/bin/crashdump nr_cpus=1 reset_devices console=hvc0 earlycon=uart8250,mmio,0x3f8"
4) Trigger a crash
echo c > /proc/sysrq-trigger
5) Check the ramdump at /data/local/tmp/virt/myramdump
Change-Id: I1f90537961632708ca5a889cdd53390030518bb8
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index d8f0b2e..af5029a 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -464,6 +464,19 @@
})
.collect::<Result<Vec<DiskFile>, _>>()?;
+ // Creating this ramdump file unconditionally is not harmful as ramdump will be created
+ // only when the VM is configured as such. `ramdump_write` is sent to crosvm and will
+ // be the backing store for the /dev/hvc3 where VM will emit ramdump to. `ramdump_read`
+ // will be sent back to the client (i.e. the VM owner) for readout.
+ let ramdump_path = temporary_directory.join("ramdump");
+ let ramdump = prepare_ramdump_file(&ramdump_path).map_err(|e| {
+ error!("Failed to prepare ramdump file: {}", e);
+ new_binder_exception(
+ ExceptionCode::SERVICE_SPECIFIC,
+ format!("Failed to prepare ramdump file: {}", e),
+ )
+ })?;
+
// Actually start the VM.
let crosvm_config = CrosvmConfig {
cid,
@@ -479,6 +492,7 @@
task_profiles: config.taskProfiles.clone(),
console_fd,
log_fd,
+ ramdump: Some(ramdump),
indirect_files,
platform_version: parse_platform_version_req(&config.platformVersion)?,
detect_hangup: is_app_config,
@@ -558,6 +572,11 @@
part.flush()
}
+fn prepare_ramdump_file(ramdump_path: &Path) -> Result<File> {
+ File::create(&ramdump_path)
+ .context(format!("Failed to create ramdump file {:?}", &ramdump_path))
+}
+
/// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
///
/// This may involve assembling a composite disk from a set of partition images.
@@ -883,6 +902,17 @@
}
}
+ /// Call all registered callbacks to say that there was a ramdump to download.
+ pub fn callback_on_ramdump(&self, cid: Cid, ramdump: File) {
+ let callbacks = &*self.0.lock().unwrap();
+ let pfd = ParcelFileDescriptor::new(ramdump);
+ for callback in callbacks {
+ if let Err(e) = callback.onRamdump(cid as i32, &pfd) {
+ error!("Error notifying ramdump of VM CID {}: {}", cid, e);
+ }
+ }
+ }
+
/// Add a new callback to the set.
fn add(&self, callback: Strong<dyn IVirtualMachineCallback>) {
self.0.lock().unwrap().push(callback);