README-zh.md
July 10, 2026 · View on GitHub
GhostScope
⚡ 下一代 eBPF 用户态运行时追踪器
Printf 调试的进化 — 无需 stop-the-world 检查即可实时追踪。
概述
GhostScope 是一个 面向源码语义的用户态追踪器。有 DWARF 调试信息时,它可以在不由调试器控制目标暂停的前提下,按函数、源码行或指令粒度设点,打印真正重要的信息,并输出源码感知的调用栈。
支持范围以及可信输出所依赖的不变量,统一定义在设计保证与可信性模型中。
"The most effective debugging tool is still careful thought, coupled with judiciously placed print statements." — Brian Kernighan
什么时候适合用 GhostScope
- 你在排查线上正在运行的服务,不能接受 GDB 式 stop-the-world 带来的巨大性能扰动,同时又更希望使用基于 eBPF 的工作流,获得比传统内核模块式探测更好的安全边界和更低开销。
- 你需要在源码行级探针里读取真实的局部变量、参数、全局变量和复杂数据,而不只是函数入口参数。
- 你想在同一个 live probe 里既打印关键状态,又输出基于 DWARF unwind 的调用栈 (backtrace)。
- 你需要解释一次请求、函数、源码行或代码路径是如何被执行到的,同时又不能做 stop-the-world 检查。
- 你想把“这里要是能加一条 printf 就好了”快速变成一个可重复运行的 CLI 追踪脚本。
什么时候不适合用 GhostScope
- 如果你需要带断点、单步、改内存或调 coredump 的交互式调试体验,用 GDB。
- 如果你想在 perf 生态里快速对某个函数、源码行或局部变量打一针式 probe,用
perf probe。 - 如果你要在同一套脚本里混合大量内核 + 用户态事件做聚合,优先考虑 bpftrace 或 SystemTap。
- 如果目标模块没有 DWARF 调试信息,就不要期待源码级变量追踪能很好地工作。
GhostScope 和 perf、GDB、bpftrace、SystemTap 的区别
完整、统一维护的对比请看 工具对比。另外也可以参考 常见问题。
AI 运行时分析 Skill
GhostScope 支持两种模式:一种是交互式 TUI 模式,另一种是基于 --script 和 --script-file 的 CLI 模式。后者是更适合 AI 和自动化的工作流。
给 Codex 或 Claude Code 安装共享 skill:
curl -fsSL https://raw.githubusercontent.com/swananan/ghostscope/main/scripts/skills/install_ghostscope_runtime_analysis_skill.sh | bash -s -- --copy
如果你已经把仓库 clone 到本地,也可以继续使用 ./scripts/skills/install_ghostscope_runtime_analysis_skill.sh --copy。
如果你想强制指定目标,可以追加 --codex、--claude 或 --all。安装后需要重启 Codex 或 Claude Code。
当 AI 和你共享同一个 workspace 时,它通常可以自己发现源码 checkout 路径以及 DWARF/调试符号状态。只有在这些信息无法可靠判断时,它才应该先追问,再生成带源码依据的结果。两个常用 prompt 模式是:先让它展示你关心的变量值,再让它展示解释执行路径的调用栈。
这个例子里,AI 可以在本地自行发现的上下文:
- 源码 checkout:
/mnt/500g/code/openresty/openresty-1.27.1.1/build/nginx-1.27.1 - 目标二进制:
/usr/local/openresty/nginx/sbin/nginx - 调试信息状态:二进制内带有 DWARF 调试信息
提问 1:查看运行时变量
$ghostscope-runtime-analysis 跟踪正在运行的 nginx worker,并把请求 body 的原始字节打印出来
生成的命令:
WORKER_PID=$(pgrep -n -f 'nginx: worker process')
sudo ghostscope -p "$WORKER_PID" --script-file /tmp/ghostscope-nginx-body-discard.gs --script-output plain
生成的脚本:
trace /mnt/500g/code/openresty/openresty-1.27.1.1/build/nginx-1.27.1/src/http/ngx_http_request_body.c:671 {
if size > 0 {
let req_line_len = r.request_line.len;
let body_len = size;
print "src=discard-preread pid={} req={:p} line={:s.req_line_len$} body_len={} body={:x.body_len$}",
$pid, r, r.request_line.data, body_len, r.header_in.pos;
}
}
演示效果:

