rr(debugging) 的配置与基本使用

当我们进行数据库等复杂应用系统时,如果出现了问题需要进行 debug 的话,gdb 就是第一选择。但是对于类似于内存等复杂问题来说,使用 gdb 进行调试颇为不便: gdb 无法“回滚”我们的操作,也就是说,当我们使用断点错过了一个非常重要的操作的话,一切就得重新开始。此时我们就可以选择 rr 这个支持反向调试的工具,一次复现,N 次调试。

1. Ubuntu 20.04 安装并配置 rr

# prerequisite
sudo apt-get install linux-tools-generic linux-cloud-tools-generic

# install rr-debug
sudo apt-get install rr

而后运行如下指令以检测 perf 运行良好:

perf stat -e br_inst_retired.conditional true

此时应该可以得到如下输出:

 Performance counter stats for 'true':

       105,775     br_inst_retired.conditional                                   

       0.000555427 seconds time elapsed

       0.000571000 seconds user
       0.000000000 seconds sys

2. rr 的使用

rr 最重要的功能就在于能够保留完整的进程执行状态,即使进程因为种种原因(Segment Fault、Terminated、Aborted 等)而异常退出,我们依然可以使用 rr replay 来无限次的重放该进程从初始化到结束的过程。

2.1 Simple Demo Case

#include <random>
#include <ctime>
#include <iostream>

using namespace std;

int main() {
    srand(time(NULL));
    int num;
  
    for (int i = 0; i < 3; ++i) {
        num = random() % 5;
        cout << num << endl;
    }

    abort();
    return 0;
}

可以用一段非常简单的 C++ 代码来演示 rr-debug 的使用,使用 random 的原因在于说明 rr-debug 会持久化进程的整个执行生命周期。编译并使用 rr-debu 进行记录:

smart@stable:~/workspace$ g++ -g --std=c++11 -o rr-demo rr-debug-demo.cpp
smart@stable:~/workspace$ rr record ./rr-demo
rr: Saving execution to trace directory `/home/smart/.local/share/rr/rr-demo-0'.
1
3
4
Aborted

当运行完毕时 rr 会打印出程序上下文所保存地址,当前进程完整记录被保存在 rr-demo-0 中,之所以添加 -0 是为了区分同一个程序的多次运行,下一次执行 rr record ./rr-demo 所产生的记录文件即为 rr-demo-1

而后,可使用 rr replay <filename> 来重放指定的进程记录或者是直接使用 rr replay 来重放最近一次的记录:

smart@stable:~/workspace$ rr replay rr-demo-0
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /home/smart/.local/share/rr/rr-demo-0/mmap_hardlink_3_rr-demo...
Really redefine built-in command "restart"? (y or n) [answered Y; input not from terminal]
Remote debugging using 127.0.0.1:43176
Reading symbols from /lib64/ld-linux-x86-64.so.2...
(No debugging symbols found in /lib64/ld-linux-x86-64.so.2)
0x00007f6e94051100 in ?? () from /lib64/ld-linux-x86-64.so.2
(rr)

此时进程处于初始化的状态,我们需要添加断点并运行它:

(rr) list 1,18
1       #include <random>
2       #include <ctime>
3       #include <iostream>
4
5       using namespace std;
6
7
8       int main() {
9           srand(time(NULL));
10          int num;
11        
12          for (int i = 0; i < 3; ++i) {
13              num = random() % 5;
14              cout << num << endl;
15          }
16
17          abort();
18
(rr) b 14
Breakpoint 1 at 0x556c309b428e: file rr-debug-demo.cpp, line 14.
(rr) c
Continuing.

Breakpoint 1, main () at rr-debug-demo.cpp:14
14              cout << num << endl;
(rr) p num
$1 = 1
(rr) c
Continuing.
1

Breakpoint 1, main () at rr-debug-demo.cpp:14
14              cout << num << endl;
(rr) p num
$2 = 3

此时,还可以使用 reverse-continue(rc) 回到上一个断点处,这也是 rr 一个最为重要的特性:

(rr) rc
Continuing.

Breakpoint 1, main () at rr-debug-demo.cpp:14
14              cout << num << endl;
(rr) p num
$3 = 1

此时并不会改变程序的运行结果,因为程序中所有的变量、函数调用栈等内容都被持久化地塞到一个文件中了。continue(c)reverse-continue(rc) 两个指令示意如下图所示:

Alt text

进一步地,rr 也可以记录子进程和回放子进程的一生,当需要回放特定的子进程时,只需要添加 -f <child-pid> 参数即可。因此,rr 可以非常轻松地定位 Greenplum 中出现的内存问题或其它通过 GDB 较难搞定的问题。

2.2 使用 rr 记录 Greenplum 进程

当我们需要使用 rr 来记录 QD 或者是 QE 进程时,需要使用 pg_ctl 将原有的 postgres 进程终止,然后再用 rr record 启动一个由 rr 所记录的进程:

pg_ctl stop -D <dir>
rr record <path>/postgres -D <dir> -p <port>

这个进程为 postmaster 进程,而我们大部分时间会更关注 postgres 进程(实际工作进程),因此,当我们使用 psql 或者是其他工具对问题进行复现之后,需要手动停止 rr 所记录的 postmaster 进程,否则将无法重放。

而后,只需要使用 rr replay -f <pid> 即可重放对应出现错误的 postgres 进程。

3 附录: vCenter 虚拟机 CPU 配置

只需开启虚拟机 CPU 的 “Enable virtualized CPU performance counters” 即可:

Alt text

PS: 该配置需要在虚拟机电源关闭情况下进行。

SmartKeyerror

日拱一卒,功不唐捐