linux专题:GDB详细调试方法与实现

简介: linux专题:GDB详细调试方法与实现

一、实验目的


  1. 掌握使用 gcc 分步编译 c 代码为可执行程序步骤以及 gcc 常用选项作用
  2. 掌握 linux 系统下 gcc 编译器和 gdb 调试器的配合
  3. 掌握 Linux 系统下 gdb 调试器的使用


二、实验现象


     1.对比添加-g选项编译前面可执行程序的大小,添加-g生成的可执行程序会比较大,因为其中包括调试信息,如果要使用gdb对源码进行调试必须添加-g进行编译。

  2.对比gdb各命令的执行效果

run 重新开始运行程序,但是以前设置的断开信息不会丢失

break 可用来设置一个断开,让程序运行遇到断点停止

continue 继续全速运行程序直到遇到断点停下来

step 如果当前代码是调用一个函数,则会进入到函数源码单步运行

next 和 s 命令相似,不同点是把前代码是函数调用时,并不会进入函数源码内部单步执行

    print 可以输出变量的值

       finish 可以快速跳过当前函数

       quit 可以结束gdb 调试


三、实验准备


  1. 系统需求:安装有 Windows 7 及以上版本的操作系统的电脑;
  2. 硬件配置:内存 4G+,硬盘 200G+,处理器 I3 以上;
  3. 软件需求:安装 VMware 虚拟机、以及在 VMware 中已经安装好Ubuntu 18.04;


四、Linux GDB调试实验流程


       1、要使用 gdb 调试器来调试程序,和编译程序时候也需要给 gcc 编译器指定编译选项才可以。本小节先通过一个简单的c 程序来学习gcc 分步编译源文件到可执行程序的分步编译方法以及常用选项。


       2、使用vim或gedit编辑器编写测试代码,具体内容如下:

@:01_gcc_test$ gedit hello.c


      编写内容如下:

#include <stdio.h> 
int main (void)
{
    printf("hello world\n") ; 
    return 0;
}


  3、gcc编译文件

gcc 指令的一般格式为:gcc [选项] 要编译的文件 [选项] [目标文件]

例:使用 gcc 编译命令,编译 hello.c 生成可执行文件 hello,并运行 hello

@:01_gcc_test$ gcc hello.c -o hello
@:01_gcc_test$ ./hello
hello world


上面的命令一步由.c 文件生成了可执行文件,将gcc 的四个编译流程:预处理、编译、汇编、连接一步完成,下面将介绍四个流程分别做了什么工作。


       4、-E 选项的作用:只进行预处理,不做其他处理。

例:只对 hello.c 文件进行预处理,生成文件 hello.i,并查看

@:01_gcc_test$ gcc -E hello.c -o hello.i 
@:01_gcc_test$ ls
hello hello.c hello.i


  通过查看可以看到头文件包含部分代码#include <stdio.h>经过预处理阶段之后,编译器已将 stdio.h的容贴了进来。


       5、-S 选项的使用

-S 选项的作用:只是编译不汇编,生成汇编代码

例:将 hello.i 文件只进行编译而不进行汇编,生成汇编代码 hello.s

@:01_gcc_test$ gcc -S hello.i -o hello.s 
@:01_gcc_test$ ls
hello hello.c hello.i hello.s


       6、-c 选项的使用

-c 选项的作用:只是编译不连接,生成目标文件.o

例:将汇编代码 hello.s 只编译不成 hello.o 文件

@:01_gcc_test$ gcc -c hello.s -o hello.o
 @:01_gcc_test$ ls
hello hello.c hello.i hello.o hello.s


   7、将编译好的hello.o 库,生成可执行文件hello

@:01_gcc_test$ gcc -o helloo hello.o 
@:01_gcc_test$ ./hello
hello world


    8、-static 选项的使用

-static 选项的作用:静态库

例:比较 hello.c 连接动态库生成的可执行文件 hello 和静态库生成的可执行文件 hello1 的大小默认是使用动态库链接:

@:01_gcc_test$ gcc -o hello hello.c


 添加 -static 编译时会使用静态库链接:

@:01_gcc_test$ gcc -static -o hello_static hello.c


    对比使用静态库和动态库链接的最后可执行文件大小:

