pub trait BpfMapAccessor {
// Required methods
fn maps(&self) -> Vec<BpfMapInfo>;
fn read_value(
&self,
map: &BpfMapInfo,
offset: usize,
len: usize,
) -> Option<Vec<u8>>;
fn read_array(&self, map: &BpfMapInfo, key: u32) -> Option<Vec<u8>>;
fn iter_hash_map(&self, map: &BpfMapInfo) -> Vec<(Vec<u8>, Vec<u8>)>;
fn read_percpu_array(
&self,
map: &BpfMapInfo,
key: u32,
num_cpus: u32,
) -> Vec<Option<Vec<u8>>>;
// Provided methods
fn find_map(&self, name_suffix: &str) -> Option<BpfMapInfo> { ... }
fn iter_percpu_hash_map(
&self,
_map: &BpfMapInfo,
_num_cpus: u32,
) -> Vec<(Vec<u8>, Vec<Option<Vec<u8>>>)> { ... }
fn iter_task_storage(&self, _map: &BpfMapInfo) -> Vec<(Vec<u8>, Vec<u8>)> { ... }
fn read_arena_pages(
&self,
_map: &BpfMapInfo,
_arena_offsets: &BpfArenaOffsets,
) -> ArenaSnapshot { ... }
fn load_program_btf(
&self,
_map: &BpfMapInfo,
_base_btf: &Btf,
) -> Option<Btf> { ... }
}Expand description
Read-only abstraction over BPF map enumeration and value reads across data sources. Mutating operations (write_value etc.) are inherent on each backend, NOT exposed here — the trait surface is a snapshot-style read API used by the failure-dump renderer and any future read-only consumer.
Two implementations exist today: GuestMemMapAccessor (this
module — reads a frozen guest VM’s physical memory) and
super::bpf_syscall::BpfSyscallAccessor (live-host introspection
via the bpf() syscall). Both plug into this trait surface.
GuestMemMapAccessor— reads from a frozen guest VM’s physical memory via PTE walks against the frozeninit_mm. Used by the freeze-coordinator path (super::dump::dump_state) on the in-VM scheduler test runs. Hash map iteration walksbpf_htab.bucketsdirectly without RCU; the freeze rendezvous IS the ordering primitive (every CPU is parked at a known KVM exit before the host begins reading memory). Per-CPU value reads use the cached__per_cpu_offset[cpu]array; out-of-range CPUs surface asNonerather than aliasing CPU 0 (seeread_percpu_array_value).
The live-host backend produces identical
BpfMapInfo / byte buffers, so the rendering pipeline
(super::btf_render::render_value) stays data-source-agnostic
and consumes either accessor through this trait. The
live-host backend’s failure modes are different (e.g. hash reads
will rely on the kernel’s RCU read-side critical section,
bpf_map_lookup_elem rejection for non-readable types) and
individual method docs spell those out where they matter.
dump_state currently takes a concrete
GuestMemMapAccessor because its sdt_alloc post-pass walks
the underlying super::guest::GuestKernel — that handle is
not part of the trait surface. Once sdt_alloc walking moves
into a backend-specific path, dump_state can switch to
&dyn BpfMapAccessor. Other call
sites that need only the trait surface can already bind on
&dyn BpfMapAccessor (or <A: BpfMapAccessor>) without paying
virtual dispatch.
Required Methods§
Sourcefn maps(&self) -> Vec<BpfMapInfo>
fn maps(&self) -> Vec<BpfMapInfo>
Enumerate every BPF map visible to this accessor.
Order is implementation-defined: the guest-memory backend walks
map_idr (allocation order); the bpf-syscall backend walks the
kernel’s id space via BPF_MAP_GET_NEXT_ID (also allocation
order, modulo concurrent destruction races on the live host).
Callers that want a stable view should sort by name.
Sourcefn read_value(
&self,
map: &BpfMapInfo,
offset: usize,
len: usize,
) -> Option<Vec<u8>>
fn read_value( &self, map: &BpfMapInfo, offset: usize, len: usize, ) -> Option<Vec<u8>>
Read a contiguous byte range from a map’s value region.
Returns None for non-readable map types (e.g. ARENA — use
Self::read_arena_pages; HASH — use Self::iter_hash_map)
or when the backing read fails. The guest-memory backend’s
failure modes are unmapped guest pages and out-of-range value
regions; the bpf-syscall backend additionally surfaces
bpf_map_lookup_elem rejection (e.g. -EINVAL on
arena maps, kernel-side ACL denials).
Sourcefn read_array(&self, map: &BpfMapInfo, key: u32) -> Option<Vec<u8>>
fn read_array(&self, map: &BpfMapInfo, key: u32) -> Option<Vec<u8>>
Read the value bytes of one entry of a BPF_MAP_TYPE_ARRAY map
by entry index.
Parallels Self::read_percpu_array (also keyed by entry
index) but for a plain ARRAY: one value per key, so the return
is a single Option<Vec<u8>> rather than a per-CPU vector. On
success the buffer is exactly map.value_size bytes.
Returns None for non-ARRAY maps, key >= map.max_entries, or
when the backing read fails (unmapped guest page on the
guest-memory backend; bpf_map_lookup_elem rejection on the
live-host backend). Distinct from Self::read_value, which
stays the byte-range reader for single-entry global-section
ARRAYs and STRUCT_OPS (both key 0); multi-entry ARRAY indexing
goes through this method so read_value’s key-0 contract is
untouched.
Sourcefn iter_hash_map(&self, map: &BpfMapInfo) -> Vec<(Vec<u8>, Vec<u8>)>
fn iter_hash_map(&self, map: &BpfMapInfo) -> Vec<(Vec<u8>, Vec<u8>)>
Iterate every entry in a BPF_MAP_TYPE_HASH or
BPF_MAP_TYPE_LRU_HASH map.
Both share the inline-value htab_elem layout
(kernel/bpf/hashtab.c::htab_elem_value); LRU adds an
eviction policy but the value bytes still sit at
key + round_up(key_size, 8). Returns an empty vec for any
other map type.
Per-element atomicity is backend-specific: the guest-memory
backend reads raw bytes at the freeze instant (the freeze
rendezvous IS the synchronization — no concurrent writers
exist while parked vCPUs stay parked); the bpf-syscall backend
reads under the kernel’s RCU read-side critical section
(bpf_map_lookup_elem -> htab_map_lookup_elem). Both can
produce torn views relative to a multi-element transaction
the scheduler intended to commit atomically — that’s a feature
of reading without locking the whole table.
Sourcefn read_percpu_array(
&self,
map: &BpfMapInfo,
key: u32,
num_cpus: u32,
) -> Vec<Option<Vec<u8>>>
fn read_percpu_array( &self, map: &BpfMapInfo, key: u32, num_cpus: u32, ) -> Vec<Option<Vec<u8>>>
Read every CPU’s value for a key in a BPF_MAP_TYPE_PERCPU_ARRAY map.
Returns one entry per CPU, indexed by CPU number. Some(bytes)
when the per-CPU slot is readable; None when it isn’t (e.g.
an out-of-range CPU index — __per_cpu_offset[cpu] reads as
the BSS-zero sentinel — or an unmapped page on the
guest-memory path; the bpf-syscall backend surfaces
out-of-range CPU on bpf_map_lookup_elem failure). Returns an
empty vec for non-PERCPU_ARRAY maps or key >= max_entries.
Provided Methods§
Sourcefn find_map(&self, name_suffix: &str) -> Option<BpfMapInfo>
fn find_map(&self, name_suffix: &str) -> Option<BpfMapInfo>
Find the first BPF map whose name ends with name_suffix.
Default impl walks Self::maps. Backends with cheaper
targeted lookups can override (e.g. a libbpf-handle-backed
accessor that already holds a name index).
Sourcefn iter_percpu_hash_map(
&self,
_map: &BpfMapInfo,
_num_cpus: u32,
) -> Vec<(Vec<u8>, Vec<Option<Vec<u8>>>)>
fn iter_percpu_hash_map( &self, _map: &BpfMapInfo, _num_cpus: u32, ) -> Vec<(Vec<u8>, Vec<Option<Vec<u8>>>)>
Iterate every entry in a BPF_MAP_TYPE_PERCPU_HASH or
BPF_MAP_TYPE_LRU_PERCPU_HASH map. Returns
(key_bytes, per_cpu_values) where per_cpu_values is one
entry per CPU indexed by CPU number; Some(bytes) when the
CPU’s slot is readable, None otherwise (unmapped page or
out-of-range CPU).
Returns an empty vec for any other map type. Default implementation returns empty so backends that haven’t yet wired the percpu-hash path don’t break trait dispatch — the dump renderer surfaces the resulting empty list as a “no entries” outcome rather than a panic.
Sourcefn iter_task_storage(&self, _map: &BpfMapInfo) -> Vec<(Vec<u8>, Vec<u8>)>
fn iter_task_storage(&self, _map: &BpfMapInfo) -> Vec<(Vec<u8>, Vec<u8>)>
Iterate every entry in a BPF_MAP_TYPE_TASK_STORAGE map (and
the shape-identical INODE_STORAGE / SK_STORAGE /
CGRP_STORAGE variants — they all use
super::btf_offsets::TaskStorageOffsets).
Returned tuples are (owner_kva_le_bytes, value_bytes):
owner_kva_le_bytesis the 8-byte little-endian encoding of thebpf_local_storage.ownerpointer reached by following eachbpf_local_storage_elem.local_storage. ForTASK_STORAGEthis is thetask_structKVA; for the other variants it is the inode/sock/cgroup KVA. The walker treats it as opaque so the same shape works across all four map types.value_bytesisvalue_sizebytes copied frombpf_local_storage_elem.sdata.data[]— the value the scheduler stored under this owner.
Returns an empty vec for any other map type, when
task_storage_offsets is unavailable, or when the map’s
buckets pointer cannot be translated. Returns an empty vec
for any other map type. Default implementation returns empty
so backends that haven’t yet wired this path don’t break
trait dispatch — the dump renderer surfaces the resulting
empty list as a “no entries” outcome rather than a panic.
Sourcefn read_arena_pages(
&self,
_map: &BpfMapInfo,
_arena_offsets: &BpfArenaOffsets,
) -> ArenaSnapshot
fn read_arena_pages( &self, _map: &BpfMapInfo, _arena_offsets: &BpfArenaOffsets, ) -> ArenaSnapshot
Snapshot every mapped page of a BPF_MAP_TYPE_ARENA map.
arena_offsets resolves kernel struct field offsets the
guest-memory backend uses to walk bpf_arena -> kern_vm -> vm_struct.addr; the bpf-syscall backend mmaps the arena fd
directly (the only data path the kernel exposes — arena’s
lookup_elem returns -EINVAL, see kernel/bpf/arena.c)
and ignores arena_offsets. The default
implementation returns an empty snapshot; backends override to
produce real content.
Sourcefn load_program_btf(&self, _map: &BpfMapInfo, _base_btf: &Btf) -> Option<Btf>
fn load_program_btf(&self, _map: &BpfMapInfo, _base_btf: &Btf) -> Option<Btf>
Load the program BTF object referenced by a map.
base_btf is the host’s vmlinux BTF used as the base for
split-BTF parsing. Returns None when the map carries no
program BTF (e.g. kernel-builtin maps), when the BTF blob can’t
be loaded, or when [btf_rs::Btf::from_bytes] /
[btf_rs::Btf::from_split_bytes] reject the bytes.
The default implementation returns None; backends override to
hand back a parsed [btf_rs::Btf].