程序一跑就崩,Gdb 官方怎么把调用栈看出来?

程序一启动就崩掉,需要把调用栈打出来,看卡在哪一层。不必把整套调试平台都铺开,只把启动、运行和查看调用栈这一段跑通。

GDB 文档入口 指向现行在线手册 Debugging with GDB。手册页头写的是第十版,版本串是 19.0.50.20261115-git。下面按 Invoking GDB、Running 一章里的 Compilation、Starting,以及 Backtrace 来看。

Running 一章开篇就写:要在 GDB 下运行程序,编译时必须先生成调试信息。Compilation 一节说,这些信息写在目标文件里,描述变量和函数的类型,以及源码行号和可执行代码地址的对应关系。向编译器请求这些信息,要加上 -g。文档建议,用 GCC 时不管有没有加 -O,都带上 -g。旧的 -gg 格式 GDB 已经不再支持,不要用。

启动调试器,Invoking 一节写的是运行 gdb。最常见的用法是带一个参数,指出可执行文件:gdb program。也可以同时给出可执行文件和核心转储:gdb program core。若要附到已经在跑的进程,第二个参数可以是进程号,或使用 -p。程序若要带自己的命令行参数,可以用 gdb --args,后面的内容会作为被调试程序的参数。不确定有哪些选项时,执行 gdb -help 或 gdb -h。

程序名字指定之后,用 Starting 一节里的 run(简写 r)来运行它。文档说,必须先用启动参数,或 file / exec-file 命令指定程序名。在支持进程的环境里,run 会创建一个下级进程并让它执行该程序。参数写在 run 后面。若目标上有 shell,会经 shell 传递,因此可以使用通配和变量替换。run 一经发出,程序会立刻开始执行。它停下来之后,就可以查看当时的状态。若可执行文件比 GDB 上次读符号时更新过,GDB 会丢掉旧的符号表再读一遍,并尽量保留已有的断点。

程序停在某一处之后,Backtrace 一节把 backtrace 写成一份摘要,说明程序是怎么走到当前位置的:每一帧一行,从当前正在执行的帧(0 号)开始,然后是它的调用者(1 号),再沿栈往上。打印整份调用栈,用 backtrace 或别名 bt。默认会打印全部栈帧,可用系统中断键(通常是 Ctrl-c)中途停下。bt 3 这类用法里的正数表示只看最内侧的若干帧,负数则表示最外侧的若干帧。加上 -full 会连带打印局部变量。where 和 info stack(可简写 info s)也是同一条命令的别名。多线程程序里,默认只显示当前线程的调用栈。若要看全部线程,文档示例是 thread apply all backtrace,并写明在分析这类程序的核心转储时会很有用。

每一行会给出帧号和函数名,通常还有程序计数器、源文件、行号以及函数参数。手册里的例子用 bt 3 打出最内侧三帧,0 号帧若停在该行代码的起始位置,行首就不会再显示程序计数器。若程序按优化选项编译,某些参数可能显示为 <optimized out>,文档的建议是:要么从其他相关变量反推,要么去掉优化后重新编译。对于 C 程序,GDB 找到用户入口 main 后默认会停止往上追,以免走进系统启动代码。若需要越过这个位置,再用 set backtrace past-main。也可以用 set backtrace limit 限制打印的层数,0 或 unlimited 表示不限制。若调用栈里看不到源文件和行号,先回到 Compilation 一节,确认编译时确实加了 -g。入门把这一步、gdb program、run 和 bt 跑通即可,不必一次把远程目标、断点体系和界面扩展都铺开。

你们这次是程序当场崩掉、可以直接在 run 之后打 bt,还是手头已经有一份核心转储要先用 gdb program core 打开?