GDB 反向调试(Reverse Debugging)
2011-05-31 22:36 htc开发 阅读(469) 评论(0) 收藏 举报
使用调试器时最常用的功能就是step, next, continue,这几个调试命令都是“往下执行”的, 但是很多时候会有这种需求:你在调试的过程中多跳过了几步而错过中间过程,这时候不得不重头调试一遍,非常麻烦。而GDB从7.0版本开始支持反向调试功能,也就是允许你倒退着运行程序,或者说撤销程序执行的步骤从而会到以前的状态。
直观地来看,加入你正在使用GDB7.0以上版本的调试器并且运行在支持反向调试的平台,你就可以用以下几条命令来调试程序:
reverse-continue
反向运行程序知道遇到一个能使程序中断的事件(比如断点,观察点,异常)。
reverse-step
反向运行程序到上一次被执行的源代码行。
reverse-stepi
反向运行程序到上一条机器指令
reverse-next
反向运行到上一次被执行的源代码行,但是不进入函数。
reverse-nexti
反向运行到上一条机器指令,除非这条指令用来返回一个函数调用、整个函数将会被反向执行。
reverse-finish
反向运行程序回到调用当前函数的地方。
set exec-direction [forward | reverse]
设置程序运行方向,可以用平常的命令step和continue等来执行反向的调试命令。
上面的反向运行也可以理解为撤销后面运行的语句所产生的效果,回到以前的状态。
好的,接下来我们来试试看如何反向调试。
首先确认自己的平台支持进程记录回放(Process Record and Replay),当在调试器启用进程记录回放功能时,调试器会记录下子进程,也就是被调试进程的每一步的运行状态与上一步运行状态的差异,需要撤销的时候就可以很方便回到上一步。
假设我们有以下C程序:
view plaincopy to clipboardprint?
int main(int argc, const char *argv[])
{
int a = 0;
a = 1;
a = 2;
return 0;
}
int main(int argc, const char *argv[])
{
int a = 0;
a = 1;
a = 2;
return 0;
}
int main(int argc, const char *argv[])
{
int a = 0;
a = 1;
a = 2;
return 0;
}
int main(int argc, const char *argv[])
{
int a = 0;
a = 1;
a = 2;
return 0;
}
将它编译并加上调试符号:
view plaincopy to clipboardprint?
$ gcc -Wall -g a.c
$ gcc -Wall -g a.c
$ gcc -Wall -g a.c
$ gcc -Wall -g a.c
开始调试:
view plaincopy to clipboardprint?
$ gdb a.out
$ gdb a.out
$ gdb a.out
$ gdb a.out
查看一下源代码:
view plaincopy to clipboardprint?
(gdb) l
1 int main(int argc, const char *argv[])
2 {
3 int a = 0;
4 a = 1;
5 a = 2;
6 return 0;
7 }
(gdb) l
1 int main(int argc, const char *argv[])
2 {
3 int a = 0;
4 a = 1;
5 a = 2;
6 return 0;
7 }
(gdb) l
1 int main(int argc, const char *argv[])
2 {
3 int a = 0;
4 a = 1;
5 a = 2;
6 return 0;
7 }
(gdb) l
1 int main(int argc, const char *argv[])
2 {
3 int a = 0;
4 a = 1;
5 a = 2;
6 return 0;
7 }
接下来设置一个断点在第三行:
view plaincopy to clipboardprint?
(gdb) b 3
Breakpoint 1 at 0x804839a: file a.c, line 3.
(gdb) b 3
Breakpoint 1 at 0x804839a: file a.c, line 3.
(gdb) b 3
Breakpoint 1 at 0x804839a: file a.c, line 3.
(gdb) b 3
Breakpoint 1 at 0x804839a: file a.c, line 3.
运行,程序会在第三行的地方停下来:
view plaincopy to clipboardprint?
(gdb) r
Starting program: /home/cheryl/a.out
Breakpoint 1, main (argc=1, argv=0xbffff3e4) at a.c:3
3 int a = 0;
(gdb) r
Starting program: /home/cheryl/a.out
Breakpoint 1, main (argc=1, argv=0xbffff3e4) at a.c:3
3 int a = 0;
(gdb) r
Starting program: /home/cheryl/a.out
Breakpoint 1, main (argc=1, argv=0xbffff3e4) at a.c:3
3 int a = 0;
(gdb) r
Starting program: /home/cheryl/a.out
Breakpoint 1, main (argc=1, argv=0xbffff3e4) at a.c:3
3 int a = 0;
给变量a设置监视点方便我们观察:
view plaincopy to clipboardprint?
(gdb) watch a
Hardware watchpoint 2: a
(gdb) watch a
Hardware watchpoint 2: a
(gdb) watch a
Hardware watchpoint 2: a
(gdb) watch a
Hardware watchpoint 2: a
启动进程记录回放:
view plaincopy to clipboardprint?
(gdb) record
(gdb) record
(gdb) record
(gdb) record
现在每运行一步调试器都会记录下变化,以便回溯。我们连续执行3条语句。
view plaincopy to clipboardprint?
(gdb) n
4 a = 1;
(gdb)
Hardware watchpoint 2: a
Old value = 0
New value = 1
main (argc=1, argv=0xbffff3e4) at a.c:5
5 a = 2;
(gdb)
Hardware watchpoint 2: a
Old value = 1
New value = 2
main (argc=1, argv=0xbffff3e4) at a.c:6
6 return 0;
(gdb) n
4 a = 1;
(gdb)
Hardware watchpoint 2: a
Old value = 0
New value = 1
main (argc=1, argv=0xbffff3e4) at a.c:5
5 a = 2;
(gdb)
Hardware watchpoint 2: a
Old value = 1
New value = 2
main (argc=1, argv=0xbffff3e4) at a.c:6
6 return 0;
(gdb) n
4 a = 1;
(gdb)
Hardware watchpoint 2: a
Old value = 0
New value = 1
main (argc=1, argv=0xbffff3e4) at a.c:5
5 a = 2;
(gdb)
Hardware watchpoint 2: a
Old value = 1
New value = 2
main (argc=1, argv=0xbffff3e4) at a.c:6
6 return 0;
(gdb) n
4 a = 1;
(gdb)
Hardware watchpoint 2: a
Old value = 0
New value = 1
main (argc=1, argv=0xbffff3e4) at a.c:5
5 a = 2;
(gdb)
Hardware watchpoint 2: a
Old value = 1
New value = 2
main (argc=1, argv=0xbffff3e4) at a.c:6
6 return 0;
可以看到,a的值先是从0变为了1,然后变为2,如果想让程序倒退回到以前的状态怎么办?可以用reverse-next命令:
view plaincopy to clipboardprint?
(gdb) reverse-next
Hardware watchpoint 2: a
Old value = 2
New value = 1
main (argc=1, argv=0xbffff3e4) at a.c:5
5 a = 2;
(gdb)
Hardware watchpoint 2: a
Old value = 1
New value = 0
main (argc=1, argv=0xbffff3e4) at a.c:4
4 a = 1;
(gdb)
No more reverse-execution history.
main (argc=1, argv=0xbffff3e4) at a.c:3
3 int a = 0;
(gdb)
(gdb) reverse-next
Hardware watchpoint 2: a
Old value = 2
New value = 1
main (argc=1, argv=0xbffff3e4) at a.c:5
5 a = 2;
(gdb)
Hardware watchpoint 2: a
Old value = 1
New value = 0
main (argc=1, argv=0xbffff3e4) at a.c:4
4 a = 1;
(gdb)
No more reverse-execution history.
main (argc=1, argv=0xbffff3e4) at a.c:3
3 int a = 0;
(gdb)
(gdb) reverse-next
Hardware watchpoint 2: a
Old value = 2
New value = 1
main (argc=1, argv=0xbffff3e4) at a.c:5
5 a = 2;
(gdb)
Hardware watchpoint 2: a
Old value = 1
New value = 0
main (argc=1, argv=0xbffff3e4) at a.c:4
4 a = 1;
(gdb)
No more reverse-execution history.
main (argc=1, argv=0xbffff3e4) at a.c:3
3 int a = 0;
(gdb)
(gdb) reverse-next
Hardware watchpoint 2: a
Old value = 2
New value = 1
main (argc=1, argv=0xbffff3e4) at a.c:5
5 a = 2;
(gdb)
Hardware watchpoint 2: a
Old value = 1
New value = 0
main (argc=1, argv=0xbffff3e4) at a.c:4
4 a = 1;
(gdb)
No more reverse-execution history.
main (argc=1, argv=0xbffff3e4) at a.c:3
3 int a = 0;
(gdb)
这样程序就倒退到了我们启动进程记录回放的地方,a的值经过两步回到了最初的状态。
若需要关闭进程记录回放,可以使用record stop:
view plaincopy to clipboardprint?
(gdb) record stop
Process record is stoped and all execution log is deleted.
(gdb) record stop
Process record is stoped and all execution log is deleted.
浙公网安备 33010602011771号