Linux·gdb用法详解

一、gdb简介

1、什么是gdb

gdb(GNU Debugger)是一个强大的调试工具,广泛用于调试 C、C++、Fortran 和其他编程语言的程序。gdb 支持多种调试功能,包括断点、单步执行、变量查看、内存操作和多线程调试等。通过合理使用这些功能,可以有效地定位和解决程序中的错误。

2. gdb的检查与安装

基于centos 7 系统的操作

  • 检查是否安装gdb
rpm -qa | grep gdb

检查是否安装gdb

此时则已安装

  • 查看当前gdb的版本
gdb -version

查看当前gdb的版本

  • gdp 的安装
sudo yum install -y gdb

3.调试的作用

在 Linux 中,调试 是指在程序开发和维护过程中,分析、识别和修复程序中的错误或不符合预期的行为的过程。

其作用可总结为:
错误定位、代码理解、性能优化、验证和测试、开发支持

二、开发版本debug与release的区别

1.编译优化与代码大小

debug 版本:

优化级别低或禁用优化,保持源代码的结构不变。有助于调试,因为程序的行为更加可预测且易于追踪。文件大小通常较大,因为包含了调试信息,这些信息用于帮助开发人员进行调试。
调试信息: 编译时包含详细的调试信息(通过 -g 选项),使调试器能够提供源代码级别的调试支持,如断点设置和变量查看。

编译debug 需在gcc 后加 -g 选项

gcc -g test.c 

release 版本:

优化级别高,以提高程序的性能和效率。可能包括内联函数、循环展开、死代码删除等优化。文件大小通常较小,因为优化和去除调试信息可以减少最终二进制文件的大小。
调试信息通常被省略: 出于性能和文件大小考虑,Release 版本通常不包含调试信息,或者仅包含最少量的调试信息。

gcc默认编译版本为 release

gcc test.c

release版本与debug版本大小对比图

可以发现在对同一文件生成两种版本的可执行文件时,debug版本的文件大小是大于release版本的

gcc在代码编译生成可执行程序时,并不是简单的转换成二进制文件,而是为ELF格式的可执行程序文件。

readelf 是一个 Linux 命令行工具,用于查看和分析 ELF文件的内容。readelf 允许开发者检查 ELF 文件的各种信息。

所以可以用readelf命令进行更加详细的查看,可以发现在release版本中并不存在.debug_*类文件。
release版本与debug版本文件对比

2. 性能

debug 版本:

由于禁用了大多数优化,程序的运行速度较慢。调试信息的附加也可能增加文件的体积。

release 版本:

经过优化的程序通常具有更好的性能,执行速度更快,内存使用更高效。
但在某些情况下,优化可能会导致程序出现错误,特别是在代码中存在未定义行为或复杂的逻辑时。

3. 错误处理

debug 版本:

可能包含额外的错误检查和断言,帮助捕获和报告程序中的问题。这些检查有助于在开发过程中发现潜在的错误,但可能影响性能。

release 版本:

错误检查和断言通常会被移除,以提高性能和减少开销。在 Release 版本中,程序通常对错误的处理更为宽松。

4. 使用场景

debug 版本:

用于开发阶段的测试,以便开发人员可以追踪代码、查找错误和验证功能。

release 版本:

用于生产环境或最终用户发布,旨在提供高性能和高效的运行。
在完成程序开发后的测试也是测试release版本,用于性能验证和最终用户体验的确认。

三、gdb命令汇总

由于命令过多,阅读时可使用Ctrl+f进行具体命令的查找。

1.启动和加载程序

  • gdb <program>
    启动 GDB 并加载指定的程序。
    示例: gdb my_program

  • gdb
    启动 GDB,但不加载程序,可以通过 file 命令加载程序。
    示例: gdb

  • file <filename>
    加载指定的可执行文件或符号文件。
    示例: file my_program

2.运行程序

  • run [args]
    启动程序并传递命令行参数 args
    示例: run arg1 arg2

  • start
    启动程序并在主函数的开始处停下。
    示例: start

3.断点

  • break <location>
    在指定位置设置断点,例如函数名、行号或地址。
    示例: break mainbreak 42

  • condition <breakpoint> <condition>
    为指定的断点设置条件。
    示例: condition 1 x > 10

  • delete [breakpoint]
    删除指定的断点,若不指定则删除所有断点。
    示例: delete 1

  • disable [breakpoint]
    禁用指定的断点,若不指定则禁用所有断点。
    示例: disable 1

  • enable [breakpoint]
    启用指定的断点,若不指定则启用所有断点。
    示例: enable 1

  • info break
    显示所有断点的信息。
    示例: info break

