目录
调试器:核心工作,主要是为了定位问题
所有查看的指令不影响实际的调试指令
1. 背景
程序的发布方式有两种,debug模式和release模式Linux gcc/g++出来的二进制程序,默认是release模式要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项
2. 开始使用
- gdb binFile 退出: ctrl + d 或 quit 调试命令:
- list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
- list/l 函数名:列出某个函数的源代码。
- r或run:运行程序。
- n 或 next:单条执行。
- s或step:进入函数调用
- break(b) 行号:在某一行设置断点
- break 函数名:在某个函数开头设置断点
- info break :查看断点信息。
- finish:执行到当前函数返回,然后挺下来等待命令
- print(p):打印表达式的值,通过表达式可以修改变量的值或者调用函数
- p 变量:打印变量值。
- set var:修改变量的值
- continue(或c):从当前位置开始连续而非单步执行程序
- run(或r):从开始连续而非单步执行程序
- delete breakpoints:删除所有断点
- delete breakpoints n:删除序号为n的断点
- disable breakpoints:禁用断点
- enable breakpoints:启用断点
- info(或i) breakpoints:参看当前设置了哪些断点
- display 变量名:跟踪查看一个变量,每次停下来都显示它的值
- undisplay:取消对先前设置的那些变量的跟踪
- until X行号:跳至X行
- breaktrace(或bt):查看各级函数调用及参数
- info(i) locals:查看当前栈帧局部变量的值
- quit:退出gdb
3. 理解
创建需要调试的代码
[root@VM-12-17-centos lesson8]# mkdir gdb [root@VM-12-17-centos lesson8]# cd gdb [root@VM-12-17-centos gdb]# touch test.c [root@VM-12-17-centos gdb]# ll total 0 -rw-r--r-- 1 root root 0 Jan 17 19:39 test.c [root@VM-12-17-centos gdb]# ls ../ gdb Makefile mytest proc test.c [root@VM-12-17-centos gdb]# cp ../Makefile . [root@VM-12-17-centos gdb]# ll total 4 -rw-r--r-- 1 root root 71 Jan 17 19:40 Makefile -rw-r--r-- 1 root root 0 Jan 17 19:39 test.c [root@VM-12-17-centos gdb]# vim test.c [root@VM-12-17-centos gdb]# cat test.c #include <stdio.h> int addToTop(int top) { printf("enter addToTop\n"); int i=1; int sum=0; for( i=1;i<=top;i++) { sum+=i; } printf("quit addToTop\n"); return sum; } int main() { int top=100; int result=addToTop(top); printf("result:%d\n",result); return 0; } [root@VM-12-17-centos gdb]# cat Makefile mytest:test.c gcc -o mytest test.c -std=c99 .PHONY:clean clean: rm -f mytest [root@VM-12-17-centos gdb]# ll total 8 -rw-r--r-- 1 root root 71 Jan 17 19:40 Makefile -rw-r--r-- 1 root root 285 Jan 17 19:45 test.c
[root@VM-12-17-centos gdb]# vim Makefile
[root@VM-12-17-centos gdb]# make
gcc -o mytest test.c
test.c: In function ‘addToTop’:
test.c:8:3: error: ‘for’ loop initial declarations are only allowed in C99 mode
for(int i=1;i<=top;i++)
^
test.c:8:3: note: use option -std=c99 or -std=gnu99 to compile your code
make: *** [mytest] Error 1
如果出现这种报错,该语法在C99标准下被支持,在Makefile内添加-std=c99
mytest:test.c
gcc -o mytest test.c -std=c99
.PHONY:clean
clean:
rm -f mytest
debug&&release
我们对之前生成的mytest重命名,使我们观察的现象更明显
[root@VM-12-17-centos gdb]# mv mytest mytest-release [root@VM-12-17-centos gdb]# ll total 20 -rw-r--r-- 1 root root 83 Jan 17 20:12 Makefile -rwxr-xr-x 1 root root 8440 Jan 17 19:49 mytest-release -rw-r--r-- 1 root root 292 Jan 17 19:49 test.c [root@VM-12-17-centos gdb]# ./mytest-release enter addToTop quit addToTop result:5050
我们生成debug方式编译的文件
[root@VM-12-17-centos gdb]# make gcc -o mytest test.c -g -std=c99 [root@VM-12-17-centos gdb]# ll total 32 -rw-r--r-- 1 root root 83 Jan 17 20:12 Makefile -rwxr-xr-x 1 root root 9672 Jan 17 20:18 mytest -rwxr-xr-x 1 root root 8440 Jan 17 19:49 mytest-release -rw-r--r-- 1 root root 292 Jan 17 19:49 test.c [root@VM-12-17-centos gdb]# mv mytest mytest-debug [root@VM-12-17-centos gdb]# ll total 32 -rw-r--r-- 1 root root 83 Jan 17 20:12 Makefile -rwxr-xr-x 1 root root 9672 Jan 17 20:18 mytest-debug -rwxr-xr-x 1 root root 8440 Jan 17 19:49 mytest-release -rw-r--r-- 1 root root 292 Jan 17 19:49 test.c [root@VM-12-17-centos gdb]# ./mytest-debug enter addToTop quit addToTop result:5050
release、debug都能正常运行
debug版本内有很多的调试信息,其体积比release版本大
[root@VM-12-17-centos gdb]# readelf -S mytest-debug | grep -i debug [27] .debug_aranges PROGBITS 0000000000000000 00001069 [28] .debug_info PROGBITS 0000000000000000 00001099 [29] .debug_abbrev PROGBITS 0000000000000000 00001191 [30] .debug_line PROGBITS 0000000000000000 0000121c [31] .debug_str PROGBITS 0000000000000000 00001273 [root@VM-12-17-centos gdb]# readelf -S mytest-release | grep -i debug [root@VM-12-17-centos gdb]#
4 详细调试
list/l 行号
显示binFile源代码,接着上次的位置往下列,每次列10行。
[root@VM-12-17-centos gdb]# gdb mytest-debug GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7 Copyright (C) 2013 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /root/lesson8/gdb/mytest-debug...done. (gdb) l 11 12 } 13 printf("quit addToTop\n"); 14 return sum; 15 } 16 17 18 int main() 19 { 20 int top=100; (gdb) l 0 1 #include <stdio.h> 2 3 int addToTop(int top) 4 { 5 printf("enter addToTop\n"); 6 int i=1; 7 int sum=0; 8 for( i=1;i<=top;i++) 9 { 10 sum+=i; (gdb) l 11 12 } 13 printf("quit addToTop\n"); 14 return sum; 15 } 16 17 18 int main() 19 { 20 int top=100; (gdb) l 21 22 int result=addToTop(top); 23 printf("result:%d\n",result); 24 return 0; 25 } (gdb) l Line number 26 out of range; test.c has 25 lines. (gdb)
list/l 函数名
列出某个函数的源代码,函数名居中(gdb) l main 14 return sum; 15 } 16 17 18 int main() 19 { 20 int top=100; 21 22 int result=addToTop(top); 23 printf("result:%d\n",result);
r或run
运行程序
(gdb) r Starting program: /root/lesson8/gdb/mytest-debug enter addToTop quit addToTop result:5050 [Inferior 1 (process 480) exited normally]
break(b)
插入断点
(gdb) b 19 Breakpoint 1 at 0x4005d2: file test.c, line 19.
info b(reak)
查看断点信息
(gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000004005d2 in main at test.c:19 (gdb) r Starting program: /root/lesson8/gdb/mytest-debug Breakpoint 1, main () at test.c:20 20 int top=100; (gdb) info b Num Type Disp Enb Address What 1 breakpoint keep y 0x00000000004005d2 in main at test.c:19 breakpoint already hit 1 time
d num
删除断点(num是断点的序号)
(gdb) d 19 No breakpoint number 19. (gdb) d 1 (gdb) info b No breakpoints or watchpoints.
(gdb) b 19 Breakpoint 2 at 0x4005d2: file test.c, line 19. (gdb) r Starting program: /root/lesson8/gdb/mytest-debug Breakpoint 2, main () at test.c:20 20 int top=100; (gdb) info b Num Type Disp Enb Address What 2 breakpoint keep y 0x00000000004005d2 in main at test.c:19 breakpoint already hit 1 time
我们重新设置断点,然后发现当我们还未退出这一次gdb时,我们即便删除之前的断点,我们重新设置的断点的num会增加,这里提一个功能,就是打开/关闭断点,我们看到enb下是yes,说明该断点是打开的,尤其是当我们不确定代码哪里出现问题,我们设置多个但是不一定同时使用的断点时,num的递增计数方式及断点的打开/关闭结合的功能会很好地帮助我们解决问题。
disable breakpoints
禁用断点(gdb) disable breakpoint 2 (gdb) info b Num Type Disp Enb Address What 2 breakpoint keep n 0x00000000004005d2 in main at test.c:19 breakpoint already hit 1 time (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /root/lesson8/gdb/mytest-debug enter addToTop quit addToTop result:5050 [Inferior 1 (process 4066) exited normally]
enable breakpoints
启用断点(gdb) enable breakpoint 3 (gdb) info b Num Type Disp Enb Address What 3 breakpoint keep y 0x00000000004005d2 in main at test.c:19
n (next)
逐过程
(gdb) r Starting program: /root/lesson8/gdb/mytest-debug Breakpoint 1, main () at test.c:20 20 int top=100; Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.x86_64 (gdb) n 22 int result=addToTop(top); (gdb) n enter addToTop quit addToTop 23 printf("result:%d\n",result); (gdb) l 22 17 18 int main() 19 { 20 int top=100; 21 22 int result=addToTop(top); 23 printf("result:%d\n",result); 24 return 0; 25 } (gdb) Line number 26 out of range; test.c has 25 lines. (gdb) n result:5050 24 return 0; (gdb) n 25 } (gdb) n 0x00007ffff7a2f555 in __libc_start_main () from /lib64/libc.so.6
s(step)
逐语句
(gdb) b 22 Breakpoint 2 at 0x4005d9: file test.c, line 22. (gdb) d 1 (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /root/lesson8/gdb/mytest-debug Breakpoint 2, main () at test.c:22 22 int result=addToTop(top); (gdb) s addToTop (top=100) at test.c:5 5 printf("enter addToTop\n");
调用函数实现压栈的过程,main函数在栈底,addToTop在其之上
breaktrace(或bt)
查看各级函数调用及参数
p 变量
打印变量值
(gdb) p sum $1 = 3 (gdb) p i $2 = 2 (gdb) p &sum $3 = (int *) 0x7fffffffe438 (gdb) p &i $4 = (int *) 0x7fffffffe43c
但是要一次一次地显示,非常麻烦
display 变量名
常显示(内置类型,结构体等自定义类型,stl);
跟踪查看一个变量,每次停下来都显示它的值
(gdb) display sum 1: sum = 3 (gdb) display i 2: i = 2 (gdb) display &sum 3: &sum = (int *) 0x7fffffffe438 (gdb) display &i 4: &i = (int *) 0x7fffffffe43c (gdb) s 10 sum+=i; 4: &i = (int *) 0x7fffffffe43c 3: &sum = (int *) 0x7fffffffe438 2: i = 3 1: sum = 3 (gdb) s 8 for( i=1;i<=top;i++) 4: &i = (int *) 0x7fffffffe43c 3: &sum = (int *) 0x7fffffffe438 2: i = 3 1: sum = 6
undisplay
取消对先前设置的那些变量的跟踪
(gdb) s 8 for( i=1;i<=top;i++) 4: &i = (int *) 0x7fffffffe43c 3: &sum = (int *) 0x7fffffffe438 2: i = 3 1: sum = 6 (gdb) undisplay &sum warning: bad display number at or near '&sum' (gdb) undisplay 3 (gdb) n 10 sum+=i; 4: &i = (int *) 0x7fffffffe43c 2: i = 4 1: sum = 6
until X行号
跳至X行,执行完区间代码
(gdb) l 0 1 #include <stdio.h> 2 3 int addToTop(int top) 4 { 5 printf("enter addToTop\n"); 6 int i=1; 7 int sum=0; 8 for( i=1;i<=top;i++) 9 { 10 sum+=i; (gdb) l 11 12 } 13 printf("quit addToTop\n"); 14 return sum; 15 } 16 17 18 int main() 19 { 20 int top=100; (gdb) until 13 addToTop (top=100) at test.c:13 13 printf("quit addToTop\n"); 4: &i = (int *) 0x7fffffffe43c 2: i = 101 1: sum = 5050
finish
执行到当前函数返回,然后停下来等待命令
(gdb) finish Run till exit from #0 addToTop (top=100) at test.c:7 quit addToTop 0x00000000004005e3 in main () at test.c:22 22 int result=addToTop(top); Value returned is $5 = 5050 (gdb) n 23 printf("result:%d\n",result); (gdb) p result $6 = 5050
c
从一个 断点处,直接运行至下一个断点处
(gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /root/lesson8/gdb/mytest-debug Breakpoint 3, main () at test.c:20 20 int top=100; (gdb) c Continuing. Breakpoint 2, main () at test.c:22 22 int result=addToTop(top);