Use official minicov and add unit test for coverage feature
This commit is contained in:
parent
79335b272f
commit
b04d62ae71
|
|
@ -1148,7 +1148,8 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "minicov"
|
||||
version = "0.3.7"
|
||||
source = "git+https://github.com/asterinas/minicov?rev=bd5454a#bd5454a58f5e64ef67519df9fb2025c96b47f8be"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"walkdir",
|
||||
|
|
|
|||
|
|
@ -27,26 +27,6 @@ comma separated configuration list:
|
|||
|
||||
Besides, to collect coverage data, we can use option `--coverage`. This option
|
||||
enables the coverage feature and collect coverage data to `coverage.profraw` when exit.
|
||||
It actually does several things:
|
||||
|
||||
- It adds `-Cinstrument-coverage -Zno-profiler-runtime` to `RUSTFLAGS` so LLVM will
|
||||
generate coverage instrument. And `coverage` features will be enabled for `ostd`,
|
||||
then before `exit_qemu` actually quit QEMU, it will call `minicov` to collect
|
||||
the coverage data to guest's own memory, and print its address and size, so that
|
||||
OSDK can dump it out of guest.
|
||||
- Next, `--no-shutdown` will be enabled for QEMU, and OSDK will setup a monitor
|
||||
connection to QEMU to monitor its status. Once exit, it dumps the coverage data
|
||||
from guest's memory to `coverage.profraw`.
|
||||
|
||||
**Note.** The code coverage feature of OSDK requires a non-default OSTD feature called
|
||||
`coverage`, which relies on dependencies that are not published on crates.io.
|
||||
To utilize this feature, projects must specify OSTD as a dependency using a Git
|
||||
repository or a local filesystem path in their `Cargo.toml`. For example:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ostd = { git = "https://github.com/asterinas/asterinas", rev = "v0.11.0", features = ["coverage"] }
|
||||
```
|
||||
|
||||
See [Debug Command](debug.md) to interact with the GDB server in terminal.
|
||||
|
||||
|
|
|
|||
|
|
@ -245,8 +245,16 @@ fn build_kernel_elf(
|
|||
}
|
||||
|
||||
const CFLAGS: &str = "CFLAGS_x86_64-unknown-none";
|
||||
let env_cflags = std::env::var(CFLAGS).unwrap_or_default();
|
||||
command.env(CFLAGS, env_cflags + " -fPIC");
|
||||
let mut env_cflags = std::env::var(CFLAGS).unwrap_or_default();
|
||||
env_cflags += " -fPIC";
|
||||
|
||||
if features.contains(&"coverage".to_string()) {
|
||||
// This is a workaround for minicov <https://github.com/Amanieu/minicov/issues/29>,
|
||||
// makes coverage work on x86_64-unknown-none.
|
||||
env_cflags += " -D__linux__";
|
||||
}
|
||||
|
||||
command.env(CFLAGS, env_cflags);
|
||||
|
||||
info!("Building kernel ELF using command: {:#?}", command);
|
||||
info!("Building directory: {:?}", std::env::current_dir().unwrap());
|
||||
|
|
|
|||
|
|
@ -61,6 +61,33 @@ mod workspace {
|
|||
}
|
||||
}
|
||||
|
||||
mod coverage_feature {
|
||||
use super::*;
|
||||
use crate::util::{cargo_osdk, depends_on_coverage};
|
||||
use assert_cmd::Command;
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn basic_coverage() {
|
||||
// Test skipped because TDX is enabled.
|
||||
if is_tdx_enabled() {
|
||||
return;
|
||||
}
|
||||
let workspace = workspace::WorkSpace::new(WORKSPACE, "basic_coverage");
|
||||
let manifest_path = Path::new(&workspace.os_dir()).join("Cargo.toml");
|
||||
let osdk_path = Path::new(&workspace.os_dir()).join("OSDK.toml");
|
||||
depends_on_coverage(&manifest_path, &osdk_path);
|
||||
let mut instance = cargo_osdk(["run", "--coverage"]);
|
||||
instance.current_dir(&workspace.os_dir());
|
||||
|
||||
let _output = instance
|
||||
.output()
|
||||
.expect("Failed to wait for QEMU coverage instance");
|
||||
let coverage_file = Path::new(&workspace.os_dir()).join("coverage.profraw");
|
||||
assert!(coverage_file.exists(), "Coverage file not found");
|
||||
}
|
||||
}
|
||||
|
||||
mod qemu_gdb_feature {
|
||||
use super::*;
|
||||
use crate::util::cargo_osdk;
|
||||
|
|
|
|||
|
|
@ -134,6 +134,44 @@ pub(crate) fn depends_on_local_ostd(manifest_path: impl AsRef<Path>) {
|
|||
fs::write(manifest_path, manifest.to_string().as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
/// Makes crates created by `cargo ostd new` enable coverage features,
|
||||
/// adding features = ["coverage"] to ostd dependency and creating a [features] section.
|
||||
///
|
||||
/// This transforms the manifest to enable coverage support.
|
||||
pub(crate) fn depends_on_coverage(manifest_path: impl AsRef<Path>, osdk_path: impl AsRef<Path>) {
|
||||
let manifest_content = fs::read_to_string(&manifest_path).unwrap();
|
||||
let mut manifest: Table = toml::from_str(&manifest_content).unwrap();
|
||||
|
||||
// Add features = ["coverage"] to ostd dependency
|
||||
let dep = manifest
|
||||
.get_mut("dependencies")
|
||||
.map(Value::as_table_mut)
|
||||
.flatten()
|
||||
.unwrap();
|
||||
|
||||
if let Some(ostd_dep) = dep.get_mut("ostd").map(Value::as_table_mut).flatten() {
|
||||
let features = vec![Value::String("coverage".to_string())];
|
||||
ostd_dep.insert("features".to_string(), Value::Array(features));
|
||||
}
|
||||
|
||||
// Add [features] section with coverage = []
|
||||
let mut features_table = Table::new();
|
||||
features_table.insert("coverage".to_string(), Value::Array(vec![]));
|
||||
manifest.insert("features".to_string(), Value::Table(features_table));
|
||||
|
||||
fs::write(manifest_path, manifest.to_string().as_bytes()).unwrap();
|
||||
|
||||
// Modify OSDK.toml to add logfile=qemu.log to chardev line
|
||||
if osdk_path.as_ref().exists() {
|
||||
let osdk_content = fs::read_to_string(&osdk_path).unwrap();
|
||||
let modified_content = osdk_content.replace(
|
||||
"-chardev stdio,id=mux,mux=on,signal=off \\",
|
||||
"-chardev stdio,id=mux,mux=on,signal=off,logfile=qemu.log \\",
|
||||
);
|
||||
fs::write(osdk_path, modified_content).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_tdx_scheme(osdk_path: impl AsRef<Path>) -> std::io::Result<()> {
|
||||
let template_path = Path::new(file!())
|
||||
.parent()
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ unwinding = { version = "=0.2.5", default-features = false, features = ["fde-gnu
|
|||
volatile = "0.6.1"
|
||||
bitvec = { version = "1.0", default-features = false, features = ["alloc"] }
|
||||
|
||||
minicov = { git = "https://github.com/asterinas/minicov", rev = "bd5454a", version = "0.3", optional = true }
|
||||
minicov = { version = "0.3", optional = true }
|
||||
|
||||
[target.x86_64-unknown-none.dependencies]
|
||||
x86_64 = "0.14.13"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ pub enum QemuExitCode {
|
|||
/// `-device isa-debug-exit,iobase=0xf4,iosize=0x04`.
|
||||
pub fn exit_qemu(exit_code: QemuExitCode) -> ! {
|
||||
#[cfg(feature = "coverage")]
|
||||
crate::coverage::dump_profraw();
|
||||
crate::coverage::on_qemu_exit();
|
||||
|
||||
use x86_64::instructions::port::Port;
|
||||
let mut port = Port::new(0xf4);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,19 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::vec::Vec;
|
||||
//! Support for the code coverage feature of OSDK.
|
||||
//!
|
||||
//! For more information about the code coverage feature (`cargo osdk run --coverage`),
|
||||
//! check out the OSDK reference manual.
|
||||
|
||||
pub fn dump_profraw() {
|
||||
let mut coverage = Vec::new();
|
||||
use alloc::vec::Vec;
|
||||
use core::mem::ManuallyDrop;
|
||||
|
||||
/// A hook to be invoked on QEMU exit for dumping the code coverage data.
|
||||
pub(crate) fn on_qemu_exit() {
|
||||
let mut coverage = ManuallyDrop::new(Vec::new());
|
||||
unsafe {
|
||||
minicov::capture_coverage(&mut coverage).unwrap();
|
||||
minicov::capture_coverage(&mut *coverage).unwrap();
|
||||
}
|
||||
|
||||
let coverage = coverage.leak();
|
||||
crate::early_println!("#### Coverage: {:p} {}", coverage.as_ptr(), coverage.len());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue