Linux GDB 调试指南

课程亮点

  • 三个维度介绍 GDB 调试中的技巧和注意事项
  • 以时下流行的内存数据库 Redis 为示例对象
  • 梳理开源软件项目结构和分析源码思路

课程背景

调试是程序开发和排障很重要的一个环节,对于从事 Linux C/C++ 后台开发的读者来说,GDB 调试是需要熟练掌握的一项技能。

有些读者可能想学习像 Redis、Apache Web Server、Nginx 等以 C/C++ 为主要开发语言的开源项目,但是不知道如何下手,繁多的代码很快让新手迷失方向,最终放弃;或者使用某个开源软件时,总是提示某个错误,这些错误让很多人如临大敌。学会 GDB 去调试、分析它们的执行脉络乃至整个项目的框架是解决这类困惑很好的一个方法

作者介绍

范蠡,资深开发工程师,担任过 C++ 客户端和服务器端开发主程,先后做过互联网金融交易系统、即时通讯、游戏服务器、音视频直播服务器等项目,目前在某大型互联网公司任开发经理一职。公众号:高性能服务器开发。

课程大纲

enter image description here

课程内容

开篇词:Linux C/C++ 开发人员要熟练掌握 GDB 调试

大家好,我是范蠡,目前在某知名互联网旅游公司基础框架业务部技术专家组任开发经理一职。

本系列课程的主题是 Linux 后台开发的 C/C++ 调试,通俗地说就是 GDB 调试。GDB(GNU Debugger)是类 Unix(如 Linux)操作系统下的一款开源的 C/C++ 程序调试器。最初是在 1988 年由理查德 · 马修 · 斯托曼(Richard Stallman)所撰写,之后以 GNU 通用公共许可证(GNU General Public License,GNU GPL)的许可方式将软件发布,因此 GNU Debugger 是一套自由软件。发布后的 1990 年-1993 年间则由任职于 Cygnus Solutions 公司的约翰 · 吉尔摩(John Gilmore)负责后续的软件维护工作,点击这里了解其历史详情

0.1 为什么要写这个课程

我从学生时代到进入软件开发这个行业,不知不觉已经十余年了,真是时光荏苒、光阴如梭。这些年,先后在网游公司做过游戏服务器,为上海某交易所做过金融交易系统、在金融证券公司做过股票证券交易系统和即时通讯软件、在音视频直播公司做过直播服务器,各种项目使用的服务器操作系统都是 Linux,开发语言是 C/C++ 。正如从事 Windows C/C++ 开发的一定要熟悉 Visual Studio、从事 Java 开发的要熟悉 Eclipse 或 IntelliJ IDEA、从事 Android 开发的要熟悉 Android Studio、从事 iOS 开发的要熟悉 XCode 一样,从事 Linux C/C++ 开发要熟悉 GDB。

虽然 Linux 系统下读者编写 C/C++ 代码的 IDE 可以自由选择,但是调试生成的 C/C++ 程序一定是直接或者间接使用 GDB。可以毫不夸张地说,我所做这些项目的开发和调试包括故障排查都是利用 GDB 完成的,调试是开发流程中一个非常重要的环节,因此对于从事 Linux C/C++ 的开发人员熟练使用 GDB 调试是一项基本要求

当每次有人来面试时,我会问他熟悉哪些开发工具,有些面试者对各种开发工具都不熟悉,我猜想这类应聘者应该是基本“不写代码”或者“不会写代码”的,当然面试结果也可想而知了。“工欲善其事、必先利其器”,作为一名合格的软件开发者,至少得熟悉一种软件开发工具和调试器, 而对于 Linux C/C++ 后台开发,舍 GDB 其谁。

