GDB touturial: a walkthrough with examples
本文参考自umd。可以配合online manual学习。
gdb 是个啥?
- “GNU Debugger”
- 众多语言的 debugger,包括 C 和 C++
- 能展示程序运行时,在某个特定的地方程序干了什么
- 使用 gdb 更容易发现诸如 segmentation faults 这样的错误
编译
通常我们这样编译程序:
gcc [flags] <source files> -o <output file>
例如:
gcc -Wall -Werror -ansi -pedantic-errors prog1.c -o prog1.x
现在,根据 gdb 需要,为了启动内置 debug 支持,我们要加上 -g
选项:
gcc [other_flags] -g <source_files> -o <output_file>
例如:
gcc -Wall -Werror -ansi -pedantic-errors -g prog1.c -o prog1.x
启动
输入 gdb
或者 gdb prog1.x
,就能得到如下命令提示符
(gdb)
如果你刚刚没有指定 gdb 哪个文件,那就需要现在来加载:
(gdb) file prog1.x
这里,file
是加载文件的命令,prog1.x 是你要加载的文件名。
gdb 和 Linux shell 有着相似的操作,比如方向键查看历史命令、TAB 键自动补全等
如果对命令不太清楚,可以使用
help
指令:(gdb) help [command]
运行
要想运行程序,只需要使用
(gdb) run
在这里,如果程序出现错误,你就会看到错误提示。这也正是希望使用 gdb 解决的事情。
断点
断点被用来让程序在运行中的某个地方停下来。指令是 break
。比如:
(gdb) break file1.c:6
在 file1.c 的第 6 行设置了一个断点。如果程序运行到了这行,程序就会自动暂停,并且等待你输入其他命令。
我们可以设置任意多个断点。
我们也可以告让程序在某个函数的地方暂停。比如我们有一个函数
int my_func(int a, char *b)
那我们就可以按如下方式设置断点:
(gdb) break my_func
设置完断点后,我们再次执行 run
命令,程序就会开始运行,并在断点处暂停。暂停后,我们使用 continue
命令,让程序继续运行:
(gdb) continue
当然,我们还可以使用 step
让程序仅仅再执行下一行,然后暂停:
(gdb) step
相似的还有 next
,不过如果它遇到函数调用,只会把它当中一个整体执行,而不会进入到函数里面:
(gdb) next
有时候我们需要输入好多
step
和next
,我们只需要按下回车,就可以重复上一条命令。
查询
我们设置断点的目的是让程序暂停,查看当前的状态。那么如何查看状态呢?
我们使用 print
来输出变量的值:
(gdb) print my_var
如果想要 16 进制,则可以用 print/x
:
(gdb) print/x my_var
我们有时需要关注一个变量,并在它发生变化时暂停程序,查询它的值。这可以用 watch
实现:
(gdb) watch my_var
在上面的例子中,只要 var 改变了,查询就会暂停,输出 var 的旧值和新值。
如果程序中有多个同名变量,那么输出哪个取决于当前程序运行到了哪儿。
其他命令
- backtrace - produces a stack trace of the function calls that lead to a seg fault (should remind you of Java exceptions)
- where - same as backtrace; you can think of this version as working even when you’re still in the middle of the program
- finish - runs until the current function is finished
- delete - deletes a specified breakpoint
- info breakpoints - shows information about all declared breakpoints
条件断点
有时候设置了断点,它每次都停下,但我们只想看其中的少数情况,这时可以使用条件断点:
(gdb) break file1.c:6 if i >= ARRAYSIZE
该命令在 file1.c 的第 6 行设置了一个断点,且只有当 i 大于 ARRAYSIZE 的时候才会触发。
指针
比如我们有如下结构体
struct entry {
int key;
char *name;
float price;
long serial_number;
};
假设现在程序已经执行完了语句
struct entry * e1 = <something>;
这时可以使用下面的语句查看指针位置:
(gdb) print e1
使用下面的指令查看结构体中具体的内容:
(gdb) print e1->key
(gdb) print e1->name
(gdb) print e1->price
(gdb) print e1->serial number
上面的命令和下面的命令时等价的:
(gdb) print (*e1).key
(gdb) print (*e1).name
(gdb) print (*e1).price
(gdb) print (*e1).serial number
如果我们要查看结构体中所有的内容,可以使用命令:
(gdb) print *e1
正如 C 语言中的指针,这种 reference 可以串起来:
(gdb) print list prt->next->next->next->data
Comments