Add coverage support
Co-authored-by: Marsman1996 <lqliuyuwei@outlook.com>
This commit is contained in:
parent
03fc309b95
commit
79335b272f
|
|
@ -431,6 +431,15 @@ version = "1.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c31a0499c1dc64f458ad13872de75c0eb7e3fdb0e67964610c914b034fc5956e"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
|
@ -1136,6 +1145,15 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minicov"
|
||||
version = "0.3.7"
|
||||
source = "git+https://github.com/asterinas/minicov?rev=bd5454a#bd5454a58f5e64ef67519df9fb2025c96b47f8be"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "multiboot2"
|
||||
version = "0.24.0"
|
||||
|
|
@ -1259,6 +1277,7 @@ dependencies = [
|
|||
"intrusive-collections",
|
||||
"linux-boot-params",
|
||||
"log",
|
||||
"minicov",
|
||||
"multiboot2",
|
||||
"num-traits",
|
||||
"ostd-macros",
|
||||
|
|
@ -1531,6 +1550,15 @@ version = "1.0.20"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sbi-rt"
|
||||
version = "0.0.3"
|
||||
|
|
@ -1587,6 +1615,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.15.0"
|
||||
|
|
@ -1882,12 +1916,104 @@ version = "0.6.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8ca9a5d4debca0633e697c88269395493cebf2e10db21ca2dbde37c1356452"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.40"
|
||||
|
|
|
|||
5
Makefile
5
Makefile
|
|
@ -20,6 +20,7 @@ SMP ?= 1
|
|||
OSTD_TASK_STACK_SIZE_IN_PAGES ?= 64
|
||||
FEATURES ?=
|
||||
NO_DEFAULT_FEATURES ?= 0
|
||||
COVERAGE ?= 0
|
||||
# End of global build options.
|
||||
|
||||
# GDB debugging and profiling options.
|
||||
|
|
@ -110,6 +111,10 @@ else
|
|||
CARGO_OSDK_COMMON_ARGS += --boot-method="$(BOOT_METHOD)"
|
||||
endif
|
||||
|
||||
ifeq ($(COVERAGE), 1)
|
||||
CARGO_OSDK_COMMON_ARGS += --coverage
|
||||
endif
|
||||
|
||||
ifdef FEATURES
|
||||
CARGO_OSDK_COMMON_ARGS += --features="$(FEATURES)"
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -25,6 +25,29 @@ comma separated configuration list:
|
|||
- `vscode`: generate a '.vscode/launch.json' for debugging with Visual Studio Code
|
||||
(Requires [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb)).
|
||||
|
||||
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.
|
||||
|
||||
## Examples
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ riscv = { version = "0.11.1", features = ["s-mode"] }
|
|||
[features]
|
||||
all = ["cvm_guest"]
|
||||
cvm_guest = ["dep:tdx-guest", "ostd/cvm_guest", "aster-virtio/cvm_guest"]
|
||||
coverage = ["ostd/coverage"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ dependencies = [
|
|||
"serde_json",
|
||||
"shlex",
|
||||
"syn",
|
||||
"tempfile",
|
||||
"toml",
|
||||
"which",
|
||||
]
|
||||
|
|
@ -941,6 +942,19 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.5.1"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ serde = { version = "1.0.195", features = ["derive"] }
|
|||
serde_json = "1.0.111"
|
||||
shlex = "1.3.0"
|
||||
syn = { version = "2.0.52", features = ["extra-traits", "full", "parsing", "printing"] }
|
||||
tempfile = "3.14.0"
|
||||
toml = { version = "0.8.8", features = ["preserve_order"] }
|
||||
which = "8.0.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,13 @@ pub mod vm_image;
|
|||
|
||||
use bin::AsterBin;
|
||||
use file::{BundleFile, Initramfs};
|
||||
use std::process;
|
||||
use std::{
|
||||
io::{BufRead, BufReader, Write},
|
||||
os::unix::net::UnixStream,
|
||||
process,
|
||||
time::Duration,
|
||||
};
|
||||
use tempfile::NamedTempFile;
|
||||
use vm_image::{AsterVmImage, AsterVmImageType};
|
||||
|
||||
use std::{
|
||||
|
|
@ -258,19 +264,40 @@ impl Bundle {
|
|||
}
|
||||
}
|
||||
|
||||
info!("Running QEMU: {:#?}", qemu_cmd);
|
||||
let exit_status = if action.qemu.with_monitor {
|
||||
let qemu_socket = NamedTempFile::new().unwrap().into_temp_path();
|
||||
qemu_cmd.arg("-monitor").arg(format!(
|
||||
"unix:{},server,nowait",
|
||||
qemu_socket.to_string_lossy()
|
||||
));
|
||||
|
||||
let exit_status = qemu_cmd.status().unwrap();
|
||||
info!("Running QEMU: {qemu_cmd:#?}");
|
||||
let mut qemu_child = qemu_cmd.spawn().unwrap();
|
||||
std::thread::sleep(Duration::from_secs(1)); // Wait for QEMU to start
|
||||
let mut qemu_monitor_stream = UnixStream::connect(qemu_socket).unwrap();
|
||||
|
||||
// Find the QEMU output in "qemu.log", read it and check if it failed with a panic.
|
||||
// Setting a QEMU log is required for source line stack trace because piping the output
|
||||
// is less desirable when running QEMU with serial redirected to standard I/O.
|
||||
let qemu_log_path = config.work_dir.join("qemu.log");
|
||||
if let Ok(file) = std::fs::File::open(qemu_log_path) {
|
||||
if let Some(aster_bin) = &self.manifest.aster_bin {
|
||||
crate::util::trace_panic_from_log(file, self.path.join(aster_bin.path()));
|
||||
// Check VM status every 0.1 seconds and break the loop if the VM is stopped.
|
||||
while qemu_monitor_stream.write_all(b"info status\n").is_ok() {
|
||||
let status = BufReader::new(&qemu_monitor_stream)
|
||||
.lines()
|
||||
.find(|line| line.as_ref().is_ok_and(|s| s.starts_with("VM status:")));
|
||||
if status.is_some_and(|msg| msg.unwrap() == "VM status: paused (shutdown)") {
|
||||
break;
|
||||
}
|
||||
std::thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
}
|
||||
info!("VM is paused (shutdown)");
|
||||
|
||||
self.post_run_action(config, Some(&mut qemu_monitor_stream));
|
||||
|
||||
let _ = qemu_monitor_stream.write_all(b"quit\n");
|
||||
qemu_child.wait().unwrap()
|
||||
} else {
|
||||
info!("Running QEMU: {qemu_cmd:#?}");
|
||||
let exit_status = qemu_cmd.status().unwrap();
|
||||
self.post_run_action(config, None);
|
||||
exit_status
|
||||
};
|
||||
|
||||
// FIXME: When panicking it sometimes returns success, why?
|
||||
if !exit_status.success() {
|
||||
|
|
@ -309,4 +336,23 @@ impl Bundle {
|
|||
let manifest_file_path = self.path.join("bundle.toml");
|
||||
std::fs::write(manifest_file_path, manifest_file_content).unwrap();
|
||||
}
|
||||
|
||||
fn post_run_action(&self, config: &Config, qemu_monitor_stream: Option<&mut UnixStream>) {
|
||||
// Find the QEMU output in "qemu.log", read it and check if it failed with a panic.
|
||||
// Setting a QEMU log is required for source line stack trace because piping the output
|
||||
// is less desirable when running QEMU with serial redirected to standard I/O.
|
||||
let qemu_log_path = config.work_dir.join("qemu.log");
|
||||
if let Ok(file) = std::fs::File::open(&qemu_log_path) {
|
||||
if let Some(aster_bin) = &self.manifest.aster_bin {
|
||||
crate::util::trace_panic_from_log(file, self.path.join(aster_bin.path()));
|
||||
}
|
||||
}
|
||||
|
||||
// Find the coverage data information in "qemu.log", and dump it if found.
|
||||
if let Some(qemu_monitor_stream) = qemu_monitor_stream {
|
||||
if let Ok(file) = std::fs::File::open(&qemu_log_path) {
|
||||
crate::util::dump_coverage_from_qemu(file, qemu_monitor_stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,11 @@ pub fn main() {
|
|||
let load_config = |common_args: &CommonArgs| {
|
||||
let manifest = TomlManifest::load();
|
||||
let scheme = manifest.get_scheme(common_args.scheme.as_ref());
|
||||
Config::new(scheme, common_args)
|
||||
let mut config = Config::new(scheme, common_args);
|
||||
config
|
||||
.build
|
||||
.append_rustflags(&std::env::var("RUSTFLAGS").unwrap_or_default());
|
||||
config
|
||||
};
|
||||
|
||||
let cli = Cli::parse();
|
||||
|
|
@ -481,4 +485,6 @@ pub struct CommonArgs {
|
|||
global = true
|
||||
)]
|
||||
pub encoding: Option<PayloadEncoding>,
|
||||
#[arg(long = "coverage", help = "Enable coverage", global = true)]
|
||||
pub coverage: bool,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,6 +135,8 @@ pub fn do_cached_build(
|
|||
ActionChoice::Test => (&config.test.build, &config.test.boot),
|
||||
};
|
||||
|
||||
let mut rustflags = rustflags.to_vec();
|
||||
rustflags.push(&build.rustflags);
|
||||
let aster_elf = build_kernel_elf(
|
||||
config.target_arch,
|
||||
&build.profile,
|
||||
|
|
@ -142,7 +144,7 @@ pub fn do_cached_build(
|
|||
build.no_default_features,
|
||||
&build.override_configs[..],
|
||||
&cargo_target_directory,
|
||||
rustflags,
|
||||
&rustflags,
|
||||
);
|
||||
|
||||
// Check the existing bundle's reusability
|
||||
|
|
@ -199,11 +201,9 @@ fn build_kernel_elf(
|
|||
let target_os_string = OsString::from(&arch.triple());
|
||||
let rustc_linker_script_arg = format!("-C link-arg=-T{}.ld", arch);
|
||||
|
||||
let env_rustflags = std::env::var("RUSTFLAGS").unwrap_or_default();
|
||||
let mut rustflags = Vec::from(rustflags);
|
||||
// Asterinas does not support PIC yet.
|
||||
rustflags.extend(vec![
|
||||
&env_rustflags,
|
||||
&rustc_linker_script_arg,
|
||||
"-C relocation-model=static",
|
||||
"-C relro-level=off",
|
||||
|
|
@ -244,6 +244,10 @@ fn build_kernel_elf(
|
|||
command.arg("--config").arg(override_config);
|
||||
}
|
||||
|
||||
const CFLAGS: &str = "CFLAGS_x86_64-unknown-none";
|
||||
let env_cflags = std::env::var(CFLAGS).unwrap_or_default();
|
||||
command.env(CFLAGS, env_cflags + " -fPIC");
|
||||
|
||||
info!("Building kernel ELF using command: {:#?}", command);
|
||||
info!("Building directory: {:?}", std::env::current_dir().unwrap());
|
||||
|
||||
|
|
|
|||
|
|
@ -178,6 +178,10 @@ fn apply_args_after_finalize(action: &mut Action, args: &CommonArgs) {
|
|||
if args.display_grub_menu {
|
||||
action.grub.display_grub_menu = true;
|
||||
}
|
||||
if args.coverage {
|
||||
action.qemu.args += " --no-shutdown";
|
||||
action.qemu.with_monitor = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ pub struct BuildScheme {
|
|||
#[serde(default)]
|
||||
pub strip_elf: bool,
|
||||
pub encoding: Option<PayloadEncoding>,
|
||||
#[serde(default)]
|
||||
pub rustflags: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
@ -42,6 +44,8 @@ pub struct Build {
|
|||
#[serde(default)]
|
||||
pub strip_elf: bool,
|
||||
pub encoding: PayloadEncoding,
|
||||
#[serde(default)]
|
||||
pub rustflags: String,
|
||||
}
|
||||
|
||||
impl Default for Build {
|
||||
|
|
@ -54,6 +58,7 @@ impl Default for Build {
|
|||
linux_x86_legacy_boot: false,
|
||||
strip_elf: false,
|
||||
encoding: PayloadEncoding::default(),
|
||||
rustflags: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -79,6 +84,16 @@ impl Build {
|
|||
if let Some(encoding) = common_args.encoding.clone() {
|
||||
self.encoding.clone_from(&encoding);
|
||||
}
|
||||
|
||||
if common_args.coverage {
|
||||
self.append_rustflags("-Cinstrument-coverage -Zno-profiler-runtime");
|
||||
self.features.push("coverage".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn append_rustflags(&mut self, rustflags: &str) {
|
||||
self.rustflags += " ";
|
||||
self.rustflags += rustflags;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,6 +117,7 @@ impl BuildScheme {
|
|||
if self.encoding.is_none() {
|
||||
self.encoding.clone_from(&parent.encoding);
|
||||
}
|
||||
self.rustflags = parent.rustflags.clone() + " " + &self.rustflags;
|
||||
}
|
||||
|
||||
pub fn finalize(self) -> Build {
|
||||
|
|
@ -113,6 +129,7 @@ impl BuildScheme {
|
|||
linux_x86_legacy_boot: self.linux_x86_legacy_boot,
|
||||
strip_elf: self.strip_elf,
|
||||
encoding: self.encoding.unwrap_or_default(),
|
||||
rustflags: self.rustflags,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ pub struct QemuScheme {
|
|||
pub bootdev_append_options: Option<String>,
|
||||
/// The path of qemu
|
||||
pub path: Option<PathBuf>,
|
||||
/// Whether to run QEMU as daemon and connect to it in monitor mode.
|
||||
pub with_monitor: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
|
||||
|
|
@ -43,6 +45,8 @@ pub struct Qemu {
|
|||
/// [`crate::bundle::Bundle::run`].
|
||||
pub bootdev_append_options: Option<String>,
|
||||
pub path: PathBuf,
|
||||
/// Whether to run QEMU as daemon and connect to it in monitor mode.
|
||||
pub with_monitor: bool,
|
||||
}
|
||||
|
||||
impl Default for Qemu {
|
||||
|
|
@ -51,6 +55,7 @@ impl Default for Qemu {
|
|||
args: String::new(),
|
||||
bootdev_append_options: None,
|
||||
path: PathBuf::from(get_default_arch().system_qemu()),
|
||||
with_monitor: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,6 +71,7 @@ impl PartialEq for Qemu {
|
|||
strip_numbers(&self.args) == strip_numbers(&other.args)
|
||||
&& self.bootdev_append_options == other.bootdev_append_options
|
||||
&& self.path == other.path
|
||||
&& self.with_monitor == other.with_monitor
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +98,9 @@ impl QemuScheme {
|
|||
if self.path.is_none() {
|
||||
self.path.clone_from(&from.path);
|
||||
}
|
||||
if self.with_monitor.is_none() {
|
||||
self.with_monitor.clone_from(&from.with_monitor);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finalize(self, arch: Arch) -> Qemu {
|
||||
|
|
@ -99,6 +108,7 @@ impl QemuScheme {
|
|||
args: self.args.unwrap_or_default(),
|
||||
bootdev_append_options: self.bootdev_append_options,
|
||||
path: self.path.unwrap_or(PathBuf::from(arch.system_qemu())),
|
||||
with_monitor: self.with_monitor.unwrap_or(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ use std::{
|
|||
ffi::OsStr,
|
||||
fs::{self, File},
|
||||
io::{BufRead, BufReader, Result, Write},
|
||||
os::unix::net::UnixStream,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
sync::{LazyLock, Mutex},
|
||||
|
|
@ -310,6 +311,30 @@ pub fn trace_panic_from_log(qemu_log: File, bin_path: PathBuf) {
|
|||
addr2line_proc.wait().unwrap();
|
||||
}
|
||||
|
||||
/// Dump the coverage data from QEMU if the coverage information is found in the log.
|
||||
pub fn dump_coverage_from_qemu(qemu_log: File, monitor_socket: &mut UnixStream) {
|
||||
const COVERAGE_SIGNATRUE: &str = "#### Coverage: ";
|
||||
let reader = rev_buf_reader::RevBufReader::new(qemu_log);
|
||||
|
||||
let Some(line) = reader
|
||||
.lines()
|
||||
.find(|l| l.as_ref().unwrap().starts_with(COVERAGE_SIGNATRUE))
|
||||
.map(|l| l.unwrap())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let line = line.strip_prefix(COVERAGE_SIGNATRUE).unwrap();
|
||||
let (addr, size) = line.split_once(' ').unwrap();
|
||||
let addr = usize::from_str_radix(addr.strip_prefix("0x").unwrap(), 16).unwrap();
|
||||
let size: usize = size.parse().unwrap();
|
||||
|
||||
let cmd = format!("memsave 0x{addr:x} {size} coverage.profraw\n");
|
||||
if monitor_socket.write_all(cmd.as_bytes()).is_ok() {
|
||||
info!("Coverage data saved to coverage.profraw");
|
||||
}
|
||||
}
|
||||
|
||||
/// A guard that ensures the current working directory is restored
|
||||
/// to its original state when the guard goes out of scope.
|
||||
pub struct DirGuard(PathBuf);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ 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 }
|
||||
|
||||
[target.x86_64-unknown-none.dependencies]
|
||||
x86_64 = "0.14.13"
|
||||
x86 = "0.52.0"
|
||||
|
|
@ -59,6 +61,7 @@ fdt = { version = "0.1.5", features = ["pretty-printing"] }
|
|||
default = ["cvm_guest"]
|
||||
# The guest OS support for Confidential VMs (CVMs), e.g., Intel TDX
|
||||
cvm_guest = ["dep:tdx-guest", "dep:iced-x86"]
|
||||
coverage = ["minicov"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ pub enum QemuExitCode {
|
|||
/// QEMU command line arguments that specifies the ISA debug exit device:
|
||||
/// `-device isa-debug-exit,iobase=0xf4,iosize=0x04`.
|
||||
pub fn exit_qemu(exit_code: QemuExitCode) -> ! {
|
||||
#[cfg(feature = "coverage")]
|
||||
crate::coverage::dump_profraw();
|
||||
|
||||
use x86_64::instructions::port::Port;
|
||||
let mut port = Port::new(0xf4);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::vec::Vec;
|
||||
|
||||
pub fn dump_profraw() {
|
||||
let mut coverage = Vec::new();
|
||||
unsafe {
|
||||
minicov::capture_coverage(&mut coverage).unwrap();
|
||||
}
|
||||
|
||||
let coverage = coverage.leak();
|
||||
crate::early_println!("#### Coverage: {:p} {}", coverage.as_ptr(), coverage.len());
|
||||
}
|
||||
|
|
@ -50,6 +50,9 @@ pub mod trap;
|
|||
pub mod user;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(feature = "coverage")]
|
||||
mod coverage;
|
||||
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub use ostd_macros::{
|
||||
|
|
|
|||
Loading…
Reference in New Issue