integration-test: compile without touching disk

pull/644/head
Tamir Duberstein 1 year ago
parent 3d463a3610
commit c76d5a9950
No known key found for this signature in database

@ -18,7 +18,6 @@ object = { version = "0.31", default-features = false, features = [
"elf", "elf",
] } ] }
rbpf = "0.2.0" rbpf = "0.2.0"
tempfile = "3.3.0"
tokio = { version = "1.24", default-features = false, features = ["time"] } tokio = { version = "1.24", default-features = false, features = ["time"] }
[build-dependencies] [build-dependencies]

@ -1,6 +1,9 @@
use anyhow::{bail, Context as _, Result}; use anyhow::{anyhow, bail, Context as _, Result};
use std::{path::PathBuf, process::Command, thread::sleep, time::Duration}; use std::{
use tempfile::TempDir; process::{Child, ChildStdout, Command, Stdio},
thread::sleep,
time::Duration,
};
use aya::{maps::Array, programs::TracePoint, util::KernelVersion, BpfLoader, Btf, Endianness}; use aya::{maps::Array, programs::TracePoint, util::KernelVersion, BpfLoader, Btf, Endianness};
@ -215,9 +218,15 @@ impl RelocationTest {
/// - Generate the source eBPF filling a template /// - Generate the source eBPF filling a template
/// - Compile it with clang /// - Compile it with clang
fn build_ebpf(&self) -> Result<Vec<u8>> { fn build_ebpf(&self) -> Result<Vec<u8>> {
let local_definition = self.local_definition; use std::io::Read as _;
let relocation_code = self.relocation_code;
let (_tmp_dir, compiled_file) = compile(&format!( let Self {
local_definition,
relocation_code,
..
} = self;
let mut stdout = compile(&format!(
r#" r#"
#include <linux/bpf.h> #include <linux/bpf.h>
@ -250,23 +259,29 @@ impl RelocationTest {
char _license[] __attribute__((section("license"), used)) = "GPL"; char _license[] __attribute__((section("license"), used)) = "GPL";
"# "#
)) ))
.context("Failed to compile eBPF program")?; .context("failed to compile eBPF program")?;
let bytecode = let mut output = Vec::new();
std::fs::read(compiled_file).context("Error reading compiled eBPF program")?; stdout.read_to_end(&mut output)?;
Ok(bytecode) Ok(output)
} }
/// - Generate the target BTF source with a mock main() /// - Generate the target BTF source with a mock main()
/// - Compile it with clang /// - Compile it with clang
/// - Extract the BTF with llvm-objcopy /// - Extract the BTF with llvm-objcopy
fn build_btf(&self) -> Result<Btf> { fn build_btf(&self) -> Result<Btf> {
let target_btf = self.target_btf; use std::io::Read as _;
let relocation_code = self.relocation_code;
let Self {
target_btf,
relocation_code,
..
} = self;
// BTF files can be generated and inspected with these commands: // BTF files can be generated and inspected with these commands:
// $ clang -c -g -O2 -target bpf target.c // $ clang -c -g -O2 -target bpf target.c
// $ pahole --btf_encode_detached=target.btf -V target.o // $ pahole --btf_encode_detached=target.btf -V target.o
// $ bpftool btf dump file ./target.btf format c // $ bpftool btf dump file ./target.btf format c
let (tmp_dir, compiled_file) = compile(&format!( let stdout = compile(&format!(
r#" r#"
#include <linux/bpf.h> #include <linux/bpf.h>
@ -280,14 +295,20 @@ impl RelocationTest {
}} }}
"# "#
)) ))
.context("Failed to compile BTF")?; .context("failed to compile BTF")?;
let mut cmd = Command::new("llvm-objcopy"); let mut cmd = Command::new("llvm-objcopy");
cmd.current_dir(tmp_dir.path()) cmd.args(["--dump-section", ".BTF=-", "-"])
.args(["--dump-section", ".BTF=target.btf"]) .stdin(stdout)
.arg(compiled_file); .stdout(Stdio::piped());
let status = cmd let mut child = cmd
.status() .spawn()
.with_context(|| format!("Failed to run {cmd:?}"))?; .with_context(|| format!("failed to spawn {cmd:?}"))?;
let Child { stdout, .. } = &mut child;
let mut stdout = stdout.take().ok_or(anyhow!("failed to open stdout"))?;
let status = child
.wait()
.with_context(|| format!("failed to wait for {cmd:?}"))?;
match status.code() { match status.code() {
Some(code) => match code { Some(code) => match code {
0 => {} 0 => {}
@ -295,25 +316,39 @@ impl RelocationTest {
}, },
None => bail!("{cmd:?} terminated by signal"), None => bail!("{cmd:?} terminated by signal"),
} }
let btf = Btf::parse_file(tmp_dir.path().join("target.btf"), Endianness::default())
.context("Error parsing generated BTF")?; let mut output = Vec::new();
Ok(btf) stdout.read_to_end(&mut output)?;
Btf::parse(output.as_slice(), Endianness::default())
.context("failed to parse generated BTF")
} }
} }
/// Compile an eBPF program and return the path of the compiled object. /// Compile an eBPF program and return its bytes.
/// Also returns a TempDir handler, dropping it will clear the created dicretory. fn compile(source_code: &str) -> Result<ChildStdout> {
fn compile(source_code: &str) -> Result<(TempDir, PathBuf)> { use std::io::Write as _;
let tmp_dir = tempfile::tempdir().context("Error making temp dir")?;
let source = tmp_dir.path().join("source.c");
std::fs::write(&source, source_code).context("Writing bpf program failed")?;
let mut cmd = Command::new("clang"); let mut cmd = Command::new("clang");
cmd.current_dir(&tmp_dir) cmd.args([
.args(["-c", "-g", "-O2", "-target", "bpf"]) "-c", "-g", "-O2", "-target", "bpf", "-x", "c", "-", "-o", "-",
.arg(&source); ])
let status = cmd .stdin(Stdio::piped())
.status() .stdout(Stdio::piped());
.with_context(|| format!("Failed to run {cmd:?}"))?; let mut child = cmd
.spawn()
.with_context(|| format!("failed to spawn {cmd:?}"))?;
let Child { stdin, stdout, .. } = &mut child;
{
let mut stdin = stdin.take().ok_or(anyhow!("failed to open stdin"))?;
stdin
.write_all(source_code.as_bytes())
.context("failed to write to stdin")?;
}
let stdout = stdout.take().ok_or(anyhow!("failed to open stdout"))?;
let status = child
.wait()
.with_context(|| format!("failed to wait for {cmd:?}"))?;
match status.code() { match status.code() {
Some(code) => match code { Some(code) => match code {
0 => {} 0 => {}
@ -321,7 +356,7 @@ fn compile(source_code: &str) -> Result<(TempDir, PathBuf)> {
}, },
None => bail!("{cmd:?} terminated by signal"), None => bail!("{cmd:?} terminated by signal"),
} }
Ok((tmp_dir, source.with_extension("o"))) Ok(stdout)
} }
struct RelocationTestRunner { struct RelocationTestRunner {

Loading…
Cancel
Save