调试步骤
KeyDB 的开发非常注重稳定性:我们在每个版本中都尽最大努力,以确保您能体验到一个非常稳定的产品,并且不会出现崩溃。然而,即使我们尽了最大努力,也不可能 100% 成功地避免所有关键性错误。
当 KeyDB 崩溃时,它会生成一份关于所发生情况的详细报告。然而,有时仅查看崩溃报告是不够的,KeyDB 核心团队也无法独立复现问题。在这种情况下,我们需要能够复现问题的用户的帮助。
这篇简短的指南将展示如何使用 GDB 来提供 KeyDB 开发者更轻松地追踪错误所需的所有信息。
#
什么是 GDB?GDB 是 Gnu Debugger(Gnu 调试器)的缩写:它是一个能够检查另一个程序内部状态的程序。通常,追踪和修复错误就是在错误发生时收集更多关于程序状态的信息,因此 GDB 是一个非常有用的工具。
GDB 有两种使用方式:
- 它可以附加到一个正在运行的程序上,并在运行时检查其状态。
- 它可以使用所谓的核心文件(core file)来检查一个已经终止的程序的状态,该文件是程序运行时内存的映像。
从调查 KeyDB 错误的角度来看,我们需要同时使用这两种 GDB 模式:能够复现错误的用户将 GDB 附加到他们正在运行的 KeyDB 实例上,当崩溃发生时,他们创建 core
文件,然后开发者将使用这个文件来检查崩溃时 KeyDB 的内部状态。
通过这种方式,开发者可以在自己的计算机上进行所有检查,而无需用户的帮助,用户则可以自由地在生产环境中重启 KeyDB。
#
在无优化的情况下编译 KeyDB默认情况下,KeyDB 是使用 -O2
开关编译的,这意味着启用了编译器优化。这使得 KeyDB 可执行文件更快,但同时也使 KeyDB(像任何其他程序一样)更难使用 GDB 进行检查。
最好将 GDB 附加到使用 make noopt
命令(而不是仅使用普通的 make
命令)编译的、无优化的 KeyDB 上。但是,如果您在生产环境中已经有一个正在运行的 KeyDB,并且重新编译和重启会给您带来问题,那么就没有必要这样做。即使效果稍差,GDB 仍然可以用于经过优化编译的可执行文件。
如果您能在第一次崩溃后,确保使用 make noopt
重新编译 KeyDB,那就太好了,这样下次追踪问题就会更简单。
您不必担心在无优化情况下编译 KeyDB 会导致性能下降,这很不可能在您的环境中引起问题,因为它通常只涉及很小的百分比,因为 KeyDB 并不是非常受 CPU 限制的(它为了处理查询会进行大量的 I/O 操作)。
#
将 GDB 附加到正在运行的进程如果您已经有一个正在运行的 KeyDB 服务器,您可以将 GDB 附加到它上面,这样如果 KeyDB 崩溃,就可以检查其内部状态并生成一个 core dump
文件。
将 GDB 附加到 KeyDB 进程后,它将像往常一样继续运行,不会有任何性能损失,所以这是一个安全的操作。
为了附加 GDB,您首先需要正在运行的 KeyDB 实例的进程 ID(即进程的 pid)。您可以使用 keydb-cli
轻松获取它:
在上面的例子中,进程 ID 是 58414。
登录到您的 KeyDB 服务器。
(可选但推荐)启动 screen 或 tmux 或任何其他程序,以确保您的 GDB 会话不会因为 ssh 连接超时而关闭。如果您不知道 screen 是什么,帮自己一个忙,阅读这篇文章。
通过输入以下命令将 GDB 附加到正在运行的 KeyDB 服务器:
gdb
<path-to-keydb-executable>
<pid>
例如:gdb /usr/local/bin/keydb-server 58414
GDB 将启动并附加到正在运行的服务器,并打印出类似下面的内容:
此时 GDB 已附加,但您的 KeyDB 实例被 GDB 阻塞了。为了让 KeyDB 实例继续执行,只需在 GDB 提示符下输入 continue,然后按回车键。
完成!现在您的 KeyDB 实例已附加了 GDB。您可以等待……下一次崩溃了 :)
现在是时候分离您的 screen / tmux 会话了,如果您正在使用它运行 GDB,请按通常的 Ctrl-a a 组合键。
#
崩溃之后KeyDB 有一个命令可以用来模拟分段错误(换句话说,就是一次严重的崩溃),即使用 DEBUG SEGFAULT
命令(当然,不要在真实的生产实例上使用它;)。我将使用这个命令来使我的实例崩溃,以展示在 GDB 侧会发生什么:
如您所见,GDB 检测到 KeyDB 崩溃了,并且甚至能向我显示导致崩溃的文件名和行号。这已经比 KeyDB 崩溃报告的回溯信息(只包含函数名和二进制偏移量)要好得多了。
#
获取堆栈跟踪首先要做的是用 GDB 获取完整的堆栈跟踪。这就像使用 bt 命令一样简单:(它是 backtrace 的缩写)
这显示了回溯信息,但我们还想使用 info registers 命令转储处理器寄存器:
请确保在您的错误报告中包含这两种输出。
#
获取核心文件下一步是生成核心转储文件(core dump),即正在运行的 KeyDB 进程的内存映像。这是通过 gcore
命令完成的:
现在您有了可以发送给 KeyDB 开发者的核心转储文件,但重要的是要明白,这个文件恰好包含了 KeyDB 实例在崩溃时内部的所有数据;KeyDB 开发者将确保不与任何其他人分享其内容,并会在不再用于调试目的后立即删除该文件,但请注意,通过发送核心文件,您也在发送您的数据。
#
要发送给开发者的内容最后,您可以将所有东西发送给 KeyDB 核心团队:
- 您正在使用的 KeyDB 可执行文件。
- 由 bt 命令生成的堆栈跟踪,以及寄存器转储。
- 您用 gdb 生成的核心文件。
- 关于您正在使用的操作系统、GCC 版本以及 KeyDB 版本的信息。