Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

周报(1.20 - 1.31)

开发分支

  • axvisor: https://github.com/Iscreamx/axvisor/tree/feature/ebpf
  • axebpf: https://github.com/Iscreamx/axebpf

一、已完成工作

  • axebpf 独立模块:集成 ksymtracepointtp-lexerkbpf-basicrbpf 实现 eBPF 运行时,支持程序加载执行,Array/HashMap/LruHash/Queue 四种 Map 类型,6 个标准 helper 函数和 3 个 Hypervisor 专用 helper(获取 vm_id/vcpu_id/exit_reason)。效果:可在 Hypervisor 内核中动态加载 eBPF 字节码进行追踪,无需重新编译内核。

  • Shell trace 命令:支持 trace list/enable/disable/stat/reset/load/unload/progs/latency/verbose 等命令。效果:可通过 shell 交互式启用追踪点,附加 eBPF 程序,查看统计数据和延迟直方图。

  • Tracepoint 定义:定义了 25+ 个 VMM 追踪点,其中 VM 生命周期(vm_create/boot/shutdown)、系统初始化(vmm_init/config_load)、Shell(shell_command/init)等追踪点已完成插桩并正常工作。

  • 预编译 eBPF 程序:基于 aya-ebpf 框架提供 2 个预编译程序,支持 ELF 解析和 Map 重定位,可通过 trace enable xxx --prog printk 附加程序。

  • 上游贡献:向 Starry-OS/kbpf-basic 提交 PR 并已合并,修复了 printf-compat 和 VaList API 在 Rust nightly >= 2025-12 上的兼容性问题。使 kbpf-basic 可在最新 nightly 工具链(2025-12-12)上正常编译。


二、待完善

问题原因解决方向
vCPU 运行时追踪点不可用vCPU 运行循环位于 axvm crate 而非 kernel 侧等待 axvm 重构后在其内部添加插桩
bpf_trace_printk 简化实现rbpf VM 执行时无法访问 ELF .rodata 段中的格式字符串需要实现内存映射机制
验证器功能有限rbpf 内置验证器仅检查基础安全性当前依赖预编译可信程序规避风险
Kprobe/Kretprobe 未实现下一阶段工作Phase 4 计划

三、遇到的问题与解决方案

3.1 Rust nightly VaList API 变更

问题:kbpf-basic 依赖的 printf-compat 使用了 VaListVaListImpl 类型,在 Rust nightly >= 2025-12 上编译失败,错误信息为 cannot find type VaListImpl in module core::ffi

原因:Rust 重构了 core::ffi 模块,VaListImpl 被移除,VaList 的 API 也有变化。

解决方案

  1. Fork kbpf-basic 仓库
  2. 更新 printf-compat 依赖到兼容新 API 的版本
  3. 修改 VaList 相关代码适配新 API
  4. 提交 PR 到上游并合并

3.2 rbpf 内存边界检查导致 Map 访问失败

问题bpf_map_lookup_elem 返回静态 LOOKUP_BUFFER 指针后,eBPF 程序尝试解引用触发 out of bounds memory load 错误。

原因:rbpf VM 对所有内存访问进行边界检查,仅允许访问 mem(上下文)、stack(栈)和显式注册的 allowed_memory。

解决方案

#![allow(unused)]
fn main() {
// Register LOOKUP_BUFFER as accessible memory before program execution
vm.register_allowed_memory(helpers::get_lookup_buffer_range());
}

3.3 ELF Map 重定位

问题:aya-ebpf 编译的程序包含 maps section 和 .reltracepoint 重定位表,直接加载会因为 Map FD 未修补导致运行时错误。

解决方案:实现完整的 ELF 解析流程:

  1. 解析 maps section 提取 Map 定义(每个 28 字节)
  2. 解析符号表获取 Map 名称
  3. 调用 maps::create() 创建 Map 并获取 FD
  4. 解析 .reltracepoint 重定位表
  5. 修补 ld_imm64 指令中的 Map FD

四、工作量统计

指标数量
新增代码行数~3000 行
主要模块数12 个(symbols、tracepoint、runtime、maps、helpers、attach、context、programs、output、map_ops、platform、tracepoints)
定义追踪点数25+ 个
实现 Helper 数9 个(6 标准 + 3 Hypervisor 专用)
支持 Map 类型4 种
上游 PR1 个(已合并)

五、下阶段计划

优先级任务说明
P0完善文档补充 API 文档、使用示例、架构说明
P1Kprobe 支持移植 Starry-OS kprobe 库,实现动态探针
P1axvm 追踪点插桩待 axvm 重构后添加 vCPU 运行时追踪点
P2完善 bpf_trace_printk实现 .rodata 内存映射,支持格式字符串解析
P2性能测试测量追踪点开销、eBPF 执行延迟

六、风险与阻塞项

风险项影响缓解措施
axvm 重构时间不确定vCPU 运行时追踪点无法上线优先完成其他可用追踪点的功能验证
Kprobe 架构适配复杂度aarch64 断点机制与 x86 不同参考 Starry-OS 实现,必要时只支持单架构

七、个人日志

7.1 五个关键依赖仓库的作用

