存档

文章标签 ‘C’

调试 C 程序的奇淫技巧

2013年1月5日 没有评论

调试时不要开启任何优化选项

如果在编译的时候开启了一些优化选项, 比如 -O2, -O3 什么的, 有的变量/函数会被 gcc 自动优化掉, 比如以下的代码片段:

    for (i = 0; i < msg->num_of_channels; i++) {
        channel_new_t *c;
 
        c = g_new(channel_new_t, 1);
        c->session = g_object_ref(session);
        c->type = msg->channels[i].type;
        c->id = msg->channels[i].id;
        /* no need to explicitely switch to main context, since
           synchronous call is not needed. */
        /* no need to track idle, session is refed */
        g_idle_add((GSourceFunc)_channel_new, c);
    }

当我 break 到这行, 想查看 ichannels[i] 的值时, 开启优化选项和 未开启的区别如下:

// CFLAGS="-g"
1430            c->type = msg->channels[i].type;
(gdb) p i
$9 = <optimized out>
 
// CFLAGS="-g -O0"
1430            c->type = msg->channels[i].type;
(gdb) p i
$1 = 0
(gdb) p msg->channels[0]
$2 = {type = 5 '\005', id = 0 '\000'}

要关闭 gcc 的优化, 通常可以通过以下途径达到:

  • 如果程序的 configure 脚本提供了 “–enable-debug”, 打开它
  • 在正式编译代码的的时候, 定制 CFLAGS 参数, 去掉优化选项(使用 -O0), e.g. make CFLAGS=”-O0 -g”

core dump

默认情况下, 当程序发生 Segmentation fault 时, 内核只是简单的把该进程结束, 不做任何事情, 如果想要保留出错时候的上下文, 可以使用 ulimit -c unlimited 设置 dump core 文件 的大小, 使其不为0(或者用 setrlimit()设置).

这样, 程序 Segmentation fault 的时候, 会在当前目录下(或者 /var/***) 下 生成一个 名为 core(或者 core.*) 的文件, 用 gdb 工具就可以看到程序到底为什么 Segmentation fault 了.

例如下面的程序, 明显的有一个非法内存访问:

#include <stdio.h>
 
int main(int argc, char *argv[])
{
    char *s = NULL;
    sprintf(s, "foo");
    return 0;
}

编译运行后, 会得到一个 名为 core 的文件, 用 gdb 打开, 然后 gdb 会停在程序 出错的地方, 查看 s 的值, 可以看到 s 的值为 0x0, 即传递了一个非法的内存引用给 sprintf() 函数引发了异常.

$ ulimit -c unlimited
$ gcc -o test test.c -g
$ ./test
Segmentation fault (core dumped)
 
$ gdb test core
Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
#0  0x0000000000400512 in main (argc=1, argv=0x7fff7eed3048) at test.c:6
6       sprintf(s, "foo");
(gdb) bt
#0  0x0000000000400512 in main (argc=1, argv=0x7fff7eed3048) at test.c:6
(gdb) p s
$1 = 0x0

分类: programming 标签: , ,