mozilla rr 是 GDB 反向调试的更强大的替代方案
https://github.com/mozilla/rr
GDB 的内置记录和回放有严重的限制,例如不支持 AVX 指令:gdb reverse debugging fails with "Process record does not support instruction 0xf0d at address"
rr 的好处:
- 目前更加可靠。我已经在几个复杂的软件中测试了它相对较长的运行时间。
- 还提供了一个带有 gdbserver 协议的 GDB 接口,是一个很好的替代品
- 大多数程序的性能下降很小,我自己没有进行测量就没有注意到
- 生成的跟踪在磁盘上很小,因为只记录了很少的非确定性事件,到目前为止我从来不用担心它们的大小
rr 通过首先以记录每个非确定性事件(例如线程切换)发生的情况的方式运行程序来实现这一点。
然后在第二次重放运行期间,它使用那个小得惊人的跟踪文件来准确地重建原始非确定性运行中发生的事情,但是以一种确定性的方式,无论是向前还是向后。
rr 最初由 Mozilla 开发,用于帮助他们重现在第二天的夜间测试中出现的计时错误。但是,当您遇到仅在执行内数小时内发生的错误时,反向调试方面也是必不可少的,因为您经常想退后一步检查以前的状态导致后来的失败。
以下示例展示了它的一些功能,特别是 reverse-next、reverse-step 和 reverse-continue 命令。
在 Ubuntu 18.04 上安装:
sudo apt-get install rr linux-tools-common linux-tools-generic linux-cloud-tools-generic
sudo cpupower frequency-set -g performance
# Overcome "rr needs /proc/sys/kernel/perf_event_paranoid <= 1, but it is 3."
echo 'kernel.perf_event_paranoid=1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p
测试程序:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int f() {
int i;
i = 0;
i = 1;
i = 2;
return i;
}
int main(void) {
int i;
i = 0;
i = 1;
i = 2;
/* Local call. */
f();
printf("i = %d\n", i);
/* Is randomness completely removed?
* Recently fixed: https://github.com/mozilla/rr/issues/2088 */
i = time(NULL);
printf("time(NULL) = %d\n", i);
return EXIT_SUCCESS;
}
编译运行:
gcc -O0 -ggdb3 -o reverse.out -std=c89 -Wextra reverse.c
rr record ./reverse.out
rr replay
现在您被留在 GDB 会话中,您可以正确地进行反向调试:
(rr) break main
Breakpoint 1 at 0x55da250e96b0: file a.c, line 16.
(rr) continue
Continuing.
Breakpoint 1, main () at a.c:16
16 i = 0;
(rr) next
17 i = 1;
(rr) print i
$1 = 0
(rr) next
18 i = 2;
(rr) print i
$2 = 1
(rr) reverse-next
17 i = 1;
(rr) print i
$3 = 0
(rr) next
18 i = 2;
(rr) print i
$4 = 1
(rr) next
21 f();
(rr) step
f () at a.c:7
7 i = 0;
(rr) reverse-step
main () at a.c:21
21 f();
(rr) next
23 printf("i = %d\n", i);
(rr) next
i = 2
27 i = time(NULL);
(rr) reverse-next
23 printf("i = %d\n", i);
(rr) next
i = 2
27 i = time(NULL);
(rr) next
28 printf("time(NULL) = %d\n", i);
(rr) print i
$5 = 1509245372
(rr) reverse-next
27 i = time(NULL);
(rr) next
28 printf("time(NULL) = %d\n", i);
(rr) print i
$6 = 1509245372
(rr) reverse-continue
Continuing.
Breakpoint 1, main () at a.c:16
16 i = 0;
在调试复杂软件时,您可能会遇到崩溃点,然后陷入深度框架。在这种情况下,不要忘记在更高帧上reverse-next,您必须首先:
reverse-finish
直到那个框架,仅仅做通常的up是不够的。
我认为 rr 最严重的限制是:
UndoDB 是 rr 的商业替代品:https://undo.io 两者都是基于跟踪/重放的,但我不确定它们在功能和性能方面的比较。