use anyhow::{Context, Result};
use btf_rs::Btf;
use crate::monitor::btf_offsets::{StructOrFwd, find_struct_or_fwd, member_byte_offset};
use super::SIZEOF_SDT_ID;
#[derive(Debug, Clone)]
pub struct SdtAllocOffsets {
pub allocator_pool: usize,
pub allocator_root: usize,
pub allocator_size: usize,
pub pool_elem_size: usize,
pub desc_allocated: usize,
pub desc_nr_free: usize,
pub desc_chunk: usize,
pub chunk_union: usize,
pub data_header_size: usize,
}
impl SdtAllocOffsets {
pub fn from_btf(btf: &Btf) -> Result<Self> {
let allocator = require_full_struct(btf, "scx_allocator").context(
"btf: struct scx_allocator unavailable (scheduler doesn't link sdt_alloc, or BTF only carries a forward declaration)"
)?;
let allocator_pool = member_byte_offset(btf, &allocator, "pool")?;
let allocator_root = member_byte_offset(btf, &allocator, "root")?;
let allocator_size = allocator.size();
let pool = require_full_struct(btf, "sdt_pool")
.context("btf: struct sdt_pool unavailable for member offsets")?;
let pool_elem_size = member_byte_offset(btf, &pool, "elem_size")?;
let desc = require_full_struct(btf, "sdt_desc")
.context("btf: struct sdt_desc unavailable for member offsets")?;
let desc_allocated = member_byte_offset(btf, &desc, "allocated")?;
let desc_nr_free = member_byte_offset(btf, &desc, "nr_free")?;
let desc_chunk = member_byte_offset(btf, &desc, "chunk")?;
let chunk_union = match find_struct_or_fwd(btf, "sdt_chunk")
.context("btf: struct sdt_chunk not found")?
{
StructOrFwd::Full(chunk) => chunk_union_offset(btf, &chunk)?,
StructOrFwd::Fwd => 0,
};
let data_header_size =
match find_struct_or_fwd(btf, "sdt_data").context("btf: struct sdt_data not found")? {
StructOrFwd::Full(data) => data.size(),
StructOrFwd::Fwd => SIZEOF_SDT_ID,
};
Ok(Self {
allocator_pool,
allocator_root,
allocator_size,
pool_elem_size,
desc_allocated,
desc_nr_free,
desc_chunk,
chunk_union,
data_header_size,
})
}
}
fn require_full_struct(btf: &Btf, name: &str) -> Result<btf_rs::Struct> {
match find_struct_or_fwd(btf, name)? {
StructOrFwd::Full(s) => Ok(s),
StructOrFwd::Fwd => anyhow::bail!(
"btf: struct {name} present only as BTF_KIND_FWD forward declaration; member offsets unavailable"
),
}
}
fn chunk_union_offset(btf: &Btf, chunk: &btf_rs::Struct) -> Result<usize> {
if let Ok(off) = member_byte_offset(btf, chunk, "descs") {
return Ok(off);
}
if let Ok(off) = member_byte_offset(btf, chunk, "data") {
return Ok(off);
}
anyhow::bail!("btf: struct sdt_chunk has neither `descs` nor `data` member")
}