文档:调试

Valkey 的开发重点是稳定性。我们尽力确保每个版本都能为您带来稳定的产品,避免崩溃。但是,如果您需要调试 Valkey 进程本身,请继续阅读。

当 Valkey 崩溃时,它会生成一份详细的事件报告。然而,有时仅仅查看崩溃报告是不够的,Valkey 核心团队也无法独立重现问题。在这种情况下,我们需要能够重现问题的用户提供帮助。

本指南将展示如何使用 GDB 提供 Valkey 开发人员更轻松地跟踪错误所需的信息。

什么是 GDB?

GDB 是 Gnu 调试器:一个能够检查另一个程序内部状态的程序。通常,跟踪和修复错误就是收集有关错误发生时程序状态的更多信息,因此 GDB 是一个极其有用的工具。

GDB 可以通过两种方式使用

  • 它可以附加到正在运行的程序,并在运行时检查其状态。
  • 它可以检查已终止程序的状体,方法是使用所谓的核心文件,即程序运行时的内存映像。

从调查 Valkey 错误的视角来看,我们需要使用 GDB 的这两种模式。能够重现错误的用户将 GDB 附加到他们正在运行的 Valkey 实例,当崩溃发生时,他们会创建 core 文件,开发人员将使用该文件来检查 Valkey 在崩溃时的内部状态。

这样,开发人员就可以在自己的电脑上执行所有检查,而无需用户的帮助,用户也可以自由地在其生产环境中重新启动 Valkey。

不带优化编译 Valkey

默认情况下,Valkey 使用 -O3 优化标志进行编译,该标志启用了高级别的编译器优化,旨在最大限度地提高运行时性能。Valkey 默认也使用 -fno-omit-frame-pointer 标志进行编译,确保在函数调用中保留帧指针。这种组合允许精确的堆栈遍历和调用堆栈跟踪,这对于调试至关重要。

最好使用 make noopt 命令(而不是仅仅使用普通的 make 命令)将 GDB 附加到不带优化编译的 Valkey。但是,如果您在生产环境中已经运行了 Valkey,并且重新编译和重新启动会给您带来问题,那么就没有必要这样做。GDB 仍然可以对带优化编译的可执行文件进行操作。

您不应该过分担心不带优化编译 Valkey 造成的性能损失。由于 Valkey 并非是 CPU 密集型应用,因此这不太可能在您的环境中造成问题。

将 GDB 附加到运行中的进程

如果您已经运行了一个 Valkey 服务器,您可以将 GDB 附加到它,这样如果 Valkey 崩溃,就可以检查其内部并生成一个 core dump 文件。

将 GDB 附加到 Valkey 进程后,它将照常运行,不会有任何性能损失,因此这不是一个危险的操作。

为了附加 GDB,您首先需要运行中的 Valkey 实例的进程 ID(进程的 pid)。您可以使用 valkey-cli 轻松获取它

$ valkey-cli info | grep process_id
process_id:58414

在上面的示例中,进程 ID 为 58414

登录到您的 Valkey 服务器。

(可选但推荐)启动 screentmux 或任何其他程序,以确保如果您的 ssh 连接超时,您的 GDB 会话不会关闭。您可以在这篇文章中了解更多关于 screen 的信息。

通过输入以下命令将 GDB 附加到正在运行的 Valkey 服务器

$ gdb <path-to-valkey-executable> <pid>

例如

$ gdb /usr/local/bin/valkey-server 58414

GDB 将启动并附加到正在运行的服务器,并打印类似以下内容

Reading symbols for shared libraries + done
0x00007fff8d4797e6 in epoll_wait ()
(gdb)

此时 GDB 已附加,但 您的 Valkey 实例被 GDB 阻塞。为了让 Valkey 实例继续执行,只需在 GDB 提示符下输入 continue,然后按回车键。

(gdb) continue
Continuing.

完成!现在您的 Valkey 实例已附加 GDB。现在您可以等待下一次崩溃了。:)