一些初中级开发者可能想通过阅读一些优秀的开源项目来提高自己的编码水平,但是只阅读代码,不容易找到要点,或者误解程序的执行逻辑,最终迷失方向。如果能实际利用调试器去把某个开源项目调试一遍,学习效果才能更好。站在 Linux C/C++ 后台开发的角度来说,学会了 GDB 调试,就可以对各种 C/C++ 开源项目(如 Redis、Apache、Nginx 等)游刃有余。简而言之,GDB 调试是学习这些优秀开源项目的一把钥匙。下面举两个基本的例子。

  • 例子一,对于 C/C++ 这门存在多态的特性的编程语言,当用一个基类对象类型指向其子类实例,然后调用在子类中被重写(Override)的某个方法,在复杂的代码中我们很难通过肉眼去梳理清楚程序实际的执行脉络,而使用 GDB 调试的 step 命令,可以很方便地追踪程序的实际执行流。
  • 例子二,使用 GDB 可以快速方便地了解一个程序的整体结构,在分析一个开源软件的整体结构时,可以使用 GDB 先将程序跑起来,然后中断下来,使用 GDB 的 info threadbacktrace 命令分析程序的线程数目和调用上下文。在后面内容中,我们使用 GDB 分析 redis-server 网络通信模块的基本结构也是这个思路。

还有一类情况,那就是在实际开发中总会遇到一些非常诡异的程序异常或者问题崩溃的情况。解决这些问题很重要的一个方法就是调试,而对于 Linux C/C++ 服务来说,熟悉 GDB 各种高级调试技巧非常重要。举个例子,对于大多数由内存访问冲突导致的程序崩溃,我们都可以使用 GDB 分析产生的 core 文件来定位问题原因;另外对于一些出现几率比较低,比如一万次操作可能只会出现一个,我们可以在不修改程序代码的前提下,使用 GDB 条件断点的功能设置触发条件,最终定位和解决问题。

0.2 课程特色

  • 本课程不是人云亦云地介绍 GDB 的各个命令的用法,而是从 What、How、Why 三个角度来介绍 GDB 调试中的技巧和注意事项;所引用的示例也不是“helloworld”式的 demo,而是以时下最流行的内存数据库 Redis 为示例对象。
  • 整个课程循序渐进,从基础的调试符号原理,到 GDB 启动调试的方式,再到常用命令详解,接着介绍 GDB 高级调试技巧和一些更强大的 GDB 扩展工具,最后使用 GDB 带领读者分析 Redis 服务器端和客户端的网络通信模块。
  • 在这个课程中读者不仅可以学习到实实在在的调试技巧和网络编程知识,也可以学习到如何梳理开源软件项目结构和源码分析思路

0.3 课程介绍

调试是开发过程中一项不可或缺的工作,在 Linux 编程中,通常使用 GDB 来调试 C/C++ 程序,一个合格的 Linux C/C++ 开发人员,必须熟练使用 GDB 调试。互联网上有太多关于 GDB 的教程,基本上是人云亦云,我在 Linux 服务器开发方面从业多年,本课程将介绍 GDB 调试最常用的知识和技巧,还有一些重点和难点。

d56c9d30-073f-11e9-9e09-3de450fa9701

本课程分为五大部分,共计 19 篇(含开篇词)。

  • 第一部分(第01 ~ 03课)主要介绍了 Linux C/C++ 调试的一些基础知识。例如,core 文件的基础知识、GDB 调试启动进程的方法。
  • 第二部分(第04 ~ 06课)详细介绍了 GDB 常用调试命令的使用和注意事项,这里不是孤零零地介绍每个命令的使用,而是结合具体的使用实例来说明。

