README-zh.md

July 10, 2026 · View on GitHub

GhostScope Logo

GhostScope

⚡ 下一代 eBPF 用户态运行时追踪器

Printf 调试的进化 — 无需 stop-the-world 检查即可实时追踪。

版本 协议 Linux 4.4+ Rust 1.88.0


概述

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;
    }
}

演示效果:

Prompt 1 CLI 演示

提问 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 的源码感知栈帧。

演示效果:

Prompt 2 CLI 演示

为了得到最好效果,请确保相关源码树可用、你关心的模块带有 DWARF 调试信息,并且 GhostScope 具备加载 eBPF 程序所需的权限。如果这些信息无法在本地可靠发现,skill 应该先追问,再生成带源码依据的追踪结果。拉取仓库更新后,重新执行同一个安装脚本即可;安装的 skill 自带版本号,版本变化时会自动刷新。

理想中的 Printf

GhostScope 把编译后的二进制重新变成“可观测系统”。在 TUI 里,这个过程是递进展开的:先定位到感兴趣的函数或源码行,看清楚当前可见的变量,再从那个位置进入 Script Mode 设点,最后一边让目标进程继续运行,一边在输出面板里看变量值和调用栈。它不是一个泛泛的监控面板,而是一个带执行上下文的源码导向运行时 printf 调试界面。

下面这个演示就是按这条路径展开的:先在带 DWARF 的 nginx worker 里找到目标代码,再在对应行写一小段脚本,最后立刻看到条件判断、按源码语义访问变量、调用栈上下文,以及运行中进程的实时输出。


GhostScope Demo

实时追踪运行中的 nginx worker 进程

工作原理

想象一下,你面对的是一片广袤的二进制数据荒野 —— 内存地址、寄存器值、栈帧数据 —— 没有上下文,它们只是毫无意义的数字。DWARF 调试信息就是我们的地图:它告诉我们栈地址 RSP-0x18 存储着局部变量 count,堆地址 0x5621a8c0 处的结构体是 user 对象,其偏移 +0x20 处是字符串指针 user.name;它追踪每个变量在程序执行过程中的位置变化 —— 参数 x 现在在寄存器 RDI 中,之后会被移到栈上 RSP-0x10 的位置。

有了这张地图,GhostScope 利用 eBPF 和 uprobe 技术从运行中程序的指令点安全地提取二进制数据。DWARF 揭示进程虚拟地址空间中数据的含义,eBPF 获取请求的数据,从而在不由调试器控制暂停、也不主动修改应用状态的前提下,输出源码感知的变量值和调用栈。每次 uprobe 命中仍会同步执行 trap 和 eBPF 路径,因此追踪并非对时序完全透明。

✨ 核心特性

Performance
按需开销
每次命中执行 uprobe + 有界 eBPF 工作
Real-time
实时追踪
实时跟踪流
DWARF
DWARF 感知
基于 PC 的源码语义
Rust
Rust 构建
内存安全且极速

⚠️ 可信性与开发状态

GhostScope 目前处于早期开发阶段,仍在积极迭代中。在支持范围内,已知的恢复或归因失败应当显式呈现,不能被展示成看似合理的值。

实现缺陷仍可能违反这份契约。建议将 GhostScope 采集的数据作为问题排查的辅助参考;在做出关键决策前,请结合其他调试工具交叉验证。

规范性的保证、前提和失败语义见设计保证与可信性模型

不支持和会显式降级的场景见使用限制

📚 文档

🎯 入门指南

⚙️ 配置

👨‍💻 开发

🤝 贡献

我们欢迎贡献!无论是错误报告、功能请求、文档改进还是代码贡献,我们都感谢您帮助改进 GhostScope。

请查看我们的贡献指南了解:

  • 行为准则
  • 开发工作流
  • 编码标准
  • 如何提交拉取请求

📜 许可证

GhostScope 采用 GNU 通用公共许可证 授权。

🙏 致谢

使用以下优秀的开源项目构建:

  • Aya - Rust 的 eBPF 库(使用其 loader 功能)
  • LLVM - 编译器基础设施
  • Inkwell - Rust 的安全 LLVM 绑定
  • Gimli - DWARF 解析器
  • Ratatui - 终端 UI 框架
  • Tokio - 异步运行时
  • Pest - PEG 解析器生成器

受到以下项目的启发和借鉴:

  • GDB - DWARF 解析优化
  • bpftrace - eBPF 追踪技术
  • cgdb - TUI 设计和用户体验

特别感谢以下优秀资源,从中学到了很多:

博客文章:

技术书籍: