diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 499942f921..90492ffd5f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -131,11 +131,11 @@ jobs: profile: [dev, release] include: - arch: x86_64 - packages: qemu-system-x86 libcap-ng-dev libseccomp-dev - flags: --features pci-ids + packages: qemu-system-x86 libcap-ng-dev libseccomp-dev uftrace + flags: --features hermit/pci-ids - arch: aarch64 packages: qemu-system-aarch64 - flags: --features pci-ids + flags: --features hermit/pci-ids - arch: riscv64 packages: qemu-system-misc flags: --no-default-features @@ -157,6 +157,9 @@ jobs: run: | sudo apt-get update sudo apt-get install ${{ matrix.packages }} + - uses: dtolnay/rust-toolchain@nightly + with: + components: rust-src - uses: mkroening/rust-toolchain-toml@main - uses: mkroening/rust-toolchain-toml@main with: @@ -185,6 +188,8 @@ jobs: - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package rusty_demo --smp 4 # https://github.com/hermit-os/kernel/issues/737 if: ${{ matrix.arch != 'aarch64' }} + - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package rftrace-example --virtiofsd + if: ${{ matrix.arch == 'x86_64' }} - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package httpd --features ci,dhcpv4 --netdev virtio-net-pci if: ${{ matrix.arch != 'riscv64' }} - run: cargo xtask ci qemu --arch ${{ matrix.arch }} --profile ${{ matrix.profile }} ${{ matrix.flags }} --package httpd --features ci,dhcpv4 --netdev rtl8139 --features rtl8139 diff --git a/xtask/src/archive.rs b/xtask/src/archive.rs index aed6283dfc..9c11b6537c 100644 --- a/xtask/src/archive.rs +++ b/xtask/src/archive.rs @@ -1,12 +1,10 @@ use std::collections::HashSet; -use std::env; use std::fmt::Write; use std::path::{Path, PathBuf}; -use anyhow::{anyhow, Result}; +use anyhow::Result; use goblin::archive::Archive as GoblinArchive; use goblin::elf64::header; -use llvm_tools::LlvmTools; use xshell::cmd; pub struct Archive(PathBuf); @@ -51,7 +49,7 @@ impl Archive { }; let all_symbols = { - let nm = binutil("nm")?; + let nm = crate::binutil("nm")?; let stdout = cmd!(sh, "{nm} --export-symbols {archive}").output()?.stdout; String::from_utf8(stdout)? }; @@ -75,7 +73,7 @@ impl Archive { let rename_path = archive.with_extension("redefine-syms"); sh.write_file(&rename_path, symbol_renames)?; - let objcopy = binutil("objcopy")?; + let objcopy = crate::binutil("objcopy")?; cmd!(sh, "{objcopy} --redefine-syms={rename_path} {archive}").run()?; sh.remove_path(&rename_path)?; @@ -88,7 +86,7 @@ impl Archive { let archive = self.as_ref(); let file = file.as_ref(); - let ar = binutil("ar")?; + let ar = crate::binutil("ar")?; cmd!(sh, "{ar} qL {archive} {file}").run()?; Ok(()) @@ -115,22 +113,3 @@ impl Archive { Ok(()) } } - -fn binutil(name: &str) -> Result { - let exe_suffix = env::consts::EXE_SUFFIX; - let exe = format!("llvm-{name}{exe_suffix}"); - - let path = LlvmTools::new() - .map_err(|err| match err { - llvm_tools::Error::NotFound => anyhow!( - "Could not find llvm-tools component\n\ - \n\ - Maybe the rustup component `llvm-tools` is missing? Install it through: `rustup component add llvm-tools`" - ), - err => anyhow!("{err:?}"), - })? - .tool(&exe) - .ok_or_else(|| anyhow!("could not find {exe}"))?; - - Ok(path) -} diff --git a/xtask/src/binutil.rs b/xtask/src/binutil.rs new file mode 100644 index 0000000000..e4ca73a886 --- /dev/null +++ b/xtask/src/binutil.rs @@ -0,0 +1,22 @@ +use std::path::PathBuf; + +use anyhow::{anyhow, Result}; + +pub fn binutil(name: &str) -> Result { + let exe_suffix = std::env::consts::EXE_SUFFIX; + let exe = format!("llvm-{name}{exe_suffix}"); + + let path = llvm_tools::LlvmTools::new() + .map_err(|err| match err { + llvm_tools::Error::NotFound => anyhow!( + "Could not find llvm-tools component\n\ + \n\ + Maybe the rustup component `llvm-tools` is missing? Install it through: `rustup component add llvm-tools`" + ), + err => anyhow!("{err:?}"), + })? + .tool(&exe) + .ok_or_else(|| anyhow!("could not find {exe}"))?; + + Ok(path) +} diff --git a/xtask/src/ci/build.rs b/xtask/src/ci/build.rs index 84cd3047a0..e3529ecfc1 100644 --- a/xtask/src/ci/build.rs +++ b/xtask/src/ci/build.rs @@ -26,6 +26,15 @@ impl Build { let sh = crate::sh()?; + let _push_env = if self.package.contains("rftrace") { + Some(sh.push_env( + "RUSTFLAGS", + "-Zinstrument-mcount -Cpasses=ee-instrument", + )) + } else { + None + }; + cmd!(sh, "cargo build --manifest-path ../Cargo.toml") .args(self.cargo_build.artifact.arch.ci_cargo_args()) .cargo_build_args(&self.cargo_build) diff --git a/xtask/src/ci/qemu.rs b/xtask/src/ci/qemu.rs index 35652ca17e..939b85ba7e 100644 --- a/xtask/src/ci/qemu.rs +++ b/xtask/src/ci/qemu.rs @@ -1,9 +1,10 @@ use std::io::{Read, Write}; use std::net::{TcpStream, UdpSocket}; +use std::path::Path; use std::process::{Child, Command, ExitStatus}; use std::str::from_utf8; use std::time::Duration; -use std::{env, thread}; +use std::{env, fs, thread}; use anyhow::{bail, ensure, Context, Result}; use clap::{Args, ValueEnum}; @@ -63,6 +64,10 @@ impl Qemu { let virtiofsd = self.virtiofsd.then(spawn_virtiofsd).transpose()?; thread::sleep(Duration::from_millis(100)); + if self.build.package.contains("rftrace") { + sh.create_dir("shared/tracedir")?; + } + let arch = self.build.cargo_build.artifact.arch.name(); let qemu = env::var_os("QEMU").unwrap_or_else(|| format!("qemu-system-{arch}").into()); @@ -109,6 +114,10 @@ impl Qemu { assert!(status.success()); } + if self.build.package.contains("rftrace") { + check_rftrace(&self.build.image())?; + } + Ok(()) } @@ -246,9 +255,9 @@ impl Qemu { fn spawn_virtiofsd() -> Result { let sh = crate::sh()?; - sh.create_dir("foo")?; + sh.create_dir("shared")?; - let cmd = cmd!(sh, "virtiofsd --socket-path=./vhostqemu --shared-dir ./foo --announce-submounts --sandbox none --seccomp none --inode-file-handles=never"); + let cmd = cmd!(sh, "virtiofsd --socket-path=./vhostqemu --shared-dir ./shared --announce-submounts --sandbox none --seccomp none --inode-file-handles=never"); eprintln!("$ {cmd}"); @@ -332,6 +341,32 @@ fn test_mioudp() -> Result<()> { Ok(()) } +fn check_rftrace(image: &Path) -> Result<()> { + let sh = crate::sh()?; + let image_name = image.file_name().unwrap().to_str().unwrap(); + + let nm = crate::binutil("nm")?; + let symbols = cmd!(sh, "{nm} --numeric-sort {image}").output()?.stdout; + sh.write_file(format!("shared/tracedir/{image_name}.sym"), symbols)?; + + let replay = cmd!( + sh, + "uftrace replay --data=shared/tracedir --output-fields=tid" + ) + .read()?; + eprintln!("[CI] replay: {replay}"); + + let expected = fs::read_to_string("xtask/src/ci/rftrace.snap")?; + if !replay.starts_with(&expected) { + eprintln!("[CI] expected: {expected}"); + bail!("rftrace output does not match snapshot"); + } + + eprintln!("[CI] replay matches snapshot"); + + Ok(()) +} + struct KillChildOnDrop(Child); impl Drop for KillChildOnDrop { diff --git a/xtask/src/ci/rftrace.snap b/xtask/src/ci/rftrace.snap new file mode 100644 index 0000000000..fec57ad805 --- /dev/null +++ b/xtask/src/ci/rftrace.snap @@ -0,0 +1,4 @@ +# TID FUNCTION + [ 1] | rftrace_example::f1() { + [ 1] | rftrace_example::f2() { + [ 1] | rftrace_example::f3() \ No newline at end of file diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 133db08239..af71cf868c 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -3,6 +3,7 @@ mod arch; mod archive; mod artifact; +mod binutil; mod build; mod cargo_build; mod ci; @@ -11,6 +12,7 @@ mod clippy; use std::path::Path; use anyhow::Result; +pub(crate) use binutil::binutil; use clap::Parser; use xshell::Shell;