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
目录
相关文章
|
8天前
|
Linux
Linux(5)WIFI/BT调试笔记
Linux(5)WIFI/BT调试笔记
26 0
|
3天前
|
网络协议 算法 Linux
【Linux】深入探索:Linux网络调试、追踪与优化
【Linux】深入探索:Linux网络调试、追踪与优化
|
21天前
|
关系型数据库 MySQL Linux
linux CentOS 7.4下 mysql5.7.20 密码改简单的方法
linux CentOS 7.4下 mysql5.7.20 密码改简单的方法
22 0
|
5天前
|
NoSQL Ubuntu 测试技术
【GDB自定义指令】core analyzer结合gdb的调试及自定义gdb指令详情
【GDB自定义指令】core analyzer结合gdb的调试及自定义gdb指令详情
8 1
|
5天前
|
NoSQL Ubuntu 开发工具
【gdb调试】在ubuntu环境使用gdb调试一棵四层二叉树的数据结构详解
【gdb调试】在ubuntu环境使用gdb调试一棵四层二叉树的数据结构详解
6 1
|
5天前
|
人工智能 Linux
Linux查找大文件的方法
Linux查找大文件的方法
|
8天前
|
Linux Android开发
Linux(6)CH9434 SPI调试笔记
Linux(6)CH9434 SPI调试笔记
14 0
|
21天前
|
Linux Shell 虚拟化
linux 部署docker容器虚拟化平台(二)--------docker 镜像制作方法
linux 部署docker容器虚拟化平台(二)--------docker 镜像制作方法
33 0
|
21天前
|
Ubuntu Java Linux
linux安装软件和卸载的几种方法
linux安装软件和卸载的几种方法
10 0
|
23天前
|
监控 Linux