1

CGDB 是 GDB 的一个前端工具,通过提供更丰富的界面来增强 GDB 的用户体验。如果更喜欢在增强型终端中操作,可以使用 CGDB 来代替 GDB。

作者:赵黎明,爱可生 MySQL DBA 团队成员,熟悉 Oracle、MySQL 等数据库,擅长数据库性能问题诊断、事务与锁问题的分析等,负责处理客户 MySQL 及我司自研 DMP 平台日常运维中的问题,对开源数据库相关技术非常感兴趣。
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。

本文约 2000 字,预计阅读需要 10 分钟。

简介

CGDB (Curses-based GDB):是一个基于文本界面的 GDB 前端,主要用于在终端中提供更丰富的用户界面,CGDB 使用 Curses 库 创建了一个简单的功能界面,帮助用户更方便地使用 GDB,它在 GDB 的基础上增加了一些功能,使得调试过程更加直观和高效。

CGDB 的运行依赖 GDB 环境,因此,在调试前必须先安装符合其版本要求的 GDB

简单来说,CGDB 是 GDB 的一个前端工具,通过提供更丰富的界面来增强 GDB 的用户体验。如果更喜欢在增强型终端中操作,可以使用 CGDB 来代替 GDB。

版本选择

本次选择安装 gdb 9.2 的版本,原因主要有以下两个:

  1. CentOS 7.5 中自带 gdb 的版本 7.6.1-120.el7,而 cgdb 要求 gdb 版本为 7.12 及以上。
  2. 安装 gdb 9.0 以上版本的,还可以用于调试 OBServer,否则会报版本错误。

安装 CGDB

安装步骤:

-- 安装依赖
yum -y install automake flex texinfo ncurses-devel readline-devel gcc-c++

-- 下载源码包
git clone https://github.com/cgdb/cgdb.git

-- 编译源码
cd cgdb
./autogen.sh
./configure --prefix=/usr/local
make && make install

如果在执行 make 时报错:error: ‘for’ loop initial declarations are only allowed in C99 mode,可在进行编译配置时加上参数:CFLAGS="-std=c99",如:CFLAGS="-std=c99" ./configure --prefix=/usr/local

安装 GDB

注意事项:

  • 尽量不装 10.x 及以上的高版本。可能会报错:A compiler with support for C++11 language features is required
  • CentOS 7.5 默认的 gcc 版本较低(4.8.5),原则上只要够用就行,没必要追求高版本。

安装步骤:

-- 安装依赖
yum -y install gcc gcc-c++ texinfo

-- 下载源码包
wget ftp://ftp.gnu.org/gnu/gdb/gdb-9.2.tar.gz

-- 解压并编译
tar zxf gdb-9.2.tar.gz -C /tmp
cd /tmp/gdb-9.2
mkdir build && cd build
/tmp/gdb-9.2/configure
make && make install

查看 gdb 的版本,确认是否升级成功。

执行 cgdb,进入调试界面。

查看帮助

  • 键入 help + 回车键,可查看所有的 gdb 的指令和说明
  • 键入 ESC + :help + 回车键,可查看所有 cgdb 的指令和说明

具体指令和说明不在文中展示。

下面我们通过几个常用的场景示例,演示 CGDB 和 GDB 的使用过程和效果。

调试示例

示例 1:调试 MySQL 获取源码

查看 mysqld 的进程号,此处为 26238。

在 gdb 窗口执行 att 26238,将其 attach 到 mysqld 进程上。

绿色箭头代表代码当前执行的位置,会展示代码所处行号,内存地址,代码文件等信息。

按 ESC 键,会进入上半部分的代码展示窗口,能像在 vim 中那样用快捷键上下移动光标进行查看。

如果要返回 gdb 的窗口,按 i 键即可,就能继续执行调试命令了。

根据打印的源码文件和位置,去官网代码库中找到对应的文件,再搜索相应的函数,就可以获取对应的源码内容了。

示例 2:调试 MySQL 线程

