10 分钟学会使用 GhostScope 追踪运行中的应用程序!
GhostScope 提供两种模式来附加到进程,两者都使用 Linux uprobe + eBPF 机制:
# 通过 PID 追踪特定的运行进程
sudo ghostscope -p $(pidof your_app)- 使用场景:您有一个运行中的进程,只想追踪该特定实例
- 优势:专注追踪,减少来自其他进程的噪音
- 限制:无法捕获早期启动事件
# 追踪所有使用此二进制文件的进程
sudo ghostscope -t /path/to/binary
# 也适用于共享库
sudo ghostscope -t /usr/lib/libexample.so- 使用场景:您想捕获进程启动事件或追踪多个实例
- 优势:可以捕获进程初始化的事件,非常适合调试启动问题
- 注意:将追踪所有使用此二进制文件/库的进程,可能会产生更多事件
⚠️ 注意事项:
-p选项会追踪该进程对应的主程序以及目前已经加载的所有动态库(后续通过 dlopen 加载的动态库暂不支持)- 您想追踪的可执行文件和动态库必须包含调试信息,否则 GhostScope 将无计可施。如何判断调试符号是否存在?请参考安装指南的调试符号章节
💡 技术背景:其实我也不想说那么多,但不说不行 😂。理解 uprobe 机制对正确使用 GhostScope 至关重要,推荐阅读 Uprobe 内部机制 文档,避免踩坑。
成功执行启动命令后,您会看到一个加载界面 — GhostScope 正在加载调试信息并建立查询索引。这可能需要一些时间(比如加载 nginx 大约需要 3 秒)。别问我为什么 GDB 加载那么快,我还在努力向 GDB 学习优化技巧,争取后面版本能更快一些。
如果一切顺利,只需一眨眼的时间(可能会因此错过精心设计的加载界面),我们就会看到 GhostScope 的 TUI 界面:
简单介绍一下 GhostScope 的 TUI 面板构成:
- 显示内容:应用程序的源代码(默认展示 main 函数所在的代码文件)
- 用途:浏览代码并设置追踪点
- 显示内容:实时追踪输出
- 用途:查看实时发生的执行追踪
- 显示内容:命令输入行
- 用途:输入追踪命令和控制会话
这里展示两种最主要的使用方式,也就是 README 中 demo 所演示的内容。
焦点默认在命令交互面板,该面板有三个模式:
在输入模式下,您可以执行各种命令。例如:
trace {target} # target 可以是函数或源码行号
详细命令语法请参考命令参考。
按回车后进入脚本模式,开始编写 GhostScope 脚本来探测进程:
- 使用
print打印局部变量、参数甚至全局变量 - 只要 DWARF 信息包含变量描述,就能获取有意义的数据
- 支持定义脚本变量和简单的判断逻辑
- 更多脚本语法细节请参考脚本语言参考
编写完成后,按 Ctrl+S 提交代码。如果一切顺利,脚本会被编译成 eBPF 字节码并加载到 uprobe 上。
这个时候,如果一切顺利,我们将在 eBPF 输出面板上看到脚本对应的输出。但要查看输出,我们需要把焦点切换到 eBPF 输出面板。
按 Esc 从输入模式切换到命令模式,在这个模式下:
- Vim 风格导航:使用
hjkl浏览历史消息(灵感来自 cgdb) - 回到输入模式:按
i键 - 面板切换:
Tab/Shift+Tab:在面板间切换Ctrl+W+hjkl:Vim 风格的面板跳转(Vim 爱好者的福音 😁)
当焦点在 eBPF 输出面板时,同样支持 Vim 风格的导航和快速移动。
如果面板太小,除了启动时设置比例,还可以:
- 按
Ctrl+W z:将当前焦点面板全屏(这招从 tmux 学来的,也是我的最爱 😉)
更高效的使用方式是从源代码面板开始:
- 浏览源码:把焦点切换到源码面板,使用 Vim 风格导航浏览代码
- 快速设置追踪点:当看到感兴趣的代码行时,按空格键直接进入脚本模式
- trace 的 target 会自动设置为光标所在的文件和行号
- 这个设计灵感来自 cgdb,我非常喜欢这种快捷方式
- 切换文件:按
o键唤出文件搜索栏,快速查找并切换到其他源文件
这样的工作流程更加流畅,让追踪点的设置变得轻而易举。
为了重复使用,将您的追踪保存在文件中:
# trace.gs
trace calculate_something {
print "FUNC: a={} b={}", a, b;
}
trace sample_program.c:16 {
print "LINE16: result={}", result;
}
运行它:
sudo ghostscope -p $(pidof your_app) --script-file trace.gs