如果认真学习完并掌握第一部分和第二部分内容后,然后一边调试一边学习 Linux 下开源的 C/C++ 项目应该就不成问题了。

  • 第三部分(第07 ~ 08课)介绍了我在实际工作中开发 Linux 后台 C/C++ 服务时,利用 GDB 调试和排查问题使用的一些高级实用技巧,自成一套体系,如果读者仅对这部分内容感兴趣也可以单独阅读相关内容。
  • 第四部分(第09 ~ 12课)介绍了基于 GDB 的一些相关的升级版调试工具,主要内容是 CGDBVisualGDBCGDB 是基于 GDB 的一款可以在调试过程中以图形化界面显示和跳转源码的开源工具,为很多觉得 GDB 自带的 list 显示源码不方便的开发者带来更好的调试体验;而 VisualGDB 是 Visual Studio 的一个插件,其基于 SSH 协议和 Samba Server,有了它,读者可以利用 Visual Studio 这一强大的图形化 IDE 远程调试 Linux 机器上的程序,不必再频繁地输入各种 GDB 命令,这也为广大 Windows C/C++ 开发者想学习 Linux C/C++ 开发带来很大的方便。
  • 第五部分(第13 ~ 18课)的内容是一个完整、详尽的 GDB 实例教程,通过 GDB 调试来带领读者学习时下最流行的内存数据库 Redis 的网络通信模块代码。学完这部分内容不仅可以进一步熟悉 GDB 调试,同时还可以学习到 Redis 和网络编程相关知识。

在选择哪些程序作为 GDB 调试的教学程序时,着实让我纠结了很久。很多 GDB 教程都是人为的“造”一些实例代码,我感觉这样做有点脱离实际生产。实际项目中的代码与这种“造”出来的代码相差很大,同时程序很难调;不过实际项目中的代码,由于企业单位的保密协议要求,也不方便外传。经过慎重考虑,课程舍弃了我自己编写的几个实例代码,最终选择带领读者调试 Redis 代码。

0.4 课程寄语

本课程所讨论的知识和内容都来源于实际开发,尽量避免人云亦云和闭门造车,课程中也详细地给出了每一步的操作步骤。因此,希望广大读者能跟随课程介绍,在自己的环境中实际演练一遍,这样学习的效果会更好。当你掌握了 GDB 调试的精髓以后,将有能力进一步研究其他一些优秀的开源 Linux C/C++ 项目(如 Apache、Nginx)

最后,预祝大家学习愉快!

第01课:调试信息与调试原理

Linux 下 C++ 程序开发,即使使用 makefile、cmake 等编译工具,其最终都是调用 gcc 这一编译工具组的。这里说的工具组,是因为编译 C 程序和 C++ 程序使用的编译工具还是有一点差别的,一般编译 C 程序使用 gcc,编译 C++ 程序使用 g++。(下面为了叙述方便,除非特别说明,否则不做具体区分是 gcc 还是 g++,统一使用 gcc 这一名词代指。)

本课程中我使用的操作系统是 CentOS 7.0,为了演示方便直接使用的 root 账号演示。如果读者的机器上没有安装 gcc 和 gdb,可以使用 yum 命令安装一下。

# 安装 gccyum install gcc# 安装 g++yum install gcc-c++# 安装 gdbyum install gdb

一般要调试某个程序,为了能清晰地看到调试的每一行代码、调用的堆栈信息、变量名和函数名等信息,需要调试程序含有调试符号信息。使用 gcc 编译程序时,如果加上 -g 选项即可在编译后的程序中保留调试符号信息。举个例子,以下命令将生成一个带调试信息的程序 hello_server。

gcc -g -o hello_server hello_server.c

那么如何判断 hello_server 是否带有调试信息呢?我们使用 gdb 来调试一下这个程序,gdb 会显示正确读取到该程序的调试信息,在打开的 Linux Shell 终端输入 gdb hello_server 查看显示结果即可:

[root@localhost testclient]# gdb hello_serverGNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7_4.1Copyright (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 /root/testclient/hello_server...done.(gdb)

gdb 加载成功以后,会显示如下信息:

Reading symbols from /root/testclient/hello_server...done.

即读取符号文件完毕,说明该程序含有调试信息。我们不加 -g 选项再试试:

[root@localhost testclient]# gcc -o hello_server2 hello_server.c[root@localhost testclient]# gdb hello_server2GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7_4.1Copyright (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 /root/testclient/hello_server2...(no debugging symbols found)...done.(gdb)

细心的读者应该看出差别了,不加 -g 选项用 gdb 调试生成 hello_server 2 程序时,读取调试符号信息时会收到如下提示:

