GDB 反向调试(Reverse Debugging)

逆向调试不是逆向执行,而是模拟逆向执行。在打开逆向调试开关后,程序的执行会记录下程序运行的详细状态轨迹(想象一下把图灵机运行时的每一步纸带 的状态记录下来)。这些状态就构成了逆向调试的基础。

逆向调试的预期用处是,因为程序bug的发生处和表现处一般会有距离,那么在程序bug表现处回溯执行,就可以确定地找到bug的真正原因。这可是 很有用的哦,因为这可以避免多次运行程序,有时还徒劳武无功,尤其是在不确定bug (heisenbug)的情况下。

使用调试器时最常用的功能就是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程序:

[cpp] view plain copy print ?

  1. int  main( int  argc,  const char  *argv[]) 
  2. int  a = 0; 
  3.   a = 1; 
  4.   a = 2; 
  5. return  0; 

将它编译并加上调试符号:

[python] view plain copy print ?

  1. $ gcc -Wall -g a.c 

开始调试:

[python] view plain copy print ?

  1. $ gdb a.out 

查看一下源代码:

[python] view plain copy print ?

  1. (gdb) l 
  2. 1    int main(int argc, const char *argv[]) 
  3. 2    { 
  4. 3      int a =  0 ; 
  5. 4      a =  1 ; 
  6. 5      a =  2 ; 
  7. 6 return 0 ; 
  8. 7    } 

接下来设置一个断点在第三行:

[python] view plain copy print ?

  1. (gdb) b  3
  2. Breakpoint 1  at  0x804839a : file a.c, line  3.

运行,程序会在第三行的地方停下来:

[python] view
plain
copy print ?

  1. (gdb) r 
  2. Starting program: /home/cheryl/a.out  
  3. Breakpoint 1 , main (argc= 1 , argv= 0xbffff3e4 ) at a.c: 3
  4. 3      int a =  0 ; 

给变量a设置监视点方便我们观察:

[python] view plain copy print ?

  1. (gdb) watch a 
  2. Hardware watchpoint 2 : a 

启动进程记录回放:

[python] view plain copy print ?

  1. (gdb) record 

现在每运行一步调试器都会记录下变化,以便回溯。我们连续执行3条语句。

[python] view plain copy print ?

  1. (gdb) n 
  2. 4      a =  1 ; 
  3. (gdb)  
  4. Hardware watchpoint 2 : a 
  5. Old value = 0
  6. New value = 1
  7. main (argc=1 , argv= 0xbffff3e4 ) at a.c: 5
  8. 5      a =  2 ; 
  9. (gdb)  
  10. Hardware watchpoint 2 : a 
  11. Old value = 1
  12. New value = 2
  13. main (argc=1 , argv= 0xbffff3e4 ) at a.c: 6
  14. 6 return 0 ; 

可以看到,a的值先是从0变为了1,然后变为2,如果想让程序倒退回到以前的状态怎么办?可以用reverse-next命令:

[python] view
plain
copy print ?

  1. (gdb) reverse-next 
  2. Hardware watchpoint 2 : a 
  3. Old value = 2
  4. New value = 1
  5. main (argc=1 , argv= 0xbffff3e4 ) at a.c: 5
  6. 5      a =  2 ; 
  7. (gdb)  
  8. Hardware watchpoint 2 : a 
  9. Old value = 1
  10. New value = 0
  11. main (argc=1 , argv= 0xbffff3e4 ) at a.c: 4
  12. 4      a =  1 ; 
  13. (gdb)  
  14. No more reverse-execution history. 
  15. main (argc=1 , argv= 0xbffff3e4 ) at a.c: 3
  16. 3      int a =  0 ; 
  17. (gdb)  

这样程序就倒退到了我们启动进程记录回放的地方,a的值经过两步回到了最初的状态。

若需要关闭进程记录回放,可以使用record stop:

[python] view plain copy print ?

  1. (gdb) record stop 
  2. Process record is  stoped  and  all execution log  is  deleted. 

参考:《Reverse Debugging with GDB》 --- http://sourceware.org/gdb/wiki/ReverseDebug

posted @ 2013-03-11 19:31  JustinYo  阅读(1408)  评论(0编辑  收藏  举报