BpfMapAccessor

Trait BpfMapAccessor 

Source
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 frozen init_mm. Used by the freeze-coordinator path (super::dump::dump_state) on the in-VM scheduler test runs. Hash map iteration walks bpf_htab.buckets directly 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 as None rather than aliasing CPU 0 (see read_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§

Source

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.

Source

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).

Source

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.

Source

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.

Source

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§

Source

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).

Source

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.

Source

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_bytes is the 8-byte little-endian encoding of the bpf_local_storage.owner pointer reached by following each bpf_local_storage_elem.local_storage. For TASK_STORAGE this is the task_struct KVA; 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_bytes is value_size bytes copied from bpf_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.

Source

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.

Source

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].

Implementors§