提问 2:补上调用上下文
$ghostscope-runtime-analysis 跟踪正在运行的 nginx worker,展示哪个请求到达了 ngx_http_process_request,并输出源码感知的调用栈
生成的命令:
WORKER_PID=$(pgrep -n -f 'nginx: worker process')
sudo ghostscope -p "$WORKER_PID" --script-file /tmp/ghostscope-nginx-request-stack.gs --script-output pretty
生成的脚本:
trace ngx_http_process_request {
let method_len = r.method_name.len;
let uri_len = r.uri.len;
let line_len = r.request_line.len;
print "nginx pid={} method={:s.method_len$} uri={:s.uri_len$} line={:s.line_len$}",
$pid, r.method_name.data, r.uri.data, r.request_line.data;
bt full;
}
这里的 print 回答“哪个请求状态到达了这个探针?”,bt full 回答“nginx 是沿着哪条调用路径到达这里的?”,并输出基于 DWARF unwind 的源码感知栈帧。
演示效果:

为了得到最好效果,请确保相关源码树可用、你关心的模块带有 DWARF 调试信息,并且 GhostScope 具备加载 eBPF 程序所需的权限。如果这些信息无法在本地可靠发现,skill 应该先追问,再生成带源码依据的追踪结果。拉取仓库更新后,重新执行同一个安装脚本即可;安装的 skill 自带版本号,版本变化时会自动刷新。
理想中的 Printf
GhostScope 把编译后的二进制重新变成“可观测系统”。在 TUI 里,这个过程是递进展开的:先定位到感兴趣的函数或源码行,看清楚当前可见的变量,再从那个位置进入 Script Mode 设点,最后一边让目标进程继续运行,一边在输出面板里看变量值和调用栈。它不是一个泛泛的监控面板,而是一个带执行上下文的源码导向运行时 printf 调试界面。
下面这个演示就是按这条路径展开的:先在带 DWARF 的 nginx worker 里找到目标代码,再在对应行写一小段脚本,最后立刻看到条件判断、按源码语义访问变量、调用栈上下文,以及运行中进程的实时输出。
实时追踪运行中的 nginx worker 进程
工作原理
想象一下,你面对的是一片广袤的二进制数据荒野 —— 内存地址、寄存器值、栈帧数据 —— 没有上下文,它们只是毫无意义的数字。DWARF 调试信息就是我们的地图:它告诉我们栈地址 RSP-0x18 存储着局部变量 count,堆地址 0x5621a8c0 处的结构体是 user 对象,其偏移 +0x20 处是字符串指针 user.name;它追踪每个变量在程序执行过程中的位置变化 —— 参数 x 现在在寄存器 RDI 中,之后会被移到栈上 RSP-0x10 的位置。
有了这张地图,GhostScope 利用 eBPF 和 uprobe 技术从运行中程序的指令点安全地提取二进制数据。DWARF 揭示进程虚拟地址空间中数据的含义,eBPF 获取请求的数据,从而在不由调试器控制暂停、也不主动修改应用状态的前提下,输出源码感知的变量值和调用栈。每次 uprobe 命中仍会同步执行 trap 和 eBPF 路径,因此追踪并非对时序完全透明。
✨ 核心特性
|
按需开销 每次命中执行 uprobe + 有界 eBPF 工作 |
实时追踪 实时跟踪流 |
DWARF 感知 基于 PC 的源码语义 |
Rust 构建 内存安全且极速 |
⚠️ 可信性与开发状态
GhostScope 目前处于早期开发阶段,仍在积极迭代中。在支持范围内,已知的恢复或归因失败应当显式呈现,不能被展示成看似合理的值。
实现缺陷仍可能违反这份契约。建议将 GhostScope 采集的数据作为问题排查的辅助参考;在做出关键决策前,请结合其他调试工具交叉验证。
规范性的保证、前提和失败语义见设计保证与可信性模型。
不支持和会显式降级的场景见使用限制。
📚 文档
🎯 入门指南 |
⚙️ 配置 |
👨💻 开发
|
🤝 贡献
我们欢迎贡献!无论是错误报告、功能请求、文档改进还是代码贡献,我们都感谢您帮助改进 GhostScope。
请查看我们的贡献指南了解:
- 行为准则
- 开发工作流
- 编码标准
- 如何提交拉取请求
📜 许可证
GhostScope 采用 GNU 通用公共许可证 授权。
🙏 致谢
使用以下优秀的开源项目构建:
- Aya - Rust 的 eBPF 库(使用其 loader 功能)
- LLVM - 编译器基础设施
- Inkwell - Rust 的安全 LLVM 绑定
- Gimli - DWARF 解析器
- Ratatui - 终端 UI 框架
- Tokio - 异步运行时
- Pest - PEG 解析器生成器
受到以下项目的启发和借鉴:
特别感谢以下优秀资源,从中学到了很多:
博客文章:
技术书籍: