GDB

GDB

GDB 能够允许查看程序执行时的内部情况,或者在程序出现崩溃的瞬间正在做什么。

GDB 可以做四种主要的功能来帮助捕获错误:

  • 启动程序,指定可能影响其行为的变量或条件
  • 使程序在指定条件或者位置停止
  • 检查程序停止时发生了什么
  • 更改程序中的内容,以便可以尝试更正当前已了解到的错误,然后继续调试程序,了解下一个错误

GDB支持的语言链接

使用 GDB 的条件

想要使用 GDB 进行调试程序,需要在编译程序时加入 -g 选项,让编译的程序中保留 调试符号信息。此外,最好在编译时同时加上 -O0-O能够设置编译器对我们程序的优化选项,分别由 0 - 4 五个级别,而 -O0 为让编译器不对程序做出优化,已能够准确定位错误点。


启动 GDB

启动 GDB 调试程序主要有以下三种方式

  1. gdb filename: 直接调试目标程序
  2. gdb attach pid: 附加进程
  3. gdb filename corename: 调试 core 文件

退出GDB
退出 GDB 调试命令行界面比较简单,输入 quit 或简写 q ,以及 ctrl + d 均可退出。

接下来依次介绍这三种启动方式。现在先准备一个测试程序:

// HelloGdb.c
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

int main()
{
    printf("Hello GDB\n");

    int i = 0;
    while(1) {
        i++;

        if (i == 100) {
            i = 0;
        }
    }

    return 0;
}

然后进行编译:

gcc -g -O0 HelloGdb.c -o HelloGdb

直接启动

gdb ./HelloGdb

附加进程

如果是在进程已经存在情况下,我们想在不重启该程序的情况下对其进行调试,则可以使用附加进程调试的方式。

获取进程PID

使用ps命令获取 HelloGdb 进程的 PID:

ps -ef | grep HelloGdb

# 输出如下
# h47         5130    3602  0 15:35 pts/0    00:00:00 ./HelloGdb
# h47         5249    5146  0 15:36 pts/1    00:00:00 grep --color=auto HelloGdb

可见,HelloGdb 的进程 PID 为 5130

gdb附加进程PID

接下来使用 GDB 附加进程,需要以 root 权限执行。

sudo gdb attach 5130

当用 sudo gdb attach PID 成功附加上目标进程后,调试器会将程序暂停下来,此时可以进行查看代码、添加断点等 GDB 操作,然后可以使用 continue 让程序继续运行。

当调试完程序想结束此次调试时,想单独退出GDB而不对当前进程产生影响,让其可以继续运行,可以在 GDB 命令行输入 detach 命令实现,这样进程就可以继续运行了。再使用 quit 退出GDB即可。

调试 core 文件

在实际工作中,常能见到程序在跑机测试中,运行一段时间后会偶现崩溃,这种崩溃难以及时追踪,于是可以调试 core 文件的方式追踪问题。

只要程序在崩溃时就会有 core 文件产生,就可以使用这个 core 文件来定位原因。

可以使用 ulimit -c 命令查看是否开启程序崩溃产生 core 文件的机制(Linux 系统默认不开启)。

ulimit -c

# 返回
# 0

执行以下步骤修改设置可生成 corefile :

# 打开 /etc/profile 文件
sudo vi /etc/profile

# 在该文件末新增下面语句,并保存退出
ulimit -c unlimited

# 使修改立即生效
source /etc/profile

# 查看修改是否生效
ulimit -c

执行以下步骤,指定生成 corefile 的路径:

  1. /etc/sysctl.conf 写入 corefile 文件生成的目录, 并创建该目录。
kernel.core_pattern=/home/h47/core_dump/core-%e-%p-%t

mkdir /home/h47/core_dump

/home/h47/core_dump/ 对应指定存放的路径
core-%e-%p-%t 对应文件格式

格式控制参数表如下:

参数 说明
%% 相当于%
%p pid 值
%u uid 值
%g gid 值
%s 导致dump的信号的数字
%t dump的时间
%e 执行文件名称
%h hostname
  1. 执行生效
sudo sysctl -p /etc/sysctl.conf
  1. 查看是否生效
cat /proc/sys/kernel/core_pattern

不指定生成路径,则会在执行文件的同级目录下生成 corefile

修改测试范例
我们还需要修改一下测试范例,让其在运行时崩溃:

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    printf("Hello GDB\n");

    int i = 0;
    while(1) {
        i++;

        // 让程序5s后崩溃
        if (i == 5) {
            printf("G\n");
            *(char *)0 = 0;
            i = 0;
        }
        sleep(1);
    }

    return 0;
}

执行程序崩溃后,能在设置的路径看到生成了 corefile,接下来使用以下命令进行调试:

gdb ./HelloGdb /home/h47/core_dump/core-HelloGdb-2851-1710927155

./HelloGdb 是可执行文件名称
/home/h47/core_dump/xxxxx 是刚刚生成的 corefile 的绝对路径

接着输入 bt 查看堆栈。


常用命令

命令名称 命令缩写 命令说明
run r 运行程序
continue c 让暂停的程序继续运行
next n 单步跳过,运行到下一行
step s 但不进入,进入调用的函数内部
until u 运行到指定行
finish fi 单步跳出,跳出当前函数,到上一层函数调用处
return 跳出当前函数并返回指定值,到上一层函数调用处
jump j 将当前程序执行流跳转到指定行或地址
print p 打印变量或寄存器值
backtrace bt 查看当前线程的调用堆栈
frame f 切换到当前调用线程的指定堆栈,具体堆栈通过堆栈序号指定
thread thread 切换到指定线程
break b 添加断点
tbreak tb 添加临时断点
delete del 删除断点
enable 启用某个断点
disable 禁用某个断点
watch 监视某一个变量或内存地址的值是否发生变化
list l 显示源码
info 查看断点/线程等信息
ptype 查看变量类型
disassemble dis 查看汇编代码
set args 设置程序启动命令行参数
show args 查看设置的命令行参数
posted @ 2024-03-20 22:36  功夫在诗外  阅读(148)  评论(0)    收藏  举报