Reading symbols from /root/testclient/hello_server2...(no debugging symbols found)...done.

顺便提一下,除了不加 -g 选项,也可以使用 Linux 的 strip 命令移除掉某个程序中的调试信息,我们这里对 hello_server 使用 strip 命令试试:

[root@localhost testclient]# strip hello_server##使用 strip 命令之前-rwxr-xr-x. 1 root root 12416 Sep 8 09:45 hello_server##使用 strip 命令之后-rwxr-xr-x. 1 root root 6312 Sep 8 09:55 hello_server

可以发现,对 hello_server 使用 strip 命令之后,这个程序明显变小了(由 12416 个字节减少为 6312 个字节)。我们通常会在程序测试没问题以后,将其发布到生产环境或者正式环境中,因此生成不带调试符号信息的程序,以减小程序体积或提高程序执行效率。

我们再用 gdb 验证一下这个程序的调试信息确实被移除了:

[root@localhost testclient]# gdb hello_serverGNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-100.el7_4.1Copyright (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 /root/testclient/hello_server...(no debugging symbols found)...done.(gdb)

另外补充两点说明:

  • 本课程里虽然以 gcc 为例,但 -g 选项实际上同样也适用于使用 makefile 、cmake 等工具编译生成的 Linux 程序。
  • 在实际生成调试程序时,一般不仅要加上 -g 选项,也建议关闭编译器的程序优化选项。编译器的程序优化选项一般有五个级别,从 O0 ~ O4 ( 注意第一个 O0 ,是字母 O 加上数字 0 ), O0 表示不优化,从 O1 ~ O4 优化级别越来越高,O4 最高。这样做的目的是为了调试的时候,符号文件显示的调试变量等能与源代码完全对应起来。举个例子,假设有以下代码:
  int func()  {      int a = 1;      int b = a + 1;      int c = a + b;      return a + b + c;  }  int main()  {      int a = func();      printf("%d\n", a);  }

此代码中,由于在 main() 函数中调用了 func() 函数,而 func() 函数值可以在编译期间直接算出来,如果开启了优化选项,在实际调试的时候,这个函数中的局部变量 a 、 b 、c ,被直接的值取而代之(即编译器计算得出的值, a 直接使用 1,b 直接使用 2,c 直接使用 3,不会产生通过 a 计算 b,通过 a、b 计算 c 的指令),甚至连函数 func() 也可能被优化掉。如果出现这种情况,调试显示的代码和实际代码可能就会有差异了,这会给排查问题带来困难。当然,上面说的优化现象是否一定会出现,不同版本的编译器可能会有不同的行为。总之一句话,建议生成调试文件时关闭编译器优化选项

小结

本节课主要介绍了 gdb 等工具的安装以及调试符号对于调试的重要性,同时也要注意编译器优化选项对调试正确性的影响。

第02课:启动 GDB 调试
第03课:GDB 常用的调试命令概览
第04课:使用 VS 管理开源项目
第05课:GDB 常用命令详解(上)
第06课:GDB 常用命令详解(中)
第07课:GDB 常用命令详解(下)
第08课:GDB 实用调试技巧( 上)
第09课:GDB 实用调试技巧(下)
第10课:自定义 GDB 调试命令
第11课:GDB TUI——在 GDB 中显示程序源码
第12课:GDB 高级扩展工具——CGDB
第13课:Windows 系统调试 Linux 程序——VisualGDB
第14课:[实战] 调试 Redis 准备工作
第15课:[实战] Redis 网络通信模块源码分析(1)
第16课:[实战] Redis 网络通信模块源码分析(2)
第17课:[实战] Redis 网络通信模块源码分析(3)
第18课:[实战] redis-server 定时器实现
第19课:[实战] Redis 通信协议

阅读全文: http://gitbook.cn/gitchat/column/5c0e149eedba1b683458fd5f

相关资源:GDB调试工具指南.pdf
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页