@:01_gcc_test$ ls -lh hello hello_static
-rwxr-xr-x 1   8.2K 2 月 2 14:31 hello
-rwxr-xr-x 1   826K 2 月 2 14:32 hello_static


结果显示静态库的可执行文件 hello_static 比动态库的可执行文件 hello 要大的多,但是它们的执行效果是一样的。  


       9、-g 选项的使用

     -g 选项的作用:在可执行程序中包含标准调试信息

     例:将 hello.c 编译成包含标准调试信息的可执行文件 hello2

@:01_gcc_test$ gcc -g hello.c -o hello2


  对比添加-g 选项前后生成的可执行程序文件大小:

@:01_gcc_test$ ls hello hello2 -hl
-rwxr-xr-x 1   8.2K 2 月 2 14:31 hello
-rwxr-xr-x 1    11K 2 月 2 14:35 hello2


  带有标准调试信息的可执行文件可以使用 gdb 调试器进行调试,以便找出逻辑错误。

       10、-O2 选项的使用

-O2 选项的作用:完成程序的优化工作

例:将 hello.c 用 O2 优化选项编译成可执行文件 hello3,和正常编译产生的可执行文件 hello 进行比较

@:01_gcc_test$ gcc -O2 hello.c -o hello3


五、Linux GDB 调试器


       接着上小节,本节编写一个稍微复杂一点的代码来学习 gdb 调试程序的方法,我们新创建一个新的代码目录,在新目录中编写测试代码。

@:01_gcc_test$ cd ..
@:syslab04$ pwd


1、先用 vim 或 gedit 编辑器编辑文件 test.c 用于 gdb 调试器调试,容如下:

#include <stdio.h>
int main(void) 
{
  int sum(int sum);
  int i,result=0;
  sum(100);
  for(i=1;i<=100;i++){
  result+=i;
  }
  printf("The sum in main function is %d\n",result);
  return 0;
}
int sum(int num)
 {
  int i,n=0;
  for(i=0;i<=num;i++){
  n+=i;
 }
  printf("The sum in sum function is %d\n",n);
}


2、将test.c 文件编译成包含标准调试信息的文件 test

@:02_gdb_test$ gcc -g -o test test.c    //#注意必须添加 -g 选项
@:02_gdb_test$ ls
test  test.c


3、启动gdb 进行调试

cc1069bb72d54dd0b83c52a07308c719.png

@:02_gdb_test$ gdb test
GNU gdb (Ubuntu 8.1-0ubuntu3.2) 8.1.0.20180409-git Copyright (C) 2018 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-linux-gnu". Type "show configuration" for configuration details. 

可以看到 gdb 启动界面中显示了 gdb 的版本、自由软件等信息,然后进入了有”gdb”开头的命令行界面


4、l(list)命令

    l 命令用于查看文件

