mirror of
https://github.com/sched-ext/scx.git
synced 2024-11-25 04:00:24 +00:00
bpf_builder: It can now compile scx_rusty and scx_layered both within and outside kernel tree
This commit is contained in:
parent
5e93a0f8ff
commit
30b40ac4d3
@ -11,10 +11,12 @@ description = "Utilities for sched_ext schedulers"
|
||||
anyhow = "1.0"
|
||||
bindgen = "0.69"
|
||||
glob = "0.3"
|
||||
lazy_static = "1.4"
|
||||
libbpf-cargo = "0.21"
|
||||
regex = "1.10"
|
||||
sscanf = "0.4"
|
||||
tar = "0.4"
|
||||
version-compare = "0.1"
|
||||
|
||||
[build-dependencies]
|
||||
tar = "0.4"
|
||||
|
@ -4,49 +4,45 @@
|
||||
// GNU General Public License version 2.
|
||||
|
||||
use anyhow::anyhow;
|
||||
use anyhow::bail;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use glob::glob;
|
||||
use libbpf_cargo::SkeletonBuilder;
|
||||
use sscanf::sscanf;
|
||||
use std::collections::BTreeSet;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
const BPF_H_TAR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/bpf_h.tar"));
|
||||
|
||||
fn install_bpf_h<P: AsRef<Path>>(dest: P) -> Result<()> {
|
||||
let mut ar = tar::Archive::new(BPF_H_TAR);
|
||||
ar.unpack(dest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn vmlinux_h_version() -> (String, String) {
|
||||
let mut ar = tar::Archive::new(BPF_H_TAR);
|
||||
|
||||
for file in ar.entries().unwrap() {
|
||||
let file = file.unwrap();
|
||||
if file.header().path().unwrap() != Path::new("vmlinux/vmlinux.h") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = file
|
||||
.link_name()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
return sscanf!(name, "vmlinux-v{String}-g{String}.h").unwrap();
|
||||
}
|
||||
|
||||
panic!("vmlinux/vmlinux.h not found");
|
||||
lazy_static::lazy_static! {
|
||||
// Map clang archs to the __TARGET_ARCH list in
|
||||
// tools/lib/bpf/bpf_tracing.h in the kernel tree.
|
||||
static ref ARCH_MAP: HashMap<&'static str, &'static str> = vec![
|
||||
("x86", "x86"),
|
||||
("x86_64", "x86"),
|
||||
("s390", "s390"),
|
||||
("arm", "arm"),
|
||||
("aarch64", "arm64"),
|
||||
("mips", "mips"),
|
||||
("mips64", "mips"),
|
||||
("ppc32", "powerpc"),
|
||||
("ppc64", "powerpc"),
|
||||
("sparc", "sparc"),
|
||||
("sparcv9", "sparc"),
|
||||
("riscv32", "riscv"),
|
||||
("riscv64", "riscv"),
|
||||
("arc", "arc"), // unsure this is supported
|
||||
("loongarch64", "loongarch"), // ditto
|
||||
].into_iter().collect();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BpfBuilder {
|
||||
clang: String,
|
||||
cflags: String,
|
||||
clang: (String, String, String), // (clang, ver, arch)
|
||||
cflags: Vec<String>,
|
||||
out_dir: PathBuf,
|
||||
|
||||
intf_input_output: Option<(String, String)>,
|
||||
@ -55,11 +51,209 @@ pub struct BpfBuilder {
|
||||
}
|
||||
|
||||
impl BpfBuilder {
|
||||
fn find_clang() -> Result<(String, String, String)> {
|
||||
let clang = env::var("BPF_CLANG").unwrap_or("clang".into());
|
||||
let output = Command::new(&clang)
|
||||
.args(["--version"])
|
||||
.output()
|
||||
.with_context(|| format!("Failed to run \"{} --version\"", &clang))?;
|
||||
|
||||
let stdout = String::from_utf8(output.stdout)?;
|
||||
let (mut ver, mut arch) = (None, None);
|
||||
for line in stdout.lines() {
|
||||
if let Ok(v) = sscanf!(line, "clang version {String}") {
|
||||
// Version could be followed by (URL SHA1). Only take
|
||||
// the first word.
|
||||
ver = Some(v.split_whitespace().next().unwrap().to_string());
|
||||
continue;
|
||||
}
|
||||
if let Ok(v) = sscanf!(line, "Target: {String}") {
|
||||
arch = Some(v.split('-').next().unwrap().to_string());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let (ver, arch) = (
|
||||
ver.ok_or(anyhow!("Failed to read clang version"))?,
|
||||
arch.ok_or(anyhow!("Failed to read clang target arch"))?,
|
||||
);
|
||||
|
||||
if version_compare::compare(&ver, "16") == Ok(version_compare::Cmp::Lt) {
|
||||
bail!("clang < 16 loses high 32 bits of 64 bit enums when compiling BPF");
|
||||
}
|
||||
if version_compare::compare(&ver, "17") == Ok(version_compare::Cmp::Lt) {
|
||||
println!(
|
||||
"cargo:warning=clang >= 17 recommended ({:?} ver={:?})",
|
||||
&clang, &ver
|
||||
);
|
||||
}
|
||||
|
||||
Ok((clang, ver, arch))
|
||||
}
|
||||
|
||||
fn determine_base_cflags(
|
||||
(clang, _ver, arch): &(String, String, String),
|
||||
) -> Result<Vec<String>> {
|
||||
// Determine kernel target arch.
|
||||
let kernel_target = match ARCH_MAP.get(arch.as_str()) {
|
||||
Some(v) => v,
|
||||
None => bail!("CPU arch {:?} not found in ARCH_MAP", &arch),
|
||||
};
|
||||
|
||||
// Determine system includes.
|
||||
let output = Command::new(&clang)
|
||||
.args(["-v", "-E", "-"])
|
||||
.output()
|
||||
.with_context(|| format!("Failed to run \"{} -v -E - < /dev/null", &clang))?;
|
||||
let stderr = String::from_utf8(output.stderr)?;
|
||||
|
||||
let mut sys_incls = None;
|
||||
for line in stderr.lines() {
|
||||
if line == "#include <...> search starts here:" {
|
||||
sys_incls = Some(vec![]);
|
||||
continue;
|
||||
}
|
||||
if sys_incls.is_none() {
|
||||
continue;
|
||||
}
|
||||
if line == "End of search list." {
|
||||
break;
|
||||
}
|
||||
|
||||
sys_incls.as_mut().unwrap().push(line.trim());
|
||||
}
|
||||
let sys_incls = match sys_incls {
|
||||
Some(v) => v,
|
||||
None => bail!("Failed to find system includes from {:?}", &clang),
|
||||
};
|
||||
|
||||
// Determine endian.
|
||||
let output = Command::new(&clang)
|
||||
.args(["-dM", "-E", "-"])
|
||||
.output()
|
||||
.with_context(|| format!("Failed to run \"{} -dM E - < /dev/null", &clang))?;
|
||||
let stdout = String::from_utf8(output.stdout)?;
|
||||
|
||||
let mut endian = None;
|
||||
for line in stdout.lines() {
|
||||
match sscanf!(line, "#define __BYTE_ORDER__ {str}") {
|
||||
Ok(v) => {
|
||||
endian = Some(match v {
|
||||
"__ORDER_LITTLE_ENDIAN__" => "little",
|
||||
"__ORDER_BIG_ENDIAN__" => "big",
|
||||
v => bail!("Unknown __BYTE_ORDER__ {:?}", v),
|
||||
});
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
let endian = match endian {
|
||||
Some(v) => v,
|
||||
None => bail!("Failed to find __BYTE_ORDER__ from {:?}", &clang),
|
||||
};
|
||||
|
||||
// Assemble cflags.
|
||||
let mut cflags: Vec<String> =
|
||||
["-g", "-O2", "-Wall", "-Wno-compare-distinct-pointer-types'"]
|
||||
.into_iter()
|
||||
.map(|x| x.into())
|
||||
.collect();
|
||||
cflags.push(format!("-D__TARGET_ARCH_{}", &kernel_target));
|
||||
cflags.push("-mcpu=v3".into());
|
||||
cflags.push(format!("-m{}-endian", endian));
|
||||
cflags.append(
|
||||
&mut sys_incls
|
||||
.into_iter()
|
||||
.flat_map(|x| ["-idirafter".into(), x.into()])
|
||||
.collect(),
|
||||
);
|
||||
Ok(cflags)
|
||||
}
|
||||
|
||||
const BPF_H_TAR: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/bpf_h.tar"));
|
||||
|
||||
fn install_bpf_h<P: AsRef<Path>>(dest: P) -> Result<()> {
|
||||
let mut ar = tar::Archive::new(Self::BPF_H_TAR);
|
||||
ar.unpack(dest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn vmlinux_h_version() -> (String, String) {
|
||||
let mut ar = tar::Archive::new(Self::BPF_H_TAR);
|
||||
|
||||
for file in ar.entries().unwrap() {
|
||||
let file = file.unwrap();
|
||||
if file.header().path().unwrap() != Path::new("vmlinux/vmlinux.h") {
|
||||
continue;
|
||||
}
|
||||
|
||||
let name = file
|
||||
.link_name()
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
return sscanf!(name, "vmlinux-v{String}-g{String}.h").unwrap();
|
||||
}
|
||||
|
||||
panic!("vmlinux/vmlinux.h not found");
|
||||
}
|
||||
|
||||
fn determine_cflags<P>(clang: &(String, String, String), out_dir: P) -> Result<Vec<String>>
|
||||
where
|
||||
P: AsRef<Path> + std::fmt::Debug,
|
||||
{
|
||||
let bpf_h = out_dir
|
||||
.as_ref()
|
||||
.join("scx_utils-bpf_h")
|
||||
.to_str()
|
||||
.ok_or(anyhow!(
|
||||
"{:?}/scx_utils-bph_h can't be converted to str",
|
||||
&out_dir
|
||||
))?
|
||||
.to_string();
|
||||
Self::install_bpf_h(&bpf_h)?;
|
||||
|
||||
let mut cflags = Vec::<String>::new();
|
||||
|
||||
cflags.append(&mut match env::var("BPF_BASE_CFLAGS") {
|
||||
Ok(v) => v.split_whitespace().map(|x| x.into()).collect(),
|
||||
_ => Self::determine_base_cflags(&clang)?,
|
||||
});
|
||||
|
||||
cflags.append(&mut match env::var("BPF_EXTRA_CFLAGS_PRE_INCL") {
|
||||
Ok(v) => v.split_whitespace().map(|x| x.into()).collect(),
|
||||
_ => vec![],
|
||||
});
|
||||
|
||||
cflags.push(format!("-I{}/vmlinux", &bpf_h));
|
||||
cflags.push(format!("-I{}/common", &bpf_h));
|
||||
|
||||
cflags.append(&mut match env::var("BPF_EXTRA_CFLAGS_POST_INCL") {
|
||||
Ok(v) => v.split_whitespace().map(|x| x.into()).collect(),
|
||||
_ => vec![],
|
||||
});
|
||||
|
||||
Ok(cflags)
|
||||
}
|
||||
|
||||
pub fn new() -> Result<Self> {
|
||||
let out_dir = PathBuf::from(env::var("OUT_DIR")?);
|
||||
|
||||
let clang = Self::find_clang()?;
|
||||
let cflags = match env::var("BPF_CFLAGS") {
|
||||
Ok(v) => v.split_whitespace().map(|x| x.into()).collect(),
|
||||
_ => Self::determine_cflags(&clang, &out_dir)?,
|
||||
};
|
||||
|
||||
println!("scx_utils:clang={:?} {:?}", &clang, &cflags);
|
||||
|
||||
Ok(Self {
|
||||
clang: env::var("BPF_CLANG")?,
|
||||
cflags: env::var("BPF_CFLAGS")?,
|
||||
out_dir: PathBuf::from(env::var("OUT_DIR")?),
|
||||
clang,
|
||||
cflags,
|
||||
out_dir,
|
||||
|
||||
intf_input_output: None,
|
||||
skel_input_name: None,
|
||||
@ -85,6 +279,14 @@ impl BpfBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
fn cflags_string(&self) -> String {
|
||||
self.cflags
|
||||
.iter()
|
||||
.map(|x| x.as_str())
|
||||
.collect::<Vec<&str>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
fn bindgen_bpf_intf(&self, deps: &mut BTreeSet<String>) -> Result<()> {
|
||||
let (input, output) = match &self.intf_input_output {
|
||||
Some(pair) => pair,
|
||||
@ -98,7 +300,7 @@ impl BpfBuilder {
|
||||
// you build up options for the resulting bindings.
|
||||
let bindings = bindgen::Builder::default()
|
||||
// Should run clang with the same -I options as BPF compilation.
|
||||
.clang_args(self.cflags.split_whitespace())
|
||||
.clang_args(&self.cflags)
|
||||
// The input header we would like to generate bindings for.
|
||||
.header(input)
|
||||
// Tell cargo to invalidate the built crate whenever any of the
|
||||
@ -113,10 +315,10 @@ impl BpfBuilder {
|
||||
}
|
||||
|
||||
fn gen_bpf_skel(&self, deps: &mut BTreeSet<String>) -> Result<()> {
|
||||
let (input, name) = match &self.skel_input_name {
|
||||
Some(pair) => pair,
|
||||
None => return Ok(()),
|
||||
};
|
||||
let (input, name) = match &self.skel_input_name {
|
||||
Some(pair) => pair,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let obj = self.out_dir.join(format!("{}.bpf.o", name));
|
||||
let skel_path = self.out_dir.join(format!("{}_skel.rs", name));
|
||||
@ -124,8 +326,8 @@ impl BpfBuilder {
|
||||
SkeletonBuilder::new()
|
||||
.source(input)
|
||||
.obj(&obj)
|
||||
.clang(&self.clang)
|
||||
.clang_args(&self.cflags)
|
||||
.clang(&self.clang.0)
|
||||
.clang_args(self.cflags_string())
|
||||
.build_and_generate(&skel_path)?;
|
||||
|
||||
match &self.skel_deps {
|
||||
@ -174,22 +376,16 @@ mod tests {
|
||||
use std::io::BufReader;
|
||||
|
||||
#[test]
|
||||
fn test_install_bpf_h() {
|
||||
let dir = concat!(env!("OUT_DIR"), "/test_install_bpf_h");
|
||||
super::install_bpf_h(dir).unwrap();
|
||||
|
||||
let vmlinux_h = File::open(format!("{}/vmlinux/vmlinux.h", dir)).unwrap();
|
||||
assert_eq!(
|
||||
BufReader::new(vmlinux_h).lines().next().unwrap().unwrap(),
|
||||
"#ifndef __VMLINUX_H__"
|
||||
);
|
||||
fn test_bpf_builder_new() {
|
||||
let res = super::BpfBuilder::new();
|
||||
assert!(res.is_ok(), "Failed to create BpfBuilder ({:?})", &res);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vmlinux_h_version() {
|
||||
let (ver, sha1) = super::vmlinux_h_version();
|
||||
let (ver, sha1) = super::BpfBuilder::vmlinux_h_version();
|
||||
|
||||
println!("test_vmlinux_h_version: ver={:?} sha1={:?}", &ver, &sha1,);
|
||||
println!("vmlinux.h: ver={:?} sha1={:?}", &ver, &sha1,);
|
||||
|
||||
assert!(
|
||||
regex::Regex::new(r"^[1-9][0-9]*\.[1-9][0-9]*(\.[1-9][0-9]*)?$")
|
||||
|
Loading…
Reference in New Issue
Block a user