如果您正在使用 screen/tmux 运行 GDB,现在是时候通过按下 Ctrl-a a 组合键来分离您的 screen/tmux 会话了。

崩溃后

Valkey 有一个命令可以模拟段错误(换句话说就是严重的崩溃),即使用 DEBUG SEGFAULT 命令(当然不要在真实的生产实例上使用它!)。因此,我将使用此命令来使我的实例崩溃,以显示在 GDB 侧会发生什么

(gdb) continue
Continuing.

Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0xffffffffffffffff
debugCommand (c=0x7ffc32005000) at debug.c:220
220         *((char*)-1) = 'x';

如您所见,GDB 检测到 Valkey 崩溃,甚至能够显示导致崩溃的文件名和行号。这已经比 Valkey 崩溃报告的回溯(仅包含函数名和二进制偏移量)好得多。

获取堆栈跟踪

第一件事是用 GDB 获取完整的堆栈跟踪。这就像使用 bt 命令一样简单

(gdb) bt
#0  debugCommand (c=0x7ffc32005000) at debug.c:220
#1  0x000000010d246d63 in call (c=0x7ffc32005000) at valkey.c:1163
#2  0x000000010d247290 in processCommand (c=0x7ffc32005000) at valkey.c:1305
#3  0x000000010d251660 in processInputBuffer (c=0x7ffc32005000) at networking.c:959
#4  0x000000010d251872 in readQueryFromClient (el=0x0, fd=5, privdata=0x7fff76f1c0b0, mask=220924512) at networking.c:1021
#5  0x000000010d243523 in aeProcessEvents (eventLoop=0x7fff6ce408d0, flags=220829559) at ae.c:352
#6  0x000000010d24373b in aeMain (eventLoop=0x10d429ef0) at ae.c:397
#7  0x000000010d2494ff in main (argc=1, argv=0x10d2b2900) at valkey.c:2046

这显示了回溯,但我们还想使用 info registers 命令转储处理器寄存器

(gdb) info registers
rax            0x0  0
rbx            0x7ffc32005000   140721147367424
rcx            0x10d2b0a60  4515891808
rdx            0x7fff76f1c0b0   140735188943024
rsi            0x10d299777  4515796855
rdi            0x0  0
rbp            0x7fff6ce40730   0x7fff6ce40730
rsp            0x7fff6ce40650   0x7fff6ce40650
r8             0x4f26b3f7   1327936503
r9             0x7fff6ce40718   140735020271384
r10            0x81 129
r11            0x10d430398  4517462936
r12            0x4b7c04f8babc0  1327936503000000
r13            0x10d3350a0  4516434080
r14            0x10d42d9f0  4517452272
r15            0x10d430398  4517462936
rip            0x10d26cfd4  0x10d26cfd4 <debugCommand+68>
eflags         0x10246  66118
cs             0x2b 43
ss             0x0  0
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0

务必将这两个输出都包含在您的错误报告中。

获取核心文件

下一步是生成核心转储,即正在运行的 Valkey 进程的内存映像。这通过使用 gcore 命令完成

(gdb) gcore
Saved corefile core.58414

现在您有了可以发送给 Valkey 开发人员的核心转储文件,但重要的是要明白,这其中包含了 Valkey 实例在崩溃时内部的所有数据;Valkey 开发人员将确保不与任何人分享其内容,并且一旦不再用于调试目的,将立即删除该文件,但请注意,发送核心文件即表示您正在发送您的数据。

要发送给开发人员的内容

最后,您可以将所有内容发送给 Valkey 核心团队

  • 您正在使用的 Valkey 可执行文件。
  • bt 命令生成的堆栈跟踪,以及寄存器转储。
  • 您使用 gdb 生成的核心文件。
  • 关于您正在使用的操作系统和 GCC 版本,以及 Valkey 版本的信息。

谢谢

您的帮助极其重要!许多问题只能通过这种方式进行跟踪。所以谢谢您!