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月前
|
Ubuntu 物联网 Linux
从零安装一个Linux操作系统几种方法,以Ubuntu18.04为例
一切就绪后,我们就可以安装操作系统了。当系统通过优盘引导起来之后,我们就可以看到跟虚拟机中一样的安装向导了。之后,大家按照虚拟机中的顺序安装即可。 好了,今天主要介绍了Ubuntu Server版操作系统的安装过程,关于如何使用该操作系统,及操作系统更深层的原理,还请关注本号及相关圈子。
|
8月前
|
缓存 监控 Linux
Linux系统清理缓存(buff/cache)的有效方法。
总结而言,在大多数情形下你不必担心Linux中buffer与cache占用过多内存在影响到其他程序运行;因为当程序请求更多内存在没有足够可用资源时,Linux会自行调整其占有量。只有当你明确知道当前环境与需求并希望立即回收这部分资源给即将运行重负载任务之前才考虑上述方法去主动干预。
2140 10
|
8月前
|
Ubuntu Linux 图形学
推广与体验Ubuntu Linux的便捷方法
如果你的朋友或家人对尝试Linux感兴趣,但希望在安装之前先体验一下,你可以分享以下链接给他们:Ubuntu在线导览。通过这个链接,他们可以在任何地方轻松体验Ubuntu,无需安装即可深入了解这个流行的操作系统。
|
8月前
|
XML 缓存 Linux
在Linux环境下解决Visual Studio Code字体显示异常和字体替换方法。
解决Linux下VS Code字体显示异常,需要对Linux字体渲染机制有所理解,并对VS Code的配置选项进行合理设置。替换字体时则要通过系统字体配置或VS Code设置来完成。通过上述方法,可以有效地解决字体显示问题,从而提升代码编辑的视觉体验。
1178 0
|
9月前
|
网络协议 Ubuntu Linux
Wireguard in Linux的安装方法
本文介绍了如何在Ubuntu和Rocky Linux中安装配置WireGuard,并探讨了配置过程中可能出现的DNS泄露问题及解决方法,包括通过nmtui设置DNS及调整DNS优先级参数。
|
NoSQL 搜索推荐 openCL
【C/C++ 调试 GDB指南 】gdb调试基本操作
【C/C++ 调试 GDB指南 】gdb调试基本操作
1056 2
|
NoSQL 编译器 C语言
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。高级技巧包括内存检查、性能分析和符号调试。通过实践案例学习如何有效定位和解决问题,同时注意保持耐心、合理利用工具、记录过程并避免过度调试,以提高编程能力和开发效率。
570 1
|
NoSQL Linux C语言
Linux GDB 调试
Linux GDB 调试
289 10
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
1089 3
|
NoSQL Linux C语言
Linux gdb调试的时候没有对应的c调试信息库怎么办?
Linux gdb调试的时候没有对应的c调试信息库怎么办?
275 1