调试

Redis 服务器进程调试指南

Redis 开源版

Redis 的开发强调稳定性。我们在每个版本中尽最大努力确保您体验到稳定且不崩溃的产品。但是,如果您需要调试 Redis 进程本身,请继续阅读。

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

本指南介绍了如何使用 GDB 来提供 Redis 开发者更轻松地跟踪 bug 所需的信息。

什么是 GDB?

GDB 是 GNU Debugger:一个能够检查另一个程序内部状态的程序。通常,跟踪和修复 bug 是一个收集 bug 发生时程序状态信息的练习,因此 GDB 是一个极其有用的工具。

GDB 可以通过两种方式使用

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

从调查 Redis bug 的角度来看,我们需要使用这两种 GDB 模式。能够重现 bug 的用户将其 GDB 附加到正在运行的 Redis 实例,当崩溃发生时,他们创建 core 文件,开发者随后将使用该文件来检查崩溃时 Redis 的内部状态。

这样,开发者就可以在其计算机上执行所有检查,无需用户协助,而用户可以在其生产环境中自由地重新启动 Redis。

不带优化的 Redis 编译

默认情况下,Redis 使用 -O2 开关进行编译,这意味着编译器优化已启用。这使得 Redis 可执行文件更快,但同时(像其他任何程序一样)使用 GDB 检查 Redis 更加困难。

最好使用 make noopt 命令(而不是简单的 make 命令)将 GDB 附加到未优化编译的 Redis 上。但是,如果您已经在生产环境中运行着 Redis,如果重新编译和重启会给您带来问题,则无需这样做。GDB 仍然适用于优化编译的可执行文件。

您无需过度担心编译 Redis 时不进行优化导致的性能损失。由于 Redis 对 CPU 要求不高,这不太可能在您的环境中造成问题。

将 GDB 附加到正在运行的进程

如果您已经运行着 Redis 服务器,可以将其附加到 GDB,这样即使 Redis 崩溃,也可以检查内部状态并生成 core dump 文件。

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

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

$ redis-cli info | grep process_id
process_id:58414

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

登录到您的 Redis 服务器。

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

通过输入以下内容将 GDB 附加到正在运行的 Redis 服务器

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

例如

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

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

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

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

(gdb) continue
Continuing.

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

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

崩溃后

Redis 有一个命令可以模拟段错误(换句话说,就是一次严重的崩溃),该命令是 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 检测到 Redis 崩溃,甚至能够显示导致崩溃的文件名和行号。这已经比 Redis 崩溃报告的回溯(仅包含函数名和二进制偏移量)好得多。

获取堆栈跟踪

首先要做的是使用 GDB 获取完整的堆栈跟踪。这就像使用 bt 命令一样简单

(gdb) bt
#0  debugCommand (c=0x7ffc32005000) at debug.c:220
#1  0x000000010d246d63 in call (c=0x7ffc32005000) at redis.c:1163
#2  0x000000010d247290 in processCommand (c=0x7ffc32005000) at redis.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 redis.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

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

获取核心文件

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

(gdb) gcore
Saved corefile core.58414

现在您可以将核心转储发送给 Redis 开发者,但是 请务必理解 核心转储文件包含了 Redis 实例在崩溃时的所有数据;Redis 开发者将确保不与其他人分享内容,并在不再用于调试目的后立即删除该文件,但在此警告您,发送核心文件意味着您正在发送您的数据。

需要发送给开发者的信息

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

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

感谢您

您的帮助至关重要!许多问题只能通过这种方式来跟踪。所以,谢谢!

评价此页面
返回顶部 ↑