(gdb) list
1 #include <stdio.h>
2 int main(void) 
3 {
4   int sum(int sum);
5   int i,result=0;
6   sum(100);
7   for(i=1;i<=100;i++){
8   result+=i;
9   }
10    printf("The sum in main function is %d\n",result);
(gdb) 


可以看到每行代码面前都有对应的行号,这样方便我们设置断点。


       5、b(breakpoint)命令

    b 用于设置断点,断点调试时调试程序的一个非常重要的手段,设置方法:在”b”命令之后加上对应的行号,如下:

(gdb) b 6
Breakpoint 1 at 0x400535: file a.c, line 6.
(gdb) 


6、info 命令

info break 命令用于查看断点情况,设置好断点后可以用它来查看

(gdb) info break
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400535 in main at a.c:6
(gdb) 


再在第 7 行增加一个断点:

(gdb) b 7
Breakpoint 2 at 0x40053f: file a.c, line 7.
(gdb) 


查看当前断点情况:

(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000400535 in main at a.c:6
2       breakpoint     keep y   0x000000000040053f in main at a.c:7
(gdb) 


7、r(run)命令

r 命令用于运行代码,默认是从首行开始运行,也可以在 r 后面加上行号,从程序中指定行开始运行。

(gdb) r
Starting program: /home/huzhiyuan/work/ddd/test 
Breakpoint 1, main () at a.c:6
6   sum(100);
(gdb) 


执行 r 命令后,可以看到程序停止在第 1 个断点处(第 6 行)。


7、c(continue)命令

当已经使用 r 命令运行程序,程序在某个断点处停止下来了,想让程序继续运行可以输入这个命令, 前面程序分别在 6,7 两行设置了两个断点,使用 r 命令程序停在第 6 行,要让程序停止在下一断点(第 7 行),输入 c 命令执行即可。

(gdb) c
Continuing.
The sum in sum function is 5050
Breakpoint 2, main () at a.c:7
7   for(i=1;i<=100;i++){
(gdb) 


可以看到程序运行到断点处就停止在第 2 个断点(第 7 行)。


8、p(print)命令

p 命令用于查看变量的值,在调试的时候我们经常要查看某个变量当前的值与我们逻辑设定的值是否

(gdb) p result
$1 = 0
(gdb) p num
$2 = num
(gdb) p i
$3 = 0
(gdb) 


9、s(step)命令

s 命令用于单步运行,另外 n(next)命令也用于单步运行,他们的区别在于:如果有函数调用的时候,

s 会进入该函数而n 不会进入该函数。

当前程序已经运行到第 7 行,现在我们也可以重启程序,让程序从头开始运行,输入 r 命令执行,然后输入 y 确认,程序会从头开始运行,直到遇到第一个断点(第 6 行)

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/huzhiyuan/work/ddd/test 
Breakpoint 1, main () at a.c:6
6   sum(100);
(gdb) 


输入 s 命令,进入 sum 函数内部执行,如下:

(gdb) s
sum (num=100) at a.c:16
16    int i,n=0;
(gdb) 


可以看到进入了 sum 子函数,这时候就能看到 num 的值为 100。


10、n(next)命令

n 命令用于单步运行,为了测试 s 和 n 命令区别,我们重新运行程序,在第 6 行处,不使用s 命令, 而使用 n 命令测试看看结果如果,操作如下所示:

下面是 n 命令的使用:

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/huzhiyuan/work/ddd/test 
Breakpoint 1, main () at a.c:6
6   sum(100);
(gdb) n
The sum in sum function is 5050
Breakpoint 2, main () at a.c:7
7   for(i=1;i<=100;i++){
(gdb) 


和 s 命令的运行效果对比会发现,使用 n 命令后,程序显示函数 sum 的运行结果并向下执行,而使用

s 命令后则会进入到sum 函数之中单步运行。


11、finish 命令

finish 命令用于运行程序,直到当前函数结束。例如我们进入了 sum 函数,使用 finish 命令的情况。同样,我们先重新运行程序,让程序在第 6 行停止,然后使用 s 命令进入函数内部,使用 finsh 命令快速执行完成函数代码退出函数。

(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/huzhiyuan/work/ddd/test 
Breakpoint 1, main () at a.c:6
6   sum(100);
(gdb) s
sum (num=100) at a.c:16
16    int i,n=0;
(gdb) s
17    for(i=0;i<=num;i++){
(gdb) p num
$4 = 100
(gdb) finish
Run till exit from #0  sum (num=100) at a.c:17
The sum in sum function is 5050
Breakpoint 2, main () at a.c:7
7   for(i=1;i<=100;i++){
Value returned is $5 = 32
(gdb) 

13、q(quit)命令

q 命令用于退出gdb 调试器

(gdb) q
@:02_gdb_test$


五、Linux GDB 实验原理

      使用GDB 可以调试C、C++、JAVA、汇编程序等。GDB 支持通过串口进行在线调试。

通过串口在线的调试方式,可以支持各种 CPU。调试器使用:

1、监视程序中变量的值

       2、设置断点以使程序在指定的代码行上停止执行

       3、单步执行代码

       4、检测内存中的数据

64634d4b3ae74330886c2c0c2d92543e.png

 大多数复杂Linux 程序一样,GDB 是通过内部命令来完成调试工作的。help 命令可以例出 gdb 的命令种类,如果要看种类中的命令,可以使用 help <class> 命令,如:help breakpoints,查看设置断点的所有命令。也可以直接help <command>来查看命令的帮助。gdb 中,输入命令时,可以不用打全命令,只用打命令的前几个

字符就可以了,当然,命令的前几个字符应该要标志着一个唯一的命令

在 gdb 下,你可以敲击两次 TAB 键来补齐命令的全称,如果有重复的,那么gdb 会把其例出来。

file

装入想要调试的可执行文件.

kill

终止正在调试的程序.

list

列出产 Th 执行文件的源代码的一部分.

next

执行一行源代码但不进入函数内部.

step

执行一行源代码而且进入函数内部.

run

执行当前被调试的程序

c

继续运行程序

quit

终止 gdb

watch

使你能监视一个变量的值而不管它何时被改变.

backtrace

栈跟踪,查出代码被谁调用

print

查看变量的值

make

使你能不退出 gdb 就可以重新产Th 可执行文件.

shell

使你能不离开 gdb 就执行 UNIX shell 命令

whatis

显示变量或函数类型

break

在代码里设置断点, 这将使程序执行到这里时被挂起.

info break

显示当前断点清单,包括到达断点处的次数等。

info files

显示被调试文件的详细信息。

info func

显示所有的函数名称。

info local

显示当函数中的局部变量信息。

info prog

显示被调试程序的执行状态。

delete [n]

删除第 n 个断点

disable[n]

关闭第 n 个断点

enable[n]

开启第 n 个断点

ptype

显示结构定义

set variable

设置变量的值

call name(args)

调用并执行名为 name,参数为 args 的函数

finish

终止当前函数并输出返回值

return value

停止当前函数并返回value 给调用者


总结


      今天详细总结了GDB的操作与实现流程,欢迎关注,交流心得!!!。

相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
9天前
|
运维 监控 Linux
BPF及Linux性能调试探索初探
BPF技术从最初的网络数据包过滤发展为强大的系统性能优化工具,无需修改内核代码即可实现实时监控、动态调整和精确分析。本文深入探讨BPF在Linux性能调试中的应用,介绍bpftune和BPF-tools等工具,并通过具体案例展示其优化效果。
38 14
|
1月前
|
缓存 NoSQL Linux
Linux调试
本文介绍了Linux调试、性能分析和追踪的培训资料,涵盖调试、性能分析和追踪的基础知识及常用工具。
235 6
Linux调试
|
20天前
|
NoSQL 编译器 C语言
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。高级技巧包括内存检查、性能分析和符号调试。通过实践案例学习如何有效定位和解决问题,同时注意保持耐心、合理利用工具、记录过程并避免过度调试,以提高编程能力和开发效率。
36 1
|
1月前
|
Linux Shell 数据库
文件查找是Linux用户日常工作的重要技能介绍了几种不常见的文件查找方法
文件查找是Linux用户日常工作的重要技能。本文介绍了几种不常见的文件查找方法,包括使用`find`和`column`组合、`locate`和`mlocate`快速查找、编写Shell脚本、使用现代工具`fd`、结合`grep`搜索文件内容,以及图形界面工具如`Gnome Search Tool`和`Albert`。这些方法能显著提升文件查找的效率和准确性。
51 2
|
2月前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
74 1
|
2月前
|
运维 安全 Linux
Linux文件清空的五种方法总结分享
每种方法各有优势,选择最合适的一种或几种,可以极大提高您的工作效率。更多有关Linux系统管理的技巧与资源,欢迎访问,持续提升您的运维技能。
82 1
|
2月前
|
Linux 数据安全/隐私保护 索引
linux inode索引节点使用率100% 解决+hustoj忘记密码+最新MDK注册方法
linux inode索引节点使用率100% 解决+hustoj忘记密码+最新MDK注册方法
48 1
|
4月前
|
NoSQL Linux C语言
Linux GDB 调试
Linux GDB 调试
68 10
|
4月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
128 3
|
4月前
|
NoSQL
技术分享:如何使用GDB调试不带调试信息的可执行程序
【8月更文挑战第27天】在软件开发和调试过程中,我们有时会遇到需要调试没有调试信息的可执行程序的情况。这可能是由于程序在编译时没有加入调试信息,或者调试信息被剥离了。然而,即使面对这样的挑战,GDB(GNU Debugger)仍然提供了一些方法和技术来帮助我们进行调试。以下将详细介绍如何使用GDB调试不带调试信息的可执行程序。
130 0
下一篇
DataWorks