使用gdb调试调试学习

例子1

编写程序:

#include <stdio.h>
int main ()
{
    unsigned long long int n, sum;
    n = 1;
    sum = 0;
    while (n <= 100)
    {
        sum = sum + n;
        n = n + 1;
    }
    return 0;
}

将程序编译成可执行文件:

# 使用 GDB 调试某个可执行文件,该文件中必须包含必要的调试信息(比如各行代码所在的行号、包含程序中所有变量名称的列表(又称为符号表)等)。因此必须有-g
# -o main:生成可执行文件main
 gcc main.cpp -o main -g

启动gdb调试器调试尚未执行的程序

$ gdb main               
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-114.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 /home/oceanstar/workspace/cpp/code/opencv_sln/main...(no debugging symbols found)...done.
(gdb) 

该指令在启动 GDB 的同时,会打印出一堆免责条款。通过添加 --silent(或者 -q、–quiet)选项,可将比部分信息屏蔽掉:

$ gdb main -q
Reading symbols from /home/oceanstar/workspace/cpp/code/opencv_sln/main...(no debugging symbols found)...done.
(gdb) 

显示带行号的源代码:l

  • 默认情况下,l 选项只显示 10 行源代码,如果查看后续代码,安装 Enter 回车即可

在这里插入图片描述

(gdb) l
No symbol table is loaded.  Use the "file" command.

原因可能是工程名和实际的不一致。切记不要将编译的二进制文件重命名

在第 7 行源代码处打断点: b 7
在这里插入图片描述
运行程序,遇到断点停止: r
在这里插入图片描述
查看代码中变量 n 的值: p n

(gdb) p n
$1 = 1  # 当前 n 的值为 1,$1 表示该变量所在存储区的名称

在第 7行的基础上,在第9行打断点: b 7

在这里插入图片描述

例子2

在某些情况下,我们可能想调试一个当前已经启动的程序,但又不想重启该程序,就可以借助 GDB 调试器实现。

编写程序:

#include <stdio.h>
int main()
{
    int num = 1;
    while(1)
    {
        num++;
    }
    return 0;
}

编译程序

gcc main.cpp -o main.exe -g

运行程序

./main.exe

借助 pidof 指令即可获取它对应的进程号:
在这里插入图片描述
在此基础上,启动gdb调试器调试正在执行的程序,调用指令有以下 3 种形式:

  1. gdb attach PID
  2. gdb 文件名 PID
  3. gdb -p PID

其中,PID 指的就是要调取的程序对应的进程号。随便选一个:

$ gdb -p 98281 -q
Attaching to process 98281
Reading symbols from /home/oceanstar/桌面/main.exe...done.
Reading symbols from /lib64/libc.so.6...(no debugging symbols found)...done.
Loaded symbols for /lib64/libc.so.6
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
Loaded symbols for /lib64/ld-linux-x86-64.so.2
main () at main.cpp:9
9	    return 0;
Missing separate debuginfos, use: debuginfo-install glibc-2.17-260.el7.x86_64
(gdb) 

打断点

(gdb) l
4	    int num = 1;
5	    while(1)
6	    {
7	        num++;
8	    }
9	    return 0;
10	}
(gdb) b 7 
Breakpoint 1 at 0x4004d8: file main.cpp, line 7.

令程序进行执行,其会在下一个断点处停止

(gdb) c
Continuing.

Breakpoint 1, main () at main.cpp:7
7	        num++;

查看当前 num 的值

(gdb) p num
$1 = 1549923866

注意,当调试完成后,如果想令当前程序进行执行,消除调试操作对它的影响,需手动将 GDB 调试器与程序分离,分离过程分为 2 步:

  • 执行 detach 指令,使 GDB 调试器和程序分离;
  • 执行 quit(或 q)指令,退出 GDB 调试。
    在这里插入图片描述

问:如果我们不指定可执行文件就直接启动gdb会怎么样

我们可以通过file手动指定要调试的目标程序。如下:

启动gdb调试器手动指定要调试的目标程序
在这里插入图片描述

例子3:

#include<stdio.h>
int main(int argc,char* argv[])
{
    FILE * fp;
    if((fp = fopen(argv[1],"r")) == NULL){
        printf("file open fail");
    }
    else{
        printf("file open true");
    }
    return 0;
}

编译程序

gcc main.cpp -o main.exe -g

对于上面的程序,如果想要程序正确运行,必须在启动程序的同时传递一个参数给这个程序。
为 GDB 调试器指定的目标程序传递参数有三种方法可以达到这个目的
(1) 方法1:在指定目标调试程序的同时,使用 --args 选项指定需要传递给该程序的数据

gdb  -q --args main.exe  /home/oceanstar/workspace/cpu.txt

在这里插入图片描述

(2) 方法2:借助 set args 命令指定目标调试程序启动所需要的数据

set args /home/oceanstar/workspace/cpu.txt

在这里插入图片描述
(3) 方法3:使用 run 或者 start 启动目标程序时,指定其所需要的数据

run   /home/oceanstar/workspace/cpu.txt
start  /home/oceanstar/workspace/cpu.txt

可以这样理解,使用 start 指令启动程序,完全等价于先在 main() 主函数起始位置设置一个断点,然后再使用 run 指令启动程序。另外,程序执行过程中使用 run 或者 start 指令,表示的是重新启动程序。

输出重定向

默认情况下,GDB 调试会将输出结果打印到屏幕上。我们可以将执行结果输出到文件或者某个终端上

比如下面:

(gdb) r > a.txt
Starting program: /home/oceanstar/桌面/main.exe > a.txt
[Inferior 1 (process 100663) exited normally]

在 GDB 调试的工作目录下就会生成一个 a.txt 文件,其中存储的即为 main.exe 的执行结果。

关于gdb的工作目录

默认情况下,GDB 调试器的工作目录为启动时所使用的目录。当然,我们也可以切换工作目录

(gdb) pwd  # 查看工作目录
Working directory /home/oceanstar/桌面.
(gdb) cd /home/oceanstar/  # 切换工作目录
Working directory /home/oceanstar.

问: 某些场景中,目标调试程序的执行需要临时修改 PATH 环境变量,怎么办?

答:使用path指令
注意,此修改方式只是临时的,退出 GDB 调试后会失效。

(gdb) path /temp/demo
Executable and object file path: /temp/demo:/usr/local/sbin:/usr/local/bin...

例子4:

#include<stdio.h>
int main(int argc,char* argv[])
{
    int num = 1;
    while(num<100)
    {
        num *= 2;
    }
    printf("num=%d",num);
    return 0;
}

GDB 调试器支持在被调试程序中打 3 种断点,分别为普通断点、观察断点和捕捉断点:

  • 普通断点用 break 命令建立
  • 观察断点用 watch 命令建立
  • 捕捉断点用 catch 命令建立

三者区别:

  • 普通断点作用于程序中的某一行,当程序运行至此行时停止执行
  • 观察断点作用于某一变量或表达式,当该变量(表达式)的值发生改变时,程序暂停
  • 捕捉断点的作用是,监控程序中某一事件的发生,例如程序发生某种异常时、某一动态库被加载时等等,一旦目标时间发生,则程序停止执行。

break

(gdb) l
1 #include<stdio.h>
2 int main(int argc,char* argv[])
3 {
4     int num = 1;
5     while(num<100)
6     {
7         num *= 2;
8     }
9     printf("num=%d",num);
10   return 0;
(gdb)
11 }
(gdb) b 4          <-- 程序第 4 行打断点
Breakpoint 1 at 0x1138: file main.c, line 4.
(gdb) r              <-- 运行程序,至第 4 行暂停
Starting program: /home/ubuntu64/demo/main.exe

Breakpoint 1, main (argc=1, argv=0x7fffffffe078) at main.c:4
4     int num = 1;
(gdb) b +1        <-- 在第 4 行的基础上,在第 5 行代码处打断点
Breakpoint 2 at 0x55555555513f: file main.c, line 5.
(gdb) c             <-- 继续执行程序,至第 5 行暂停
Continuing.

Breakpoint 2, main (argc=1, argv=0x7fffffffe078) at main.c:5
5     while(num<100)

如果 num>10 在第 7 行打断点

(gdb) b 7 if num>10     <-- 如果 num>10 在第 7 行打断点
Breakpoint 3 at 0x555555555141: file main.c, line 7.
(gdb) c               <-- 继续执行
Continuing.

Breakpoint 3, main (argc=1, argv=0x7fffffffe078) at main.c:7
7         num *= 2;       <-- 程序在第 7 行暂停
(gdb) p num      <-- p 命令查看 num 当前的值
$1 = 16             <-- num=16

watch

  • watch 命令的功能是:只有当被监控变量(表达式)的值发生改变,程序才会停止运行。
  • rwatch 命令:只要程序中出现读取目标变量(表达式)的值的操作,程序就会停止运行;
  • awatch 命令:只要程序中出现读取目标变量(表达式)的值或者改变值的操作,程序就会停止运行。

对于使用 watch(rwatch、awatch)命令监控 C、C++ 程序中变量或者表达式的值,有以下几点需要注意:

  • 当监控的变量(表达式)为局部变量(表达式)时,一旦局部变量(表达式)失效,则监控操作也随即失效;
  • 如果监控的是一个指针变量(例如 *p),则 watch *p 和 watch p 是有区别的,前者监控的是 p 所指数据的变化情况,而后者监控的是 p 指针本身有没有改变指向;
  • 这 3 个监控命令还可以用于监控数组中元素值的变化情况,例如对于 a[10] 这个数组,watch a 表示只要 a 数组中存储的数据发生改变,程序就会停止执行。
(gdb) l           <--列出要调试的程序源码
1 #include<stdio.h>
2 int main(int argc,char* argv[])
3 {
4     int num = 1;
5     while(num<=100)
6     {
7         num *= 2;
8     }
9     printf("%d",num);
10     return 0;
(gdb)
11 }
(gdb) b 4       <-- 使用 break 命令打断点
Breakpoint 1 at 0x115c: file main.c, line 4.
(gdb) r           <-- 执行程序
Starting program: /home/ubuntu64/demo/main.exe

Breakpoint 1, main (argc=1, argv=0x7fffffffe088) at main.c:4
4     int num = 1;
(gdb) watch num   <-- 监控程序中 num 变量的值
Hardware watchpoint 2: num
(gdb) c            <-- 继续执行,当 num 值发生改变时,程序才停止执行
Continuing.

Hardware watchpoint 2: num

Old value = 0
New value = 2
main (argc=1, argv=0x7fffffffe088) at main.c:5
5     while(num<=100)
(gdb) c           <-- num 值发生了改变,继续执行程序
Continuing.

Hardware watchpoint 2: num

Old value = 2
New value = 4
main (argc=1, argv=0x7fffffffe088) at main.c:5
5     while(num<=100)
(gdb)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值