执行 info threads,打印所有线程。

依次执行 threadbt,查看当前线程及该线程的 backtrace。

多次执行 s,一行一行地进行单步调试,注意调试期间 thread 是否发生变化。

当前为 ID 1 的线程,如果要切换到某个 thread,可以执行 thread [thread_id] 进行切换。


以下是 49 号线程打印的 backtrace 信息示例,可获取函数调用的顺序、调用的函数名、函数出现在源码文件中的位置。

示例 3:使用 cgdb ./mysqld 调试

采用此方式调试 mysqld 时,当其还未被 attach 到 mysqld 上时,并不会阻塞新的连接。

此时只能设置断点,查看某个函数在源码文件中的位置。

由于没有线程及其帧栈信息,并不能做进一步的调试。

示例 4:分析 coredump 文件

当程序异常崩溃时,如果配置过 coredump,就可以通过分析 coredump 文件来排查程序崩溃的原因。

第一个 coredump,是通过执行 kill -SIGSEGV [pid] (也就是 signal 11)将 mysqld 进程杀死后产生的,其实从文件命名上就可获知,mysqld-11 后面紧跟的这个 11,就是对应信号量的编号。

在 cgdb 中也打印了 mysqld 崩溃的原因,是收到了 SIGSEGV(11) 的信号量,即最常见的 Segmentaion fault

第二个 coredump,是在用 cgdb 调试时生成的,期间执行过 run 命令,将 mysqld 进程重启过,产生了 mysqld-5 的 coredump 文件。

在 cgdb 中也打印了 mysqld 崩溃的原因,是收到了 SIGTRAP(5) 的信号量。

如果对信号量不太熟悉,可用 kill -l 命令查看,它会输出所有信号量。

示例 5:使用 cgdb -p 调试

与之前先进入 cgdb 调试台,再执行 attach [pid] 的方式并无区别,后者会在 cgdb/gdb 进程中显示 mysqld 进程号。

要注意的是,这两种方式都会直接阻塞 mysql 客户端,因为此时 mysqld 会被阻塞,导致无法建立新的连接。

用 SIGSTOP/SIGCONT 的信号量来观测效果

Tips:信号量名中的 SIG 是可以被省略的,如:kill -SIGSTOP [pid]kill -STOP [pid] 是等效的。

示例 6:单独起一个 mysqld 调试

该方式可以在不影响已运行 mysqld 的基础上,对同版本的 mysqld 单独进行调试。

建议下载带 boost 的 MySQL 源码包,然后编译为 Debug 版本,可以打印更多的 debugging symbols 信息,方便调试。

示例 7:修改 MySQL 最大连接数

当 MySQL 的连接数满导致无法登陆实例时,可以用 cgdb 来救急。

下图中,当客户端连接实例时报错:"Too many connections",直接用 cgdb/gdb 来调大 max_connections 参数的值。

如果服务器上有多个 mysqld 进程时,建议直接指定 pid,否则可能改到了另一个 MySQL 实例上。

用 cgdb 修改

用 gdb 修改

总结

  • 本文简单介绍了 CGDB 及其基本使用方法。
  • 利用 CGDB 调试工具,能帮助我们梳理程序在运行时各种函数的调用逻辑,这对于学习和研究程序源码非常有帮助。
  • 当程序崩溃时,如果能拿到故障现场的 coredump 文件,可通过 CGDB 去分析程序崩溃的原因,如:在特定场景下,在调用某个函数时触发了程序的 bug 而引发的崩溃。
  • 当 MySQL 连接数被打满后,除了我们已知的 extra_port 方法之外,还可以用 CGDB 来解决。
  • 注意,生产环境严禁使用 CGDB 直接进行调试。

爱可生开源社区
426 声望209 粉丝

成立于 2017 年,以开源高质量的运维工具、日常分享技术干货内容、持续的全国性的社区活动为社区己任;目前开源的产品有:SQL审核工具 SQLE,分布式中间件 DBLE、数据传输组件DTLE。