依赖定位在 eBPF 执行流程中的作用
ksym符号表管理提供 KallsymsMapped 解析内核符号表,支持 lookup_symbol(addr) 地址→函数名和 lookup_addr(name) 函数名→地址双向查询。用于堆栈追踪符号化和 Kprobe 目标函数定位。
tracepoint (ktracepoint)静态追踪点框架提供 define_event_trace! 宏定义追踪点,TracepointManager 管理追踪点生命周期。当追踪点触发时,调用已注册的回调函数(包括执行附加的 eBPF 程序)。
tp-lexer过滤表达式解析解析追踪点过滤表达式语法,支持条件过滤(如 vm_id == 1)。当前为 tracepoint-support feature 的依赖,后续可用于实现追踪点过滤功能。
rbpfeBPF 虚拟机核心执行引擎。runtime.rs 使用 EbpfVmRaw 执行带上下文的 eBPF 字节码,通过 register_helper 注册 helper 函数,register_allowed_memory 注册可访问内存区域(如 LOOKUP_BUFFER)。程序执行后返回 r0 寄存器值。
kbpf-basiceBPF Map 实现提供 bpf_map_create 创建 Map,UnifiedMap 统一抽象层。maps.rs 通过实现 KernelAuxiliaryOps trait 适配 AxVisor 内核操作(cpu_id、time_ns、write_str 等),支持 Array/HashMap/LruHash/Queue 四种 Map 类型。

执行流程示意

User Shell                     axebpf                      Dependencies
    │                             │                           │
    ├─ trace enable ─────────────►│                           │
    │                             ├─ TracepointManager ──────►│ tracepoint
    │                             │   .enable(name)           │
    ├─ trace enable --prog ──────►│                           │
    │                             ├─ parse_elf_with_maps() ──►│ (ELF parsing)
    │                             ├─ bpf_map_create() ───────►│ kbpf-basic
    │                             ├─ EbpfVmRaw::new() ───────►│ rbpf
    │                             ├─ register_helper() ──────►│ rbpf
    │                             │                           │
[Tracepoint fires]                │                           │
    │                             ├─ execute_with_context() ─►│ rbpf
    │                             │   (eBPF execution)        │
    │                             ├─ map_lookup/update ──────►│ kbpf-basic
    │                             │                           │
    ├─ trace stat ───────────────►│                           │
    │                             ├─ iter_entries() ─────────►│ kbpf-basic
    │                             ├─ lookup_symbol() ────────►│ ksym (optional)

7.2 实现代码过程的权衡

决策点选项最终选择权衡理由
eBPF VM 实现自己实现 vs 复用 rbpfrbpfrbpf 是成熟的 Rust 实现,支持 no_std,指令集完整,减少开发时间。代价是依赖外部仓库。
Map 实现简化 Vec 实现 vs kbpf-basickbpf-basic早期用 Vec 线性扫描(O(n)),后迁移到 kbpf-basic 获得真正的 O(1) HashMap 和 LRU 淘汰。代价是需要实现 KernelAuxiliaryOps 适配层。
KernelAuxiliaryOps 适配完整实现 vs 最小实现最小实现(5/15 方法)只实现 get_unified_map_from_fdcurrent_cpu_idebpf_time_nsebpf_write_str 等必需方法,其他返回 NotSupported。代价是不支持 RingBuf、PerCpu Map。
bpf_map_lookup_elem 返回值零拷贝指针 vs 静态缓冲区静态缓冲区(256B)rbpf 内存边界检查严格,需要 register_allowed_memory 注册返回指针。零拷贝需要 fork kbpf-basic 添加 lookup_elem_ptr API。当前用静态缓冲区 + Mutex 保护,有大小限制和拷贝开销。
bpf_trace_printk 实现完整格式字符串解析 vs 简化打印简化打印格式字符串在 ELF .rodata 段,rbpf VM 执行时无法访问。完整实现需要内存映射机制(复杂度高)。当前直接打印 r1/r2/r3 参数值。
程序验证完整验证器 vs 依赖可信程序依赖可信预编译程序rbpf 内置基础验证器仅检查内存越界和指令合法性。完整验证器(如 PREVAIL)集成复杂度高。当前通过只允许加载预编译可信程序规避风险。
追踪点插桩位置kernel 侧 vs axvm 侧混合vCPU 运行循环在 axvm crate 中,kernel 侧的 vcpus.rs 代码路径未执行。等待 axvm 重构后迁移插桩点。

7.3 收获

技术收获

  1. eBPF 执行模型理解:深入理解了 eBPF 程序的加载→验证→执行→Map 交互流程,以及 helper 函数的调用约定(r1-r5 传参,r0 返回)。

  2. ELF 解析实践:实现了最小化的 ELF64 解析器,支持 section 查找、符号表解析、重定位表处理。理解了 ld_imm64 指令的 Map FD 修补机制。

  3. no_std 环境适配:在无标准库环境下集成多个依赖,处理了 alloc、spin lock、static-keys 等基础设施问题。

  4. Rust nightly 兼容性:修复 kbpf-basic 的 VaList API 变更问题并贡献上游,理解了 Rust 不稳定特性的演进。

工程收获

  1. 模块化设计:axebpf 通过 feature flags 控制功能启用(symbols/tracepoint-support/runtime),便于裁剪和测试。

  2. 抽象层设计platform.rs 提供 mock/real 模式切换,map_ops.rs 实现 KernelAuxiliaryOps 适配层,解耦了依赖库与内核实现。

  3. 渐进式实现:先用简化方案验证端到端流程(如 Vec 实现 Map、简化 printk),再逐步替换为生产级实现。

待深入方向

  • Kprobe 动态插桩机制(需要理解 aarch64 断点指令和异常处理)
  • eBPF 验证器原理(控制流图分析、类型状态追踪)
  • 零拷贝 Map 访问优化