一个基于 eBPF 的现代系统调用追踪器。想想 strace,但带有真正的 TUI、智能过滤器、TLS 解密,以及真正可读的输出。
一个基于 eBPF 构建的 Linux 系统调用跟踪器。类似于 strace,但拥有实时 TUI、智能过滤器以及真正可读的参数解码。

$ sudo snoop curl https://example.com
[ 0.001] curl(1234/1234) openat(AT_FDCWD, "/etc/ssl/certs/ca-certificates.crt", O_RDONLY) = 4 <0.031ms>
[ 0.002] curl(1234/1234) read(4, 0x7f3a1c000b20, 4096) = 4096 <0.012ms>
[ 0.003] curl(1234/1234) socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 5 <0.008ms>
[ 0.004] curl(1234/1234) connect(5, 93.184.216.34:443) = 0 <42.187ms>
[ 0.046] curl(1234/1234) sendto(5, 0x55a3bc001b40, 78, MSG_NOSIGNAL) = 78 <0.011ms>
或者不使用 --raw 运行,即可获得全屏 TUI:
snoop pid:1234 comm:curl events:142 elapsed:0.341s
┌── syscall stream ─────────────────────────────────────┐┌── top syscalls ─────────┐
│ [ 0.001] curl openat("/etc/ssl/…") = 4 <0.031ms> ││ syscall count pct│
│ [ 0.002] curl read(4, …) = 4096 ││ read 38 26.8%│
│ [ 0.003] curl socket(AF_INET, …) = 5 ││ write 21 14.8%│
│ [ 0.004] curl connect(5, 93.184.216.34:443) = 0 ││ openat 18 12.7%│
│ [ 0.046] curl sendto(5, …) = 78 ││ mmap 14 9.9%│
└───────────────────────────────────────────────────────┘└─────────────────────────┘
[q]uit [Space]pause [/]search [f]iles [n]et [c]lear [↑↓]scroll [G]bottom
strace 使用 ptrace,每次系统调用都会停止你的进程。而 snoop 使用 eBPF 跟踪点——你的进程可以全速运行,跟踪在内核中进行。除了性能优势,snoop 还能将参数解码成真正可读的形式,拥有实时 TUI,并且可以保存/重放/比较跟踪结果。
--raw(与 strace 兼容)、--json(JSON Lines 格式,便于 jq 处理)、--explain(将系统调用分组为高层级活动摘要)--files、--net、--slow 10、--syscall openat-p PID)或 生成 进程(snoop <cmd>)--follow 也跟踪 fork 的子进程--docker <name> 和 --pod <name> 跟踪容器内的所有进程--tls 通过 uprobes 挂钩 / 以显示明文无需内核模块,无需 C 工具链。eBPF 程序完全用 Rust(aya)编写,并内嵌在二进制文件中。
CAP_BPF + CAP_PERFMONcurl -L https://github.com/pandaadir05/snoop/releases/latest/download/snoop-x86_64-linux.tar.gz \
| tar -xz
sudo install -m755 snoop /usr/local/bin/snoop
对于 aarch64,将 x86_64 替换为 aarch64。
cargo install --git https://github.com/pandaadir05/snoop snoop
这会自动编译 eBPF 程序。无需 C 工具链或内核头文件,只需要 Rust。
# 跟踪一个命令
sudo snoop ls /etc
sudo snoop -- nginx -g 'daemon off;'
# 附加到正在运行的进程
sudo snoop -p $(pidof postgres)
sudo snoop -p 1234 --follow # 也跟踪 fork 的子进程
# 容器
sudo snoop --docker my-nginx
sudo snoop --pod my-app-pod --namespace production
# 过滤
sudo snoop -p 1234 --files # 仅文件系统调用
sudo snoop -p 1234 --net # 仅网络调用
sudo snoop -p 1234 --slow 5 # 仅慢于 5ms 的调用
sudo snoop -p 1234 --syscall openat --syscall read
--explain 将原始系统调用分组为高层级摘要,而不是逐条显示每个调用:
sudo snoop -p 1234 --explain
READ /etc/passwd ↓1.2 KB (2 calls, 0.80ms)
NET 127.0.0.1:5432 ↑512 B ↓4.0 KB (18.20ms)
EXEC /usr/bin/python3
挂钩 OpenSSL 中的 SSL_write/SSL_read 以捕获明文:
sudo snoop -p 1234 --tls
sudo snoop -p 1234 --ltrace
记录跟踪,然后稍后无需 root 即可重放:
sudo snoop record -p 1234 -o trace.snoop
snoop view trace.snoop
snoop view trace.snoop --files --slow 5 --json | jq 'select(.name=="read")'
用于发现部署后的回归问题:
sudo snoop record -p 1234 -o before.snoop
# ... 部署变更 ...
sudo snoop record -p 1234 -o after.snoop
snoop diff before.snoop after.snoop
SYSCALL COUNTS
read 1200 → 1800 (+50.0%) ▲
openat 340 → 210 (-38.2%) ▼
DURATION REGRESSIONS (median)
read 0.02ms → 0.08ms (+300%)
ONLY IN after
statx (4x)
sudo snoop -p 1234 --json | jq 'select(.name == "connect")'
sudo snoop -p 1234 --flamegraph syscalls.svg
-p, --pid <PID> 附加到正在运行的进程
--follow 跟踪 fork 的子进程(需要 --pid)
--docker <NAME|ID> 跟踪 Docker 容器中的所有进程
--pod <POD> 跟踪 Kubernetes Pod 中的所有进程
-n, --namespace <NS> Kubernetes 命名空间(默认:default)
--raw 单行 strace 兼容输出
--json JSON Lines —— 每个系统调用一个对象
--explain 语义活动摘要
--files 仅文件系统系统调用
--net 仅网络系统调用
--slow <MILLIS> 仅慢于阈值的调用
--syscall <NAME> 仅此系统调用(可重复)
--no-decode 原始十六进制参数,不进行解码
--tls 通过 SSL_write/SSL_read uprobes 捕获 TLS 明文
--ltrace 跟踪 malloc/free/calloc/realloc
--flamegraph <PATH> 退出时写入火焰图 SVG
--ebpf-obj <PATH> 覆盖内嵌的 eBPF 对象 [$SNOOP_EBPF_OBJ]
两个 eBPF 程序分别挂载到 raw_syscalls/sys_enter 和 sys_exit。在入口处,系统调用号和参数被写入每个线程的临时映射。在出口处,返回值与入口数据配对并推送到环形缓冲区。用户空间通过异步文件描述符读取环形缓冲区,并将所有内容通过过滤器/解码管道进行处理。
内核 用户空间
────── ─────────
raw_syscalls/sys_enter ──► SYSCALL_ENTER 映射(每个 tid 的临时存储)
raw_syscalls/sys_exit ──► EVENTS 环形缓冲区(4 MiB)
│
轮询消费者(tokio::spawn)
│
┌──────────┴──────────┐
RawOutput TuiApp
(每行一个系统调用) (ratatui TUI)
eBPF 程序使用 aya 用 Rust 编写,在构建时编译为 BPF 字节码,并内嵌在二进制文件中。没有运行时依赖,也不需要内核头文件。
git clone https://github.com/pandaadir05/snoop
cd snoop
# Nightly 仅用于 BPF 目标
rustup toolchain install nightly
rustup component add rust-src --toolchain nightly
# bpf-linker 链接 eBPF 对象(无需系统 LLVM)
cargo install bpf-linker --no-default-features
cargo build --release
sudo ./target/release/snoop -p $$
构建脚本会自动编译 eBPF 程序。无需单独步骤。
开发时也可以使用 xtask:
cargo xtask run -- -p $$
MIT 或 Apache-2.0,任选其一。
SSL_writeSSL_read--ltrace 跟踪 malloc/free/calloc/reallocsnoop record 保存到磁盘,snoop view 离线重放(无需 root)snoop diff before.snoop after.snoop 显示两次运行之间的变化--flamegraph out.svg| 按键 | 操作 |
|---|
q | 退出 |
Space | 暂停 / 恢复 |
/ | 增量搜索 |
f | 切换文件系统过滤器 |
n | 切换网络过滤器 |
c | 清除事件列表 |
Enter | 显示所选事件的详细信息弹出窗口 |
↑ / k | 向上滚动 |
↓ / j | 向下滚动 |
G / End | 跳转到最新事件 |
g / Home | 跳转到最早事件 |
| 层 | 依赖库 |
|---|
| eBPF 程序 | aya-ebpf |
| eBPF 加载器 | aya |
| TUI | ratatui + crossterm |
| CLI | clap(derive 模式) |
| 异步运行时 | tokio |
| 火焰图 | inferno |