《Redis官方文档》Redis调试指南

jopen 8年前

《Redis官方文档》Redis调试指南

原文链接 译者:Adeline

Redis开发过程中十分注重其稳定性:我们尽一切努力来保证每一个版本的稳定,不出现突然崩溃等情况。但是即使在我们百分百的努力下,仍然没办法保证百分百的无bug。

Redis出现崩溃时,会生成一个详细的报告来描述当时的情景,但是有时候只看报告还不够,而且Redis的核心开发团队可能也没办法独立重现你出现崩溃时候的场景:在这种情况下,我们需要用户能够重现这个情景来帮助我们。

这个指南讲解了如何使用GDB来获得Redis开发者可能用到的信息。

GDB是什么?


GDB是一款GNU调试器:一个可以查看到其他程序内部状态的程序。通常来讲,跟踪和修正bug其实就是一个收集更多的bug出现时的信息的过程,所以GDB是一个极为有用的工具。

GDB有以下两种使用模式:

  • 可以连接到一个运行的程序上来观察程序正在运行时候的状态
  • 可以观察已经结束运行的程序的状态,使用程序运行时候的内存镜像,叫做核心文件(core file)来实现。

从调试Redis的bug的角度来看,我们需要用到GDB的这两种模式:用户可以通过把GDB连接到Redis实例上来重现bug出现时候的场景,当崩溃发生时,用户可以创建core file来给Redis开发人员,开发人员可以用它来查看崩溃发生时Redis的内部运行状态。

这种方法使得开发人员可以独立在自己的电脑上进行模拟和跟踪,不需要用户的配合,也就不需要用户在生产环境中为了配合调试来进行Redis的重启了。

不使用优化选项编译Redis


默认情况下,Redis是使用 -O2选项编译的(译者注:-O2是gcc编译时的优化选项),这表示编译器优化是启动的。这使得Redis运行更快,但同时也使得Redis(与其他程序一样)更难被GDB观测。

使用GDB连接时,最好使用make noopt命令来编译Redis,使其不进行编译优化(而不是仅仅使用make)。但是如果你已经在生产环境中使用了Redis,而且重新编译可能会带来一些问题和麻烦的话,就没有必要重新编译了。尽管是会有一些限制,但是对于使用优化选项编译的程序,GDB也是可以用的。

如果你能在第一次出现崩溃后就把Redis不使用优化的重新编译一下,那是极好的,因为下次出现问题的时候就很好跟踪了。

你不用担心不使用优化编译对性能的影响,因为Redis并非计算密集型(CPU-bound)软件(更偏向I/O密集型),这点小的影响很难在你的环境中构成问题。

把GDB连接到一个运行的进程上


如果你有一个运行中的Redis 服务器,你可以把GDB连接到上面,如果Redis崩溃你既可以查看内部运行状态也可以创建一个core dump文件。

把GDB连接到Redis进程上不会对Redis的运行性能有影响,所以这不是一件危险的事,可以放心去做。

为了把GDB连接上去,首先要知道Redis实例的进程ID(进行的pid)。你可以使用 redis-cli轻松获取到:

$ redis-cli info | grep process_id  process_id:58414

上面的例子中,进程ID是58414

  • 登录到Redis服务器上
  • (非强制但建议的步骤)使用screen 或者tmux 终端来保证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,然后回车即可。
  • 完成!现在你的Redis服务器已经连接了GDB,你可以等着下次崩溃啦:)
  • 如果你是使用screen/tmux运行GDB的话,现在是时候把GDB的运行和screen/tmux会话分离开了(译者理解就是关闭会话不会影响程序运行的意思),使用Ctrl-a a 即可。

崩溃发生后


Redis的DEBUG SEGFAULT命令可以模拟段故障(也就是一个严重的意外崩溃(当然不要在真正的生产系统中这个命令哈),现在我要用这个命令来使Redis崩溃,看看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 命令就可以(bt是backtrace的简写)

(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

请一定在bug报告中包括这两种信息。

获得核心文件(core file)


下一步就是生成core dump,这是运行的Redis 进程的内存镜像。使用gcore 命令来完成:

(gdb) gcore    Saved corefile core.58414

有一点很重要,你需要知道这包含Redis实例崩溃时其中包含的所有数据:Redis开发者可以保证不会泄露这些数据,并且在调试结束后第一时间删除,但你还是需要知道发送的core file中包含你的数据。

如果你的数据中包含比较敏感的信息,我们建议您把dump文件直接发给Salvatore Sanfilippo(就是这个文档的作者),邮箱地址是antirez at gmail dot com

需要给开发人员发送的信息


现在你可以把所有的信息发送给Redis核心团队了,这些包括:

  • 你正在使用的Redis可执行文件
  • 使用bt命令导出的堆栈跟踪和寄存器dump
  • 使用GDB生成的核心文件(core file)
  • 操作系统信息,GCC版本,Redis版本

感谢


您的帮助对我们来说非常重要!有许多的问题只能用这种方式来跟踪,非常感谢!而且帮助我们进行调试你有可能获得摩卡咖啡壶的奖励哦(Redis Moka Award).

原创文章,转载请注明: 转载自并发编程网 – ifeve.com

本文链接地址: 《Redis官方文档》Redis调试指南