Tutorial: Reverse debugging with GDB 7

Tutorial: Reverse debugging with GDB 7

by Jay Conrod
posted on 2009-12-01

GDB 7 came out a few weeks ago, and one of the major new features is reverse debugging. This allows you to record the execution of a process, then play it backward and forward. This is incredibly useful for fixing those mysterious bugs that are so common in C/C++ programs.

In this post, I'll give a motivating example of why reverse debugging is so useful, then I'll give a summary of the commands you need to know about. You might also want to look at the official tutorial on the GDB wiki, which is a good reference.

Here's a typical program we might have in C. It's very simple, but it contains a subtle bug that causes it to crash when we run it.

          #include <stdio.h>
#include <stdlib.h> void initialize(int *array, int size) {
for (int i = 0; i <= size; ++i)
array[i] = 0;
} int main(void) {
int *p = malloc(sizeof(int));
int values[10]; *p = 37;
initialize(values, 10);
printf("*p = %d\n", *p);
free(p); return 0;
}

This program is compiled with the following command:

          gcc -ggdb -std=c99 -O0 test.c -o test
        

When we run this program from the command line, we get a segmentation fault. What happened? We load it into gdb and enable recording:

          (gdb) break main
Breakpoint 1 at 0x8048449: file test.c, line 11.
(gdb) run
Starting program: /home/jay/Code/test/test Breakpoint 1, main () at test.c:11
(gdb) record
(gdb)

The record command turns on recording. It must be issued after the program has started running, so the beginning of main is a good place to use it. If you are debugging code that runs before main (such as C++ global constructors), you may want to set a breakpoint in _start, the actual entry point of the program.

Once recording is enabled, we run the program until the segmentation fault occurs:

          (gdb) continue
Continuing.
warning: Process record ignores the memory change of instruction at
address 0xdd45e1 because it can't get the value of the segment
register. Program received signal SIGSEGV, Segmentation fault.
0x0804847b in main () at test.c:15
(gdb)

After getting a weird warning (which appears to occur inside malloc), we find that the segmentation fault occurs at the call to printf. The only memory operation here is dereferencing p, so we print its value:

          (gdb) print p
$1 = (int *) 0x0
(gdb)

p should not be null! We set it at the beginning with malloc and never changed its value. We can find out where it was changed by setting a watchpoint and using the reverse-continue command. This works just like you would hope: the program runs backwards until the point at which p was changed. Note that we explicitly set a software watchpoint rather than a hardware watchpoint; GDB 7 seems to silently ignore hardware watchpoints when replaying the program.

          (gdb) set can-use-hw-watchpoints 0
(gdb) watch p
Watchpoint 2: p
(gdb) reverse-continue
Continuing.
Watchpoint 2: p Old value = (int *) 0x0
New value = (int *) 0x804b008
0x0804842c in initialize (array=0xbffff5c0, size=10) at test.c:6
(gdb)

GDB stops on line 6 in the initialize function. Upon closer examination of the loop, we notice the off-by-one error in the condition. Since the array we passed to initialize is adjacent to p on the stack, we overwrite p when we write off the end of the array.

This kind of error is an example of a buffer overflow, a common bug in C. More severe versions of this bug can create security vulnerabilities. Overflows are usually quite difficult to track down because a variable like p could change many times before taking on a "bad" value like it did here. If we had set a watchpoint at the beginning of a more complex program, the debugger might stop hundreds of times before we found something interesting. More generally, we frequently debug by sneaking up on a fault from the front; if we accidentally pass the fault, we usually have to start over. Reverse debugging allows us to approach a fault much more quickly from behind. We just let the program run normally until we reach a point shortly after a fault has occurred (for instance, when the SIGSEGV signal is received). We then run the program backward until we find what went wrong. The debugger can do pretty much the same things running backward as forward, including setting new watchpoints and breakpoints.

Before we wrap up, here's a quick summary of useful commands for reverse debugging:

  • record - enable recording mode. This command must be issued while the program is running, and you can only run the program back to the point where this command was issued. There is a performance penalty.
  • reverse-step, reverse-next, reverse-finish, reverse-continue - just like the normal versions of the commands, but in the opposite direction.set exec-direction reverse - switches the program execution direction to reverse. The step, next, finish, continue commands will work in the currect execution direction.
  • set can-use-hw-watchpoints 0 - disables hardware watch points. This is necessary if you want to use watchpoints while running the program in reverse.

Once again, you need GDB 7 in order to use reverse debugging. It is supplied as part of Ubuntu 9.10 (Karmic) and other newer Linux distributions. Since reverse debugging a fairly new feature, it's not as complete as one would hope, so there's an official wish list which contains bugs and feature requests.

Finally, I'd like to reference Sebastien Duquette's tutorial on reverse debugging, which is where I found out that you need to disable hardware watchpoints. If you're interested in how reverse debugging actually works, Michael Snyder (one of the GDB developers responsible for it) has a short post on StackOverflow about it.


Comment by Lakshmy m.a posted on Fri Mar 5 09:24:25 2010

