手把手教你学会gdb,适应Linux调试环境

本文详述了如何使用gcc编译选项,包括预编译、编译、汇编、链接及宏定义,并重点介绍了gdb调试工具的常用指令,如启动、运行、断点设置、流程控制、显示变量等,旨在帮助开发者更好地适应Linux调试环境。
摘要由CSDN通过智能技术生成

在前文 基于vscode 打造Linux C++编码环境 一期中,讲解了如何基于vscode搭建Linux c++的编码环境,但是还没有讲解如何基于vscode搭建调试环境。本期,主要有两个任务:

  • 讲解常用的gcc编译选项
  • 讲解常用的gdb编译指令


常用gcc编译选项

深入了解C++系列中,我经常使用如下的格式进行编译、执行demo:

$ g++ -g -O0 main.cc -o main && ./main

下面,我们来看看常用的gcc编译选项有哪些。

选项 作用
-E 生成预处理文件
-S 生成汇编文件
-c 生成可目标文件
-o 指定生成文件的文件名
-On 指定代码优化等级
-g 用于gdb调试、objdump
-Wall 显示代码中的所有warning行为
-w 禁止显示代码中的warning行为
-Werror 将代码中的warning行为视为为error
-D 设置预定义宏
-l 链接(link)指定的函数库
-std=c++11 指定编译代码的C++标准为C++11

对于这些编译选项,简单的解释下。

-E-S-c 三个选项直接对应着编译的前三个基本阶段

预编译处理(.i)

将源文件main.cc 经过预处理后,生成文件预处理所得文件main.i

g++ -E main.cc -o main.i
编译、优化程序(.s)

main.i 文件翻译成一个汇编文件 main.s

g++ -S main.i  -o main.s
汇编程序(.o)

运行汇编器,将 main.s 翻译成一个可重定位目标文件 main.o

 g++ -c main.s -o main.o
链接程序(.elf)

运行链接器,将 main.o 中使用到的目标文件组合起来,并创建一个可执行的文件 main 。由于main.cc代码没有额外的依赖,因此可以直接输出main文件。

 g++ main.o -o main

实际上,一步就能完成上面所有的操作:

g++ main.cc -o main
定义宏 -D

比如,对于下面的一段demo,如果定义了宏DEBUG,则输出hello cpp

int main(int argc, char const *argv[]) {
   
#ifdef DEBUG
  std::cout<<"Hello Cpp" <<std::endl;
#endif
  return 0;
}

下面在gcc编译时基于-D选项设置DEBUG宏,来控制程序执行。

$ g++ -DDEBUG main.cc -o main && ./main
Hello Cpp

对于GCC的编译选项,没有必要全部记住,记住常用的即可,其他用到了再去官网查询:

https://gcc.gnu.org/onlinedocs/gcc/Invoking-GCC.html

常用gdb指令

本期主要讲解下我常用的gdb指令、以及怎么去学习gdb。希望能通过本期博客,能帮助你摆脱对gdb恐惧,并熟悉下gdb的常用指令,对于没有讲解到的指令,在本期之后,可以去官方网站自行学习,那里有着详细且为全面的介绍:

https://sourceware.org/gdb/current/onlinedocs/gdb/

为了方便后面基于gdb调试REDIS源码的讲解,可以先下载REDIS6.0的源码,并在编译代码的时候,加上-g -O0选项,生成调试信息。比如,我学习REDIS的时候,编译指令如下:

$ git clone https://github.com/redis/redis.git  # 下载redis源码
$ cd redis/src									# 进入源代码
$ make FLAGS="-g -O0"  -j 16    				# 编译
$ ./redis-server  								# 运行REDIS服务器

启动gdb

关于启动gdb的方式,下面介绍下常用的三种启动gdb方式:

  1. gdb [program]:这种方式最常用,比如使用gdb调试上面编译生成的main文件,那么就直接 gdb main
  2. gdb [program] core:用于调试导致coredump的错误,此时需要在program后面加上因为coredump生成的core文件路径。
  3. gdb -p [pid]:使用gdb调试正在运行的pid进程
gdb program

以如下的main程序为例:

// main.cc
#include <iostream>

int main(int argc, char const *argv[])
{
   
  int cnt =0;
  for(int idx=0; idx < 10; ++idx) {
    
    cnt++;
  }
  std::cout<<cnt<<std::endl;

  return 0;
}

编译指令:

$ g++ -g -O0 main.cc -o main

在终端输入gdb main,会从main文件中加载符号表,便于设置断点等信息:

$ gdb main
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 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.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
# 以上都是关于gdb的开源信息,为便于描述,下面的教程中会省略这部分信息
Reading symbols from main...
(gdb) 

输入gdb main后,会首先显示关于gdb的一大串的开源信息,而且每次启动都会显示。因此,在后文的讲解中,每次启动gdb会省略掉这部分信息。

attach pid

如果某个程序正在运行出现故障,比如服务器程序,无法被中止,如何使用gdb来调试它?

比如,此刻我电脑正在运行REDIS服务器程序,其pid是1607:

  • 我先以root权限启动gdb
  • 再使用attach pid命令来调试正在运行的REDIS服务器程序

示例如下:

$ sudo gdb							# 先以root权限启动gdb
# ...关于gdb的开源声明省略
(gdb) attach 1607					# 再使当前gdb环境去调试redis服务器
Attaching to process 1607
[New LWP 1608]
[New LWP 1609]
[New LWP 1610]
[New LWP 1611]
[New LWP 1612]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
0x00007f2d694925ce in epoll_wait (epfd=5, events=0x7f2d68ede980, maxevents=10128, timeout=100)
    at ../sysdeps/unix/sysv/linux/epoll_wait.c:30
30      in ../sysdeps/unix/sysv/linux/epoll_wait.c
(gdb) 

当使用attach命令调试完服务器程序,可以使用detach指令退出。

(gdb) detach 		
Detaching from program: /home/szza/redis-6.0.5/redis-6.0.5/src/redis-server, process 1607
[Inferior 1 (process 1607) detached]
gdb -p pid

当然,也可以直接使用gdb -p pid指令,来调试正在运行的REDIS服务器程序,其效果和attach一致:

$ sudo gdb -p 1607				# 也要使用root权限
Attaching to process 1607
[New LWP 1608]
[New LWP 1609]
[New LWP 1610]
[New LWP 1611]
[New LWP 1612]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
--Type <RET> for more, q to quit, c to continue without paging--
0x00007f2d694925ce in epoll_wait (epfd=5, events=0x7f2d68ede980, maxevents=10128, timeout=100)
    at ../sysdeps/unix/sysv/linux/epoll_wait.c:30
30      in ../sysdeps/unix/sysv/linux/epoll_wait.c

毫无疑问,这也是可以由detach命令,退出调试环境:

(gdb) detach 
Detaching from program: /home/szza/redis-6.0.5/redis-6.0.5/src/redis-server, process 1607
[Inferior 1 (process 1607) detached]

其他启动gdb的方式,可以参考官方文档:

https://sourceware.o
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值