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 的版本,原因主要有以下两个:
- CentOS 7.5 中自带 gdb 的版本 7.6.1-120.el7,而 cgdb 要求 gdb 版本为 7.12 及以上。
- 安装 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
,打印所有线程。
依次执行 thread
、bt
,查看当前线程及该线程的 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 直接进行调试。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。