Merge "Separate AVFHostTestCases from avf-postsubmit"
diff --git a/compos/common/lib.rs b/compos/common/lib.rs
index 8d49ff0..1f937c9 100644
--- a/compos/common/lib.rs
+++ b/compos/common/lib.rs
@@ -53,9 +53,6 @@
/// /system_ext available in CompOS.
pub const IDSIG_MANIFEST_EXT_APK_FILE: &str = "idsig_manifest_ext_apk";
-/// Number of CPUs to run dex2oat (actually the entire compos VM) with
-pub const DEX2OAT_THREADS_PROP_NAME: &str = "dalvik.vm.boot-dex2oat-threads";
-
/// The Android path of fs-verity build manifest APK for /system.
pub const BUILD_MANIFEST_APK_PATH: &str = "/system/etc/security/fsverity/BuildManifest.apk";
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index c4791e9..0a6c3d6 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -22,10 +22,8 @@
use anyhow::{bail, Result};
use binder::Strong;
use compos_common::compos_client::VmParameters;
-use compos_common::{CURRENT_INSTANCE_DIR, DEX2OAT_THREADS_PROP_NAME, TEST_INSTANCE_DIR};
-use rustutils::system_properties;
+use compos_common::{CURRENT_INSTANCE_DIR, TEST_INSTANCE_DIR};
use std::num::NonZeroU32;
-use std::str::FromStr;
use std::sync::{Arc, Mutex, Weak};
use virtualizationservice::IVirtualizationService::IVirtualizationService;
@@ -79,14 +77,10 @@
}
fn new_vm_parameters() -> Result<VmParameters> {
- let cpus = match system_properties::read(DEX2OAT_THREADS_PROP_NAME)? {
- Some(s) => Some(NonZeroU32::from_str(&s)?),
- None => {
- // dex2oat uses all CPUs by default. To match the behavior, give the VM all CPUs by
- // default.
- NonZeroU32::new(num_cpus::get() as u32)
- }
- };
+ // By default, dex2oat starts as many threads as there are CPUs. This can be overridden with
+ // a system property. Start the VM with all CPUs and assume the guest will start a suitable
+ // number of dex2oat threads.
+ let cpus = NonZeroU32::new(num_cpus::get() as u32);
let task_profiles = vec!["SCHED_SP_COMPUTE".to_string()];
Ok(VmParameters { cpus, task_profiles, memory_mib: Some(VM_MEMORY_MIB), ..Default::default() })
}
diff --git a/compos/verify/verify.rs b/compos/verify/verify.rs
index 71d8bcc..528719f 100644
--- a/compos/verify/verify.rs
+++ b/compos/verify/verify.rs
@@ -33,6 +33,7 @@
use log::error;
use std::fs::File;
use std::io::Read;
+use std::num::NonZeroU32;
use std::panic;
use std::path::Path;
@@ -114,7 +115,11 @@
&idsig,
&idsig_manifest_apk,
&idsig_manifest_ext_apk,
- &VmParameters { debug_mode: args.debug, ..Default::default() },
+ &VmParameters {
+ cpus: Some(NonZeroU32::new(1).unwrap()), // This VM runs very little work at boot
+ debug_mode: args.debug,
+ ..Default::default()
+ },
)?;
let service = vm_instance.connect_service()?;
diff --git a/libs/dice/src/bcc.rs b/libs/dice/src/bcc.rs
index 18b5083..a7ef882 100644
--- a/libs/dice/src/bcc.rs
+++ b/libs/dice/src/bcc.rs
@@ -16,21 +16,15 @@
//! Wrapper around dice/android/bcc.h.
-use core::ffi::CStr;
use core::mem;
use core::ptr;
-use open_dice_bcc_bindgen::BccConfigValues;
-use open_dice_bcc_bindgen::BccFormatConfigDescriptor;
use open_dice_bcc_bindgen::BccHandoverMainFlow;
use open_dice_bcc_bindgen::BccHandoverParse;
-use open_dice_bcc_bindgen::BCC_INPUT_COMPONENT_NAME;
-use open_dice_bcc_bindgen::BCC_INPUT_COMPONENT_VERSION;
-use open_dice_bcc_bindgen::BCC_INPUT_RESETTABLE;
-use crate::check_call;
+use crate::check_result;
use crate::Cdi;
-use crate::Error;
+use crate::DiceError;
use crate::InputValues;
use crate::Result;
@@ -56,7 +50,7 @@
// SAFETY - The buffer is only read and never stored and the returned pointers should all
// point within the address range of the buffer or be NULL.
- check_call(unsafe {
+ check_result(unsafe {
BccHandoverParse(
buffer.as_ptr(),
buffer.len(),
@@ -68,20 +62,20 @@
})?;
let cdi_attest = {
- let i = index_from_ptr(buffer, cdi_attest).ok_or(Error::PlatformError)?;
- let s = buffer.get(i..(i + mem::size_of::<Cdi>())).ok_or(Error::PlatformError)?;
- s.try_into().map_err(|_| Error::PlatformError)?
+ let i = index_from_ptr(buffer, cdi_attest).ok_or(DiceError::PlatformError)?;
+ let s = buffer.get(i..(i + mem::size_of::<Cdi>())).ok_or(DiceError::PlatformError)?;
+ s.try_into().map_err(|_| DiceError::PlatformError)?
};
let cdi_seal = {
- let i = index_from_ptr(buffer, cdi_seal).ok_or(Error::PlatformError)?;
- let s = buffer.get(i..(i + mem::size_of::<Cdi>())).ok_or(Error::PlatformError)?;
- s.try_into().map_err(|_| Error::PlatformError)?
+ let i = index_from_ptr(buffer, cdi_seal).ok_or(DiceError::PlatformError)?;
+ let s = buffer.get(i..(i + mem::size_of::<Cdi>())).ok_or(DiceError::PlatformError)?;
+ s.try_into().map_err(|_| DiceError::PlatformError)?
};
let bcc = if bcc.is_null() {
None
} else {
- let i = index_from_ptr(buffer, bcc).ok_or(Error::PlatformError)?;
- Some(buffer.get(i..(i + bcc_size)).ok_or(Error::PlatformError)?)
+ let i = index_from_ptr(buffer, bcc).ok_or(DiceError::PlatformError)?;
+ Some(buffer.get(i..(i + bcc_size)).ok_or(DiceError::PlatformError)?)
};
Ok(Self { buffer, cdi_attest, cdi_seal, bcc })
@@ -93,7 +87,7 @@
let mut size: usize = 0;
// SAFETY - The function only reads `self.buffer`, writes to `buffer` within its bounds,
// reads `input_values` as a constant input and doesn't store any pointer.
- check_call(unsafe {
+ check_result(unsafe {
BccHandoverMainFlow(
context,
self.buffer.as_ptr(),
@@ -109,57 +103,6 @@
}
}
-/// Formats a configuration descriptor following the BCC's specification.
-///
-/// ```
-/// BccConfigDescriptor = {
-/// ? -70002 : tstr, ; Component name
-/// ? -70003 : int, ; Component version
-/// ? -70004 : null, ; Resettable
-/// }
-/// ```
-pub fn format_config_descriptor(
- buffer: &mut [u8],
- name: Option<&CStr>,
- version: Option<u64>,
- resettable: bool,
-) -> Result<usize> {
- let mut inputs = 0;
-
- if name.is_some() {
- inputs |= BCC_INPUT_COMPONENT_NAME;
- }
-
- if version.is_some() {
- inputs |= BCC_INPUT_COMPONENT_VERSION;
- }
-
- if resettable {
- inputs |= BCC_INPUT_RESETTABLE;
- }
-
- let values = BccConfigValues {
- inputs,
- component_name: name.map_or(ptr::null(), |p| p.as_ptr()),
- component_version: version.unwrap_or(0),
- };
-
- let mut buffer_size = 0;
-
- // SAFETY - The function writes to the buffer, within the given bounds, and only reads the
- // input values. It writes its result to buffer_size.
- check_call(unsafe {
- BccFormatConfigDescriptor(
- &values as *const _,
- buffer.len(),
- buffer.as_mut_ptr(),
- &mut buffer_size as *mut _,
- )
- })?;
-
- Ok(buffer_size)
-}
-
fn index_from_ptr(slice: &[u8], pointer: *const u8) -> Option<usize> {
if slice.as_ptr_range().contains(&pointer) {
(pointer as usize).checked_sub(slice.as_ptr() as usize)
diff --git a/libs/dice/src/lib.rs b/libs/dice/src/lib.rs
index f0ac2ae..58575eb 100644
--- a/libs/dice/src/lib.rs
+++ b/libs/dice/src/lib.rs
@@ -18,62 +18,15 @@
#![no_std]
-use core::fmt;
-use core::result;
-
-pub use diced_open_dice::{Config, Hash, InputValues, HASH_SIZE};
-pub use open_dice_cbor_bindgen::DiceMode;
+pub use diced_open_dice::{
+ bcc_format_config_descriptor, check_result, Cdi, Config, DiceError, DiceMode, Hash,
+ InputValues, Result, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE,
+};
use open_dice_cbor_bindgen::DiceHash;
-use open_dice_cbor_bindgen::DiceResult;
-use open_dice_cbor_bindgen::DiceResult_kDiceResultBufferTooSmall as DICE_RESULT_BUFFER_TOO_SMALL;
-use open_dice_cbor_bindgen::DiceResult_kDiceResultInvalidInput as DICE_RESULT_INVALID_INPUT;
-use open_dice_cbor_bindgen::DiceResult_kDiceResultOk as DICE_RESULT_OK;
-use open_dice_cbor_bindgen::DiceResult_kDiceResultPlatformError as DICE_RESULT_PLATFORM_ERROR;
pub mod bcc;
-const CDI_SIZE: usize = open_dice_cbor_bindgen::DICE_CDI_SIZE as usize;
-
-/// Array type of CDIs.
-pub type Cdi = [u8; CDI_SIZE];
-
-/// Error type used by DICE.
-pub enum Error {
- /// Provided input was invalid.
- InvalidInput,
- /// Provided buffer was too small.
- BufferTooSmall,
- /// Unexpected platform error.
- PlatformError,
- /// Unexpected return value.
- Unknown(DiceResult),
-}
-
-impl fmt::Debug for Error {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Error::InvalidInput => write!(f, "invalid input"),
- Error::BufferTooSmall => write!(f, "buffer too small"),
- Error::PlatformError => write!(f, "platform error"),
- Error::Unknown(n) => write!(f, "unknown error: {}", n),
- }
- }
-}
-
-/// Result of DICE functions.
-pub type Result<T> = result::Result<T, Error>;
-
-fn check_call(ret: DiceResult) -> Result<()> {
- match ret {
- DICE_RESULT_OK => Ok(()),
- DICE_RESULT_INVALID_INPUT => Err(Error::InvalidInput),
- DICE_RESULT_BUFFER_TOO_SMALL => Err(Error::BufferTooSmall),
- DICE_RESULT_PLATFORM_ERROR => Err(Error::PlatformError),
- n => Err(Error::Unknown(n)),
- }
-}
-
fn ctx() -> *mut core::ffi::c_void {
core::ptr::null_mut()
}
@@ -82,6 +35,6 @@
pub fn hash(bytes: &[u8]) -> Result<Hash> {
let mut output: Hash = [0; HASH_SIZE];
// SAFETY - DiceHash takes a sized input buffer and writes to a constant-sized output buffer.
- check_call(unsafe { DiceHash(ctx(), bytes.as_ptr(), bytes.len(), output.as_mut_ptr()) })?;
+ check_result(unsafe { DiceHash(ctx(), bytes.as_ptr(), bytes.len(), output.as_mut_ptr()) })?;
Ok(output)
}
diff --git a/microdroid_manager/src/dice.rs b/microdroid_manager/src/dice.rs
index 739c944..a7288b6 100644
--- a/microdroid_manager/src/dice.rs
+++ b/microdroid_manager/src/dice.rs
@@ -17,7 +17,7 @@
use anyhow::{bail, Context, Error, Result};
use byteorder::{NativeEndian, ReadBytesExt};
use diced_open_dice_cbor::{
- Config, ContextImpl, DiceMode, Hash, Hidden, InputValuesOwned, OpenDiceCborContext, CDI_SIZE,
+ Config, ContextImpl, DiceMode, Hash, Hidden, InputValues, OpenDiceCborContext, CDI_SIZE,
};
use keystore2_crypto::ZVec;
use libc::{c_void, mmap, munmap, MAP_FAILED, MAP_PRIVATE, PROT_READ};
@@ -142,11 +142,10 @@
debug: bool,
hidden: Hidden,
) -> Result<DiceContext> {
- let input_values = InputValuesOwned::new(
+ let input_values = InputValues::new(
code_hash,
Config::Descriptor(config_desc),
authority_hash,
- None,
if debug { DiceMode::kDiceModeDebug } else { DiceMode::kDiceModeNormal },
hidden,
);
diff --git a/pvmfw/src/dice.rs b/pvmfw/src/dice.rs
index 4e1e60a..c5241c4 100644
--- a/pvmfw/src/dice.rs
+++ b/pvmfw/src/dice.rs
@@ -16,12 +16,13 @@
use core::ffi::CStr;
use core::mem::size_of;
-use dice::bcc::format_config_descriptor;
use dice::bcc::Handover;
+use dice::bcc_format_config_descriptor;
use dice::hash;
use dice::Config;
use dice::DiceMode;
use dice::InputValues;
+use dice::HIDDEN_SIZE;
use pvmfw_avb::{DebugLevel, Digest, VerifiedBootData};
fn to_dice_mode(debug_level: DebugLevel) -> DiceMode {
@@ -52,22 +53,20 @@
let mode = to_dice_mode(verified_boot_data.debug_level);
let component_name = CStr::from_bytes_with_nul(b"vm_entry\0").unwrap();
let mut config_descriptor_buffer = [0; 128];
- let config_descriptor_size = format_config_descriptor(
- &mut config_descriptor_buffer,
+ let config_descriptor_size = bcc_format_config_descriptor(
Some(component_name),
None, // component_version
false, // resettable
+ &mut config_descriptor_buffer,
)?;
let config = &config_descriptor_buffer[..config_descriptor_size];
let input_values = InputValues::new(
- &code_hash,
- None, // code_descriptor
+ code_hash,
Config::Descriptor(config),
- &auth_hash,
- None, // authority_descriptor
+ auth_hash,
mode,
- None, // TODO(b/249723852): Get salt from instance.img (virtio-blk) and/or TRNG.
+ [0u8; HIDDEN_SIZE], // TODO(b/249723852): Get salt from instance.img (virtio-blk) and/or TRNG.
);
bcc.main_flow(&input_values, next_bcc)
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index f17000a..db87126 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -50,8 +50,11 @@
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.OptionalLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
@@ -136,102 +139,87 @@
mInstrumentation.sendStatus(0, bundle);
}
- @Test
- public void testMicrodroidBootTime()
+ private static class BootTimeStats {
+ private final Map<BootTimeMetric, List<Double>> mData = new HashMap<>();
+
+ public BootTimeStats(int trialCount) {
+ for (BootTimeMetric metric : BootTimeMetric.values()) {
+ mData.put(metric, new ArrayList<>(trialCount));
+ }
+ }
+
+ public void collect(BootResult result) {
+ for (BootTimeMetric metric : BootTimeMetric.values()) {
+ OptionalLong value = result.getBootTimeMetricNanoTime(metric);
+ if (value.isPresent()) {
+ mData.get(metric).add(value.getAsLong() / NANO_TO_MILLI);
+ }
+ }
+ }
+
+ public List<Double> get(BootTimeMetric metric) {
+ return Collections.unmodifiableList(mData.get(metric));
+ }
+ }
+
+ private BootTimeStats runBootTimeTest(
+ String name,
+ Function<VirtualMachineConfig.Builder, VirtualMachineConfig.Builder> fnConfig)
throws VirtualMachineException, InterruptedException, IOException {
assume().withMessage("Skip on CF; too slow").that(isCuttlefish()).isFalse();
final int trialCount = 10;
- List<Double> bootTimeMetrics = new ArrayList<>();
+ BootTimeStats stats = new BootTimeStats(trialCount);
for (int i = 0; i < trialCount; i++) {
- VirtualMachineConfig normalConfig =
+ VirtualMachineConfig.Builder builder =
newVmConfigBuilder()
.setPayloadBinaryName("MicrodroidIdleNativeLib.so")
- .setDebugLevel(DEBUG_LEVEL_NONE)
.setMemoryBytes(256 * ONE_MEBI)
- .build();
- forceCreateNewVirtualMachine("test_vm_boot_time", normalConfig);
+ .setDebugLevel(DEBUG_LEVEL_NONE);
+ VirtualMachineConfig config = fnConfig.apply(builder).build();
+ forceCreateNewVirtualMachine(name, config);
- BootResult result = tryBootVm(TAG, "test_vm_boot_time");
+ BootResult result = tryBootVm(TAG, name);
assertThat(result.payloadStarted).isTrue();
-
- bootTimeMetrics.add(result.endToEndNanoTime / NANO_TO_MILLI);
+ stats.collect(result);
}
+ return stats;
+ }
- reportMetrics(bootTimeMetrics, "boot_time", "ms");
+ @Test
+ public void testMicrodroidBootTime()
+ throws VirtualMachineException, InterruptedException, IOException {
+ BootTimeStats stats = runBootTimeTest("test_vm_boot_time", (builder) -> builder);
+ reportMetrics(stats.get(BootTimeMetric.TOTAL), "boot_time", "ms");
}
@Test
public void testMicrodroidMulticoreBootTime()
throws VirtualMachineException, InterruptedException, IOException {
- assume().withMessage("Skip on CF; too slow").that(isCuttlefish()).isFalse();
-
- final int trialCount = 10;
- final int[] trialNumCpus = {2, 4, 8};
-
- for (int numCpus : trialNumCpus) {
- List<Double> bootTimeMetrics = new ArrayList<>();
- for (int i = 0; i < trialCount; i++) {
- VirtualMachineConfig normalConfig =
- newVmConfigBuilder()
- .setPayloadBinaryName("MicrodroidIdleNativeLib.so")
- .setDebugLevel(DEBUG_LEVEL_NONE)
- .setMemoryBytes(256 * ONE_MEBI)
- .setNumCpus(numCpus)
- .build();
- forceCreateNewVirtualMachine("test_vm_boot_time_multicore", normalConfig);
-
- BootResult result = tryBootVm(TAG, "test_vm_boot_time_multicore");
- assertThat(result.payloadStarted).isTrue();
-
- bootTimeMetrics.add(result.endToEndNanoTime / NANO_TO_MILLI);
- }
-
+ for (int numCpus : new int[] {2, 4, 8}) {
+ BootTimeStats stats =
+ runBootTimeTest(
+ "test_vm_boot_time_multicore",
+ (builder) -> builder.setNumCpus(numCpus));
String metricName = "boot_time_" + numCpus + "cpus";
- reportMetrics(bootTimeMetrics, metricName, "ms");
+ reportMetrics(stats.get(BootTimeMetric.TOTAL), metricName, "ms");
}
}
@Test
public void testMicrodroidDebugBootTime()
throws VirtualMachineException, InterruptedException, IOException {
- assume().withMessage("Skip on CF; too slow").that(isCuttlefish()).isFalse();
-
- final int trialCount = 10;
-
- List<Double> vmStartingTimeMetrics = new ArrayList<>();
- List<Double> bootTimeMetrics = new ArrayList<>();
- List<Double> bootloaderTimeMetrics = new ArrayList<>();
- List<Double> kernelBootTimeMetrics = new ArrayList<>();
- List<Double> userspaceBootTimeMetrics = new ArrayList<>();
-
- for (int i = 0; i < trialCount; i++) {
- // To grab boot events from log, set debug mode to FULL
- VirtualMachineConfig normalConfig =
- newVmConfigBuilder()
- .setPayloadBinaryName("MicrodroidIdleNativeLib.so")
- .setDebugLevel(DEBUG_LEVEL_FULL)
- .setVmOutputCaptured(true)
- .setMemoryBytes(256 * ONE_MEBI)
- .build();
- forceCreateNewVirtualMachine("test_vm_boot_time_debug", normalConfig);
-
- BootResult result = tryBootVm(TAG, "test_vm_boot_time_debug");
- assertThat(result.payloadStarted).isTrue();
-
- vmStartingTimeMetrics.add(result.getVMStartingElapsedNanoTime() / NANO_TO_MILLI);
- bootTimeMetrics.add(result.endToEndNanoTime / NANO_TO_MILLI);
- bootloaderTimeMetrics.add(result.getBootloaderElapsedNanoTime() / NANO_TO_MILLI);
- kernelBootTimeMetrics.add(result.getKernelElapsedNanoTime() / NANO_TO_MILLI);
- userspaceBootTimeMetrics.add(result.getUserspaceElapsedNanoTime() / NANO_TO_MILLI);
- }
-
- reportMetrics(vmStartingTimeMetrics, "vm_starting_time", "ms");
- reportMetrics(bootTimeMetrics, "boot_time", "ms");
- reportMetrics(bootloaderTimeMetrics, "bootloader_time", "ms");
- reportMetrics(kernelBootTimeMetrics, "kernel_boot_time", "ms");
- reportMetrics(userspaceBootTimeMetrics, "userspace_boot_time", "ms");
+ BootTimeStats stats =
+ runBootTimeTest(
+ "test_vm_boot_time_debug",
+ (builder) ->
+ builder.setDebugLevel(DEBUG_LEVEL_FULL).setVmOutputCaptured(true));
+ reportMetrics(stats.get(BootTimeMetric.TOTAL), "boot_time", "ms");
+ reportMetrics(stats.get(BootTimeMetric.VM_START), "vm_starting_time", "ms");
+ reportMetrics(stats.get(BootTimeMetric.BOOTLOADER), "bootloader_time", "ms");
+ reportMetrics(stats.get(BootTimeMetric.KERNEL), "kernel_boot_time", "ms");
+ reportMetrics(stats.get(BootTimeMetric.USERSPACE), "userspace_boot_time", "ms");
}
@Test
diff --git a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
index 94f7e99..ba82c38 100644
--- a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
+++ b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
@@ -26,9 +26,11 @@
}
private static final String KEY_VENDOR_DEVICE = "ro.product.vendor.device";
+ private static final String KEY_BUILD_TYPE = "ro.build.type";
private static final String KEY_METRICS_TAG = "debug.hypervisor.metrics_tag";
private static final String CUTTLEFISH_DEVICE_PREFIX = "vsoc_";
+ private static final String USER_BUILD_TYPE = "user";
private final PropertyGetter mPropertyGetter;
@@ -49,6 +51,13 @@
return vendorDeviceName != null && vendorDeviceName.startsWith(CUTTLEFISH_DEVICE_PREFIX);
}
+ /**
+ * @return whether the device is user build.
+ */
+ public boolean isUserBuild() {
+ return USER_BUILD_TYPE.equals(getProperty(KEY_BUILD_TYPE));
+ }
+
public String getMetricsTag() {
return getProperty(KEY_METRICS_TAG);
}
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 f1da43a..419b250 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
@@ -148,8 +148,9 @@
private OptionalLong mPayloadStartedNanoTime = OptionalLong.empty();
private StringBuilder mConsoleOutput = new StringBuilder();
private StringBuilder mLogOutput = new StringBuilder();
+ private boolean mProcessedBootTimeMetrics = false;
- private void processBootEvents(String log) {
+ private void processBootTimeMetrics(String log) {
if (!mVcpuStartedNanoTime.isPresent()) {
mVcpuStartedNanoTime = OptionalLong.of(System.nanoTime());
}
@@ -165,12 +166,13 @@
}
}
- private void logVmOutputAndMonitorBootEvents(
+ private void logVmOutputAndMonitorBootTimeMetrics(
String tag,
InputStream vmOutputStream,
String name,
StringBuilder result,
boolean monitorEvents) {
+ mProcessedBootTimeMetrics = monitorEvents;
new Thread(
() -> {
try {
@@ -180,7 +182,7 @@
String line;
while ((line = reader.readLine()) != null
&& !Thread.interrupted()) {
- if (monitorEvents) processBootEvents(line);
+ if (monitorEvents) processBootTimeMetrics(line);
Log.i(tag, name + ": " + line);
result.append(line + "\n");
}
@@ -191,15 +193,15 @@
.start();
}
- private void logVmOutputAndMonitorBootEvents(
+ private void logVmOutputAndMonitorBootTimeMetrics(
String tag, InputStream vmOutputStream, String name, StringBuilder result) {
- logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, result, true);
+ logVmOutputAndMonitorBootTimeMetrics(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, StringBuilder result) {
- logVmOutputAndMonitorBootEvents(tag, vmOutputStream, name, result, false);
+ logVmOutputAndMonitorBootTimeMetrics(tag, vmOutputStream, name, result, false);
}
public void runToFinish(String logTag, VirtualMachine vm)
@@ -207,7 +209,7 @@
vm.setCallback(mExecutorService, this);
vm.run();
if (vm.getConfig().isVmOutputCaptured()) {
- logVmOutputAndMonitorBootEvents(
+ logVmOutputAndMonitorBootTimeMetrics(
logTag, vm.getConsoleOutput(), "Console", mConsoleOutput);
logVmOutput(logTag, vm.getLogOutput(), "Log", mLogOutput);
}
@@ -238,6 +240,10 @@
return mLogOutput.toString();
}
+ public boolean hasProcessedBootTimeMetrics() {
+ return mProcessedBootTimeMetrics;
+ }
+
protected void forceStop(VirtualMachine vm) {
try {
vm.stop();
@@ -266,12 +272,21 @@
}
}
+ public enum BootTimeMetric {
+ TOTAL,
+ VM_START,
+ BOOTLOADER,
+ KERNEL,
+ USERSPACE,
+ }
+
public static class BootResult {
public final boolean payloadStarted;
public final int deathReason;
public final long apiCallNanoTime;
public final long endToEndNanoTime;
+ public final boolean processedBootTimeMetrics;
public final OptionalLong vcpuStartedNanoTime;
public final OptionalLong kernelStartedNanoTime;
public final OptionalLong initStartedNanoTime;
@@ -285,6 +300,7 @@
int deathReason,
long apiCallNanoTime,
long endToEndNanoTime,
+ boolean processedBootTimeMetrics,
OptionalLong vcpuStartedNanoTime,
OptionalLong kernelStartedNanoTime,
OptionalLong initStartedNanoTime,
@@ -295,6 +311,7 @@
this.payloadStarted = payloadStarted;
this.deathReason = deathReason;
this.endToEndNanoTime = endToEndNanoTime;
+ this.processedBootTimeMetrics = processedBootTimeMetrics;
this.vcpuStartedNanoTime = vcpuStartedNanoTime;
this.kernelStartedNanoTime = kernelStartedNanoTime;
this.initStartedNanoTime = initStartedNanoTime;
@@ -336,6 +353,31 @@
public long getUserspaceElapsedNanoTime() {
return getPayloadStartedNanoTime() - getInitStartedNanoTime();
}
+
+ public boolean hasProcessedBootTimeMetrics() {
+ return processedBootTimeMetrics;
+ }
+
+ public OptionalLong getBootTimeMetricNanoTime(BootTimeMetric metric) {
+ if (metric == BootTimeMetric.TOTAL) {
+ return OptionalLong.of(endToEndNanoTime);
+ }
+
+ if (processedBootTimeMetrics) {
+ switch (metric) {
+ case VM_START:
+ return OptionalLong.of(getVMStartingElapsedNanoTime());
+ case BOOTLOADER:
+ return OptionalLong.of(getBootloaderElapsedNanoTime());
+ case KERNEL:
+ return OptionalLong.of(getKernelElapsedNanoTime());
+ case USERSPACE:
+ return OptionalLong.of(getUserspaceElapsedNanoTime());
+ }
+ }
+
+ return OptionalLong.empty();
+ }
}
public BootResult tryBootVm(String logTag, String vmName)
@@ -366,6 +408,7 @@
deathReason.getNow(VmEventListener.STOP_REASON_INFRASTRUCTURE_ERROR),
apiCallNanoTime,
endTime.getNow(apiCallNanoTime) - apiCallNanoTime,
+ listener.hasProcessedBootTimeMetrics(),
listener.getVcpuStartedNanoTime(),
listener.getKernelStartedNanoTime(),
listener.getInitStartedNanoTime(),
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index 8d328bc..1766835 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -110,6 +110,10 @@
}
}
+ public boolean isUserBuild() {
+ return DeviceProperties.create(getDevice()::getProperty).isUserBuild();
+ }
+
protected boolean isCuttlefish() {
return DeviceProperties.create(getDevice()::getProperty).isCuttlefish();
}
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 0623ff2..a890770 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -55,6 +55,7 @@
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -583,8 +584,10 @@
}
@Test
+ @Ignore("b/243630590: Temporal workaround until lab devices has flashed new DPM")
public void testTombstonesAreGeneratedUponKernelCrash() throws Exception {
assumeFalse("Cuttlefish is not supported", isCuttlefish());
+ assumeFalse("Skipping test because ramdump is disabled on user build", isUserBuild());
assertThat(
isTombstoneGenerated(
"assets/vm_config_crash.json",
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index 8f88daf..19d862a 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -663,6 +663,24 @@
}
}
+fn should_configure_ramdump(protected: bool) -> bool {
+ if protected {
+ // Protected VM needs ramdump configuration here.
+ // pvmfw will disable ramdump if unnecessary.
+ true
+ } else {
+ // For unprotected VM, ramdump should be handled here.
+ // ramdump wouldn't be enabled if ramdump is explicitly set to <1>.
+ if let Ok(mut file) = File::open("/proc/device-tree/avf/guest/common/ramdump") {
+ let mut ramdump: [u8; 4] = Default::default();
+ file.read_exact(&mut ramdump).map_err(|_| false).unwrap();
+ // DT spec uses big endian although Android is always little endian.
+ return u32::from_be_bytes(ramdump) == 1;
+ }
+ false
+ }
+}
+
/// Starts an instance of `crosvm` to manage a new VM.
fn run_vm(
config: CrosvmConfig,
@@ -779,7 +797,10 @@
debug!("Preserving FDs {:?}", preserved_fds);
command.preserved_fds(preserved_fds);
- command.arg("--params").arg("crashkernel=17M");
+ if should_configure_ramdump(config.protected) {
+ command.arg("--params").arg("crashkernel=17M");
+ }
+
print_crosvm_args(&command);
let result = SharedChild::spawn(&mut command)?;