4.程序控制

  • continue (或 c)
    继续执行程序直到下一个断点。
    示例: continue

  • next (或 n)
    执行当前行并停在下一行,跳过函数调用。
    示例: next

  • step (或 s)
    执行当前行并停在下一行,进入函数调用。
    示例: step

  • finish
    执行到当前函数返回并停在返回点。
    示例: finish

  • until <location>
    执行到指定位置,例如行号或函数名。
    示例: until 45

  • stepi (或 si)
    单步执行一条机器指令。
    示例: stepi

  • nexti (或 ni)
    单步执行一条机器指令,但跳过函数调用。
    示例: nexti

  • quit (或 q)
    退出 GDB。
    示例: quit

5.查看和修改数据

  • print <expression> (或 p <expression>)
    打印表达式的值。
    示例: print x

  • set var <variable> = <value>
    修改变量的值。
    示例: set var x = 10

  • display <expression>
    每次程序停止时自动显示表达式的值。
    示例: display x

  • info locals
    显示当前函数的局部变量。
    示例: info locals

  • info args
    显示当前函数的参数。
    示例: info args

  • x/<n> <address>
    以指定格式和数量显示内存中的内容。
    示例: x/4x 0x601040

  • info registers
    显示所有寄存器的当前值。
    示例: info registers

6.堆栈和调用

  • backtrace (或 bt)
    显示当前堆栈的回溯信息。
    示例: backtrace

  • frame <n>
    切换到指定的堆栈帧 n
    示例: frame 2

  • up
    向上移动到上一个堆栈帧。
    示例: up

  • down
    向下移动到下一个堆栈帧。
    示例: down

  • info stack
    显示当前堆栈的详细信息。
    示例: info stack

7.文件和符号

  • info file
    显示当前加载的文件和符号信息。
    示例: info file

  • list (或 l)
    显示源代码中的当前位置。
    示例: list

  • info source
    显示当前源文件的信息。
    示例: info source

  • symbol-file <filename>
    加载指定的符号文件。
    示例: symbol-file symbols.sym

8.其他

  • watch <variable>
    设置观察点,每当变量值发生变化时停止程序。
    示例: watch x

  • catch <event>
    设置事件捕捉点,例如 catch throw 捕捉异常抛出。
    示例: catch throw

  • set logging on
    开始将 GDB 会话的输出记录到文件中。
    示例: set logging on

  • set logging off
    停止记录 GDB 会话的输出。
    示例: set logging off

  • info threads
    显示所有线程的信息。
    示例: info threads

  • thread <id>
    切换到指定线程 id
    示例: thread 2

  • monitor <command>
    在 GDB 中执行特定于目标的命令。
    示例: monitor reset

9.帮助

  • help
    显示帮助信息。
    示例: help

  • help <command>
    显示指定命令的帮助信息。
    示例: help print

四、常用命令详解

1.代码打印

打印上下十行

list(i)后跟行号会打印该行代码上下共十行的内容

代码打印显示

打印全部代码

gdb 会记录最近一次的的命令

再输入l 0后只需要不停的按回车便可以打印出全部内容,在打印出全部内容后在按一次回车会显示出该代码的行数

[czy@iZbp10xa52xh2cwwptyrveZ test_process]$ gdb CFile_process_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 /home/czy/test/test_process/CFile_process_debug...done.
(gdb) l 0
1	#include <stdio.h>
2	#include <string.h>
3	int main()
4	{
5	    FILE *fd = fopen("bite.txt", "w");
6	    if (fd == NULL)
7	    {
8	        perror("Failed to open file");
9	        return 1;
10	    }
(gdb) #回车
11	    char *str = "linux so easy!\n";
12	    fwrite(str, sizeof(char), strlen(str), fd);
13	    fclose(fd);
14	    fd = fopen("bite.txt", "r");
15	    if (fd == NULL)
16	    {
17	        perror("Failed to open file");
18	        return 1;
19	    }
20	    char buffer[100];
(gdb) #回车
21	    size_t read_bytes = fread(buffer, sizeof(char), strlen(str), fd);
22	    buffer[read_bytes] = '\0'; 
23	    printf("%s",buffer);
24	    fclose(fd);
25	    return 0;
26	}(gdb)#回车 
Line number 27 out of range; CFile_process.c has 26 lines.

此后再输入l 0便可直接打印全部代码

2.断点操作

创建断点
  • b 行号
  • b 函数名
  • b 源文件:行号
  • b 源文件:函数名

在对多个源文件进行调试时,可使用上述的 b 源文件:行号/函数名 操作

