Whistler: Live eBPF Programming from the Common Lisp REPL

⭐⭐⭐⭐⭐ · 2026-03-23 · eBPF Common Lisp Compiler

Whistler是一个用Common Lisp编写的eBPF DSL和优化编译器,允许开发者直接在REPL中编写、编译和加载eBPF程序。

核心亮点

  • 直接在REPL中开发:修改probe后重新eval即可立即看到结果,反馈循环是即时的
  • 完整的优化编译器:生成高度优化的eBPF字节码,效果等同于或优于clang编译的C代码
  • 无需工具链:直接生成ELF eBPF文件,不需要clang+llvm工具链
  • 零C依赖的loader:whistler/loader是完全用Common Lisp编写的BPF用户空间加载器
  • 跨语言支持:可以生成匹配的C、Go、Rust、Python结构体定义

技术细节

Whistler编译器在宏展开时运行。当SBCL编译with-bpf-session表单时,eBPF字节码已经是一个常量——作为字面字节数组嵌入在展开式中。运行时代码只是创建maps、patch FD重定位,并调用bpf(BPF_PROG_LOAD, ...)。

关键创新:whistler:defstruct为BPF和CL两边生成访问器。一次定义同时服务于内核和用户空间,无需手动解析字节偏移。

示例代码

(with-bpf-session ()
  (bpf:map counter :type :hash :key-size 4 :value-size 8 :max-entries 1)
  (bpf:prog trace (:type :kprobe
    :section "kprobe/__x64_sys_execve"
    :license "GPL")
    (incf (getmap counter 0))
    0)
  (bpf:attach trace "__x64_sys_execve")
  (loop (sleep 1)
    (format t "execve count: ~d~%" (bpf:map-ref counter 0))))

为什么这很重要

传统的eBPF工作流程是:为BPF端写C,用clang编译,然后为用户空间端写Go/Rust/Python。两门语言、单独的构建步骤、多个进程。

使用Whistler 1.0,工作流程是:写Lisp。编译器、加载器和用户空间应用程序共享一个进程。

原文链接