5.1 检查核心文件

为了允许程序在调试器下运行,'-g'选项的另一个作用是帮助查找程序错误的地方。

当一个程序异常退出时操作系统会创建一个核心文件通常名为'core',它包含在异常退出时程序在内存中的状态。结合'-g'选项产生的符号表信息,核心文件可以用于查找程序停止的地方和此时程序变量的值。这对于软件开发和调试都非常有用。

这有一个简单的程序包含一个内存访问bug,我们使用这个程序产生核心文件:

int a (int *p);

int

main (void)

{

int p = 0; / null pointer */

return a (p);

}

int

a (int *p)

{

int y = *p;

return y;

}

程序试图定义一个空指针p,这是一个无效的操作。在大多数操作系统上这会导致错误。

为了能够找到错误的地方,我们需要使用'-g'选项编译此程序:

$ gcc -Wall -g null.c

注意空指针只会在运行时刻产生错误所以'-Wall'选项不会产生任何警告信息。

在x86 GNU/Linux系统上运行这个可执行文件会导致操作系统异常终止此程序:

$ ./a.out

Segmentation fault (core dumped)

当错误信息'core dumped'显示出来时操作系统会在当前目录下产生一个名为'core'的文件。这个核心文件包含了程序终止时使用所有内存页的一个完全拷贝。有时候段错误的原因就是程序试图访问一个受限制的内存段。

一些系统被设置为默认情况下不产生核心文件,因为这个文件可以很大致使系统的硬盘被填满。在GNU命令行中'ulimit -c'命令控制核心文件的最大容量限制。如果容量限制为零则不会产生核心文件。当前容量限制可以使用下面的命令行查看:

$ ulimit -c

0

如果结果是0就像上面显示的一样,则可以使用下面的命令增加核心文件为任意大小:

$ ulimit -c unlimited

注意这个设置只会在当前shell下有效。为了永久有效可以将命令写入登陆文件,例如GNU Bash shell中的'.bash_profile'文件。

核心文件可以使用下面命令加载到GNU调试器gdb中:

$ gdb EXECUTABLE-FILE CORE-FILE

注意只有可执行文件和核心文件同时存在时才可以进行调试--不可能只使用核心文件进行调试。在这个例子中,我们可以使用下面命令将可执行文件和核心文件加载到gdb中:

$ gdb a.out core

调试器立即开始打印诊断细信息,然后显示程序终止处的代码行:

$ gdb a.out core

Core was generated by ‘./a.out’.

Program terminated with signal 11, Segmentation fault.

Reading symbols from /lib/libc.so.6...done.

Loaded symbols for /lib/libc.so.6

Reading symbols from /lib/ld-linux.so.2...done.

Loaded symbols for /lib/ld-linux.so.2

0 0x080483ed in a (p=0x0) at null.c:13

13 int y = *p;

(gdb)

最后一行是GNU调试器的提示符--它指示更多命令可以在这里输入。

为了检查异常终止的原因,我们可以使用调试器命令print显示指针p的值:

(gdb) print p

$1 = (int *) 0x0

它显示了p是一个'int '类型的空指针(0x0),所以我们可以知道p指针的定义错误导致了程序异常终止。