作者:赵黎明,爱可生 MySQL DBA 团队成员,熟悉 Oracle、MySQL 等数据库,擅长数据库性能问题诊断、事务与锁问题的分析等,负责处理客户 MySQL 及我司自研 DMP 平台日常运维中的问题,对开源数据库相关技术非常感兴趣。
爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。
本文约 2000 字,预计阅读需要 10 分钟。
CGDB (Curses-based GDB)[1]:是一个基于文本界面的 GDB[2] 前端,主要用于在终端中提供更丰富的用户界面,CGDB 使用 Curses 库[3] 创建了一个简单的功能界面,帮助用户更方便地使用 GDB,它在 GDB 的基础上增加了一些功能,使得调试过程更加直观和高效。
CGDB 的运行依赖 GDB 环境,因此,在调试前必须先安装符合其版本要求的 GDB
简单来说,CGDB 是 GDB 的一个前端工具,通过提供更丰富的界面来增强 GDB 的用户体验。如果更喜欢在增强型终端中操作,可以使用 CGDB 来代替 GDB。
本次选择安装 gdb 9.2 的版本,原因主要有以下两个:
安装步骤:
-- 安装依赖
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
。
注意事项:
A compiler with support for C++11 language features is required
。安装步骤:
-- 安装依赖
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 的使用过程和效果。
查看 mysqld 的进程号,此处为 26238。
在 gdb 窗口执行 att 26238
,将其 attach 到 mysqld 进程上。
绿色箭头代表代码当前执行的位置,会展示代码所处行号,内存地址,代码文件等信息。
按 ESC 键,会进入上半部分的代码展示窗口,能像在 vim 中那样用快捷键上下移动光标进行查看。
如果要返回 gdb 的窗口,按 i 键即可,就能继续执行调试命令了。
根据打印的源码文件和位置,去官网代码库中找到对应的文件,再搜索相应的函数,就可以获取对应的源码内容了。
执行 info threads
,打印所有线程。
依次执行 thread
、bt
,查看当前线程及该线程的 backtrace。
多次执行 s
,一行一行地进行单步调试,注意调试期间 thread 是否发生变化。
当前为 ID 1 的线程,如果要切换到某个 thread,可以执行 thread [thread_id] 进行切换。
以下是 49 号线程打印的 backtrace 信息示例,可获取函数调用的顺序、调用的函数名、函数出现在源码文件中的位置。
采用此方式调试 mysqld 时,当其还未被 attach 到 mysqld 上时,并不会阻塞新的连接。
此时只能设置断点,查看某个函数在源码文件中的位置。
由于没有线程及其帧栈信息,并不能做进一步的调试。
当程序异常崩溃时,如果配置过 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
命令查看,它会输出所有信号量。
与之前先进入 cgdb 调试台,再执行 attach [pid]
的方式并无区别,后者会在 cgdb/gdb 进程中显示 mysqld 进程号。
要注意的是,这两种方式都会直接阻塞 mysql 客户端,因为此时 mysqld 会被阻塞,导致无法建立新的连接。
用 SIGSTOP/SIGCONT 的信号量来观测效果
Tips:信号量名中的 SIG 是可以被省略的,如:
kill -SIGSTOP [pid]
和kill -STOP [pid]
是等效的。
该方式可以在不影响已运行 mysqld 的基础上,对同版本的 mysqld 单独进行调试。
建议下载带 boost 的 MySQL 源码包,然后编译为 Debug 版本,可以打印更多的 debugging symbols 信息,方便调试。
当 MySQL 的连接数满导致无法登陆实例时,可以用 cgdb 来救急。
下图中,当客户端连接实例时报错:"Too many connections"
,直接用 cgdb/gdb 来调大 max_connections
参数的值。
如果服务器上有多个 mysqld 进程时,建议直接指定 pid,否则可能改到了另一个 MySQL 实例上。
coredump
文件,可通过 CGDB 去分析程序崩溃的原因,如:在特定场景下,在调用某个函数时触发了程序的 bug 而引发的崩溃。extra_port
方法之外,还可以用 CGDB 来解决。[1]
CGDB: https://cgdb.github.io/
[2]
GDB: https://sourceware.org/gdb/
[3]
curses-library: https://www.ibm.com/docs/zh/aix/7.3?topic=concepts-curses-library
本文关键字:#CGDB# #GDB# #调试工具# #源码#