GDB使用经验分享

写了很长时间的代码,一直是使用IDE的debug,一直想试一下这一款最原始最直接的debug工具,最近在弄一生一芯,所以打算认真学一下gdb,分享以下自己的学习经验。

man gdb 

or

gdb --help

使用以上命令可以看到gdb工具的使用方法,在此只做简单介绍,后期不断补充。接下来将会介绍gdb使用中频繁会用到的功能。我们以一个简单程序为例进行介绍:

test.c

1   #include <iostream>
2   int a = 0;
3   void fun()
4   {
5       int c;
6       a += 1;
7       printf("%d\n", a);
8   }
9   int main()
10  {
11      printf("start\n");
12      a = 0;
13      int b=10;
14      for (int i = 0; i < 10; i++)
15          fun();
16      printf("end\n");
17  }

在编译时请使用 -g , -g请求编译器和链接器在可执行文件本身中生成并保留源级调试/符号信息。

g++ -g test.c 

现在得到了可执行文件a.out,使用gdb开始调试可执行文件。

gdb a.out

1.设置断点

(gdb) b/breakpoint <location>

我们在程序运行的过程中检测某个变量的变化,除了在程序中间插入printf()打印以外,还可以使用gdb对中间变量打印检测,当然我们我们在gdb中也可以使用更复杂的脚本。

设置断点可以使用"b"或者"break“,断点的设置可以通过函数名、行号、文件名+函数名、文件名+行号以及偏移量、地址等进行设置。通过info break可以查看设置的断点。

格式为:

  • break 函数名

  • break 行号

  • break 文件名:函数名

  • break 文件名:行号

  • break +偏移量

  • break -偏移量

  • break *地址

    (gdb) b 11
    Breakpoint 1 at 0x11eb: file test.c, line 11.
    (gdb) b 15
    Breakpoint 2 at 0x1214: file test.c, line 15.
    (gdb) info b
    Num Type Disp Enb Address What
    1 breakpoint keep y 0x00000000000011eb in main() at test.c:11
    2 breakpoint keep y 0x0000000000001214 in main() at test.c:15

我们在第11行和15行设置了断点,我们开始运行测试程序,使用”run"或者"r",其作用是连续执行程序,直到遇到断点。

(gdb) r
Starting program: /home/hs/gdb_test/a.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Breakpoint 1, main () at test.c:11
11          printf("start\n");

我们可以看到测试程序在第11行停住了(请注意此处程序是停在了执行printf()之前),接下来介绍4个命令:

continue|c  //继续执行程序,直到下个断点
next|n		//执行下一行语句
step|s      //进入正在执行的函数内部
finish      //进入正在执行的函数内部

很容易理解它们的含义,假设我们现在想要运行printf()这条打印命令,只需要next即可。

(gdb) n
start
12          a = 0;
(gdb) n
13          int b=10;
(gdb) n
14          for (int i = 0; i < 10; i++)
(gdb) n

Breakpoint 2, main () at test.c:15
15              fun()

现在执行到了fun()函数,我们可以使用前文提到的“step|s” 进入正在执行的函数内部。

(gdb) s
fun () at test.c:6
6           a += 1;

若我们现在想知道a的结果,可以使用进行变量打印。

2.变量查看修改及打印

(gdb) p/print/inspect [/<显示格式>] <表达式>

我们可以在此处输出a的值

6           a += 1;
(gdb) p "a = %d\n", a
$1 = 0
(gdb) p "b = %d\n", b
$2 = {i = {0, 1068498944}, x = 0.0625, d = 0.0625}
(gdb) n
7           printf("%d\n", a);
(gdb) n
1
8       }

我们可以看到到执行完 a+=1之后,a由0变成了1。

我们当然我们也可以使用以下命令打印当前作用域的变量。

(gdb) i/info loc/locals

(gdb) i locals 
c = <optimised out>

fun()函数的作用域中没有变量a,所以只输出了在fun()中定义的变量c,此时使用finish命令”退出“fun()函数,返回到main()函数,当然程序实际上不会跳过fun()函数的内部每一个步骤,只是我们在执行next的时候"fun()"命令只当做一步,而不会展开执行。

(gdb) fin
Run till exit from #0  fun () at test.c:8
main () at test.c:14
14          for (int i = 0; i < 10; i++)
(gdb) i locals 
i = 0
b = 10

此时”退出“fun()函数后使用i locals 就能看到当前作用域中的局部变量i(存在于for循环中的局部变量)和b(main()中定义)。

3.设置监视点

(gdb) watch <addr/变量>    // 设置观察点,当一个变量的值发生变化时,程序会停下来
(gdb) rwatch <addr/变量>   // 设置观察点,当一个变量被读时,停止程序
(gdb) awatch <addr/变量>   // 设置观察点,当一个变量被读或写时,停止程序
(gdb) info watch           // 查看所有设置的观察点  
(gdb) watch <addr/变量> if (expression)  // 设置条件监视点

我们在for循环中设置监视点,检视变量i。

(gdb) watch i
Hardware watchpoint 3: i   
(gdb) info watch
Num     Type           Disp Enb Address            What
3       hw watchpoint  keep y                      i

当检测到i发生变化时程序则会暂停。

(gdb) c
Continuing.

Hardware watchpoint 3: i

Old value = 0
New value = 1
0x000055555555521d in main () at test.c:14
14          for (int i = 0; i < 10; i++)

运行quit | q 或者 exit可以退出gdb的执行。

以上则是gdb的简单使用,我们可以看到作用域的概念在gdb中还是比较重要的,更多使用细节可以参考官方手册。

参考链接:

https://blog.csdn.net/StarRain2016/article/details/119779616

https://www.yanbinghu.com/2019/04/20/41283.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值