(gdb) l 0
1	#include <stdio.h>
2	#include <string.h>
3	void gdb_test()
4	{
5	  printf("hallo gdb\n");
6	}
7	int main()
8	{
9	    FILE *fd = fopen("bite.txt", "w");
10	    if (fd == NULL)
(gdb) 
11	    {
12	        perror("Failed to open file");
13	        return 1;
14	    }
15	    char *str = "linux so easy!\n";
16	    fwrite(str, sizeof(char), strlen(str), fd);
17	    fclose(fd);
18	    fd = fopen("bite.txt", "r");
19	    if (fd == NULL)
20	    {
(gdb) 
21	        perror("Failed to open file");
22	        return 1;
23	    }
24	    char buffer[100];
25	    size_t read_bytes = fread(buffer, sizeof(char), strlen(str), fd);
26	    buffer[read_bytes] = '\0'; 
27	    printf("%s",buffer);
28	    fclose(fd);
29	    return 0;
30	}
(gdb) 
Line number 31 out of range; CFile_process.c has 30 lines.
(gdb) b 5
Breakpoint 1 at 0x400731: file CFile_process.c, line 5.
(gdb) b main
Breakpoint 2 at 0x400745: file CFile_process.c, line 9.
(gdb) b CFile_process.c: 10
Breakpoint 3 at 0x400758: file CFile_process.c, line 10.
(gdb) b CFile_process.c: gdb_test
Note: breakpoint 1 also set at pc 0x400731.
Breakpoint 4 at 0x400731: file CFile_process.c, line 5.
#可以看到四种方式都可以成功的创建断点。
查看断点信息

若是直接执行info的话,出来的就是所有的调试信息但若是我们只想查看一下所打的断点的信息,那就在后面加个b/breakpoint

info b

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400731 in gdb_test at CFile_process.c:5
2       breakpoint     keep y   0x0000000000400745 in main at CFile_process.c:9
3       breakpoint     keep y   0x0000000000400758 in main at CFile_process.c:10
4       breakpoint     keep y   0x0000000000400731 in gdb_test at CFile_process.c:5

通过info b即可查看当前的断点信息。
断点字段信息

字段名内容
Num编号
Type类型
Disp状态
Enb是否可用
address地址
What在此文件的哪个函数的第几行
删除断点
  • d 断点编号
  • d breakpoints (删除所有断点)

通过上文查看当前的断点信息后发现,每一个断点都有一个编号。在删除断点时是不支持d后跟行号的。
删除断点并新增断点后断点编号示意图

断点编号是由小到大依次递增的,删除的断点编号不会被恢复。
一旦退出则全部断点删除

断点禁用
  • disable 断点编号 (禁止断点)
  • enable 断点编号 (开启断点)
    禁用指定的断点,但不删除它。这意味着断点不会在程序运行时触发,但仍然保留在断点列表中,以便以后可以重新启用。禁用断点对于暂时不需要中断程序的调试时很有用。
    断点禁用示意图
    断点开启示意图

在断点字段中Enb表示断点是否可用,可以发现在使用disable时Enb状态变为了n

3.运行、查看与更改变量

  • s 逐语句
  • n 逐过程
  • Unit 行号 运行到XX行
  • Finish 运行到当前函数结尾
  • C 从当前断点到下一个断点
  • P 变量 查看变量
  • Diplay 变量 常显示变量
  • Undisplay 变量编号 取消常显示
  • set var 变量名 = 新变量值 更改变量值
(gdb) r #输入r后开始运行
Starting program: /home/czy/test/test_process/CFile_process_debug 

Breakpoint 2, main () at CFile_process.c:9 #到达第一个断点
9	    FILE *fd = fopen("bite.txt", "w");
Missing separate debuginfos, use: debuginfo-install glibc-2.17-326.el7_9.3.x86_64
(gdb) c #c 从此断点运行到下一个断点
Continuing.

Breakpoint 5, main () at CFile_process.c:10
10	    if (fd == NULL)
(gdb) s #逐语句运行
15	    char *str = "linux so easy!\n";
(gdb) n 
16	    fwrite(str, sizeof(char), strlen(str), fd);
(gdb) p str #p 变量名 查看变量 
$1 = 0x4008f9 "linux so easy!\n"
(gdb) n #p 变量名 查看变量 只显示一次
17	    fclose(fd);
(gdb) display str #display 变量名 常显示变量
1: str = 0x4008f9 "linux so easy!\n"
(gdb) n
18	    fd = fopen("bite.txt", "r");
1: str = 0x4008f9 "linux so easy!\n" #display 变量名 常显示变量
(gdb) n
19	    if (fd == NULL)
1: str = 0x4008f9 "linux so easy!\n" #display 变量名 常显示变量
(gdb) undisplay str
warning: bad display number at or near 'str'
(gdb) undisplay 1 #undisplay 变量显示编号 取消常显示变量
(gdb) n
26	    buffer[read_bytes] = '\0'; #取消后不在显示str内容
(gdb) until 28 #跳转运行至28行
linux so easy!
main () at CFile_process.c:28
28	    fclose(fd);

# 由于代码原因不能演示set var 变量名 = 新变量值 更改变量值

常显示变量 变量编号示意图
GDB(GNU Debugger)是一个强大的调试工具,用于调试 C、C++、Fortran 等语言的程序。它支持多种调试功能,如断点、单步执行、变量查看、内存操作和多线程调试等。希望读者可以通过本文加深对gdb调试的理解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值