sir, Am a student of model engineering college trikakkara of ernakulam district,kerala,India.we a group of 4 took this rgdb as our mini project.i would like to get some technical support from your side.can you please send me a mail as your respones, sir i couldnt get ur mail id...so that i can mail you we would like to get a tutorial of each reverse commands implementation + its logic we have downloaded the source code of the new version of gdb.7 but we couldnt understand the logic ,since its very lengthy, and we hav a short time to for our mini project section... It will be very helpful if we get the basic idea behind this.


http://www.jayconrod.com/posts/28/tutorial-reverse-debugging-with-gdb-7

Tutorial: Reverse debugging with GDB 7 (转载)的更多相关文章

  1. GDB 反向调试(Reverse Debugging)

    这个挺有意思 http://blog.csdn.net/CherylNatsu/article/details/6436570 使用调试器时最常用的功能就是step, next, continue,这 ...

  2. Debugging with GDB 用GDB调试多线程程序

    Debugging with GDB http://www.delorie.com/gnu/docs/gdb/gdb_25.html GDB调试多线程程序总结 一直对GDB多线程调试接触不多,最近因为 ...

  3. 【转载】GDB反向调试(Reverse Debugging)

    记得刚开始学C语言的时候,用vc的F10来调试程序,经常就是一阵狂按,然后一不小心按过了.结果又得从头再来,那时候我就问我的老师,能不能倒退回去几步.我的老师很遗憾地和我说,不行,开弓没有回头箭.这句 ...

  4. gdb调试4--回退

    加入你正在使用GDB7.0以上版本的调试器并且运行在支持反向调试的平台,你就可以用以下几条命令来调试程序: reverse-continue 反向运行程序知道遇到一个能使程序中断的事件(比如断点,观察 ...

  5. Debugging Under Unix: gdb Tutorial (https://www.cs.cmu.edu/~gilpin/tutorial/)

    //注释掉 #include <iostream.h> //替换为 #include <iostream> using namespace std; Contents Intr ...

  6. 27 Debugging Go Code with GDB 使用GDB调试go代码

    Debugging Go Code with GDB  使用GDB调试go代码 Introduction Common Operations Go Extensions Known Issues Tu ...

  7. [转]Debugging the Mac OS X kernel with VMware and GDB

    Source: http://ho.ax/posts/2012/02/debugging-the-mac-os-x-kernel-with-vmware-and-gdb/ Source: http:/ ...

  8. 使用gdb调试Python进程

    使用gdb调试Python进程 有时我们会想调试一个正在运行的Python进程,或者一个Python进程的coredump.例如现在遇到一个mod_wsgi的进程僵死了,不接受请求,想看看究竟是运行到 ...

  9. GDB的non-stop模式

    线程调试必杀技 - GDB的non-stop模式   作者:破砂锅 开源的GDB被广泛使用在Linux.OSX.Unix和各种嵌入式系统(例如手机),这次它又带给我们一个惊喜. 多线程调试之痛 调试器 ...

随机推荐

  1. CF825F String Compression 解题报告

    CF825F String Compression 题意 给定一个串s,其中重复出现的子串可以压缩成 "数字+重复的子串" 的形式,数字算长度. 只重复一次的串也要压. 求压缩后的 ...

  2. ER-18

    ER #18简要题解 就是推出循环矩阵乘积 然后一次操作后得到的c矩阵第一行第i列就是i的情况(b矩阵下标是a矩阵下标的转置) 两个循环矩阵乘积还是循环矩阵 以此推式子,发现c矩阵的第一行可以用a,b ...

  3. CentOS 7网络故障

    By francis_hao    Nov 2,2017   在像往常一样打开了虚拟机后,打开xshell准备连接到centos,可是连不上,发现连接的网卡没有启动,使用systemctl启动netw ...

  4. Codeforces Round #306 (Div. 2)A B C D 暴力 位/暴力 暴力 构造

    A. Two Substrings time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...

  5. Windows互斥锁demo和分析

    一:windows创建锁接口 创建互斥锁的方法是调用函数CreateMutex HANDLE CreateMutex( LPSECURITY_ATTRIBUTESlpMutexAttributes, ...

  6. [Java多线程]-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  7. 前端PHP入门-020-重点日期函数之获取时期时间信息函数

    你需要知道关于时间的几个概念: 时区/世界时/unix时间戳 1.时区 这个概念,之前大家听说过很多.我们来啰嗦两句,我们现实生活中使用的实区,在电脑里面也是一样有规定的. 1884年在华盛顿召开国际 ...

  8. Spring quartz定时任务service注入问题

    今天想单元测试一下spring中的quartz定时任务,job类的大致结构和下面的SpringQtz1类相似,我的是实现的org.quartz.Job接口,到最后总是发现job类里注入的service ...

  9. 非法字符:"\ufeff"

    Eclipse项目导入IDEA可能遇到这样的问题 ,原因就是: 带BOM的UTF-8」和「无BOM的UTF-8」 方法一.用Notepad++把文件转成无BOM的UTF-8 另存为,替换原来的文件 方 ...

  10. Spark RDD——combineByKey

    为什么单独讲解combineByKey? 因为combineByKey是Spark中一个比较核心的高级函数,其他一些高阶键值对函数底层都是用它实现的.诸如 groupByKey,reduceByKey ...