作者:<leohe.leohe@gmail.com>
Linux系统中在应用程序运行过程中经常会遇到程序突然崩溃,提示:Segmentation
fault,这是因为应用程序收到了SIGSEGV信号。这个信号提示当进程发生了无效的存储访问,当接收到这个信号时,缺省动作是:终止w/core。
终止w/core的含义是:在进程当前目录生成core文件,并将进程的内存映象复制到core文件中,core文件的默认名称就是“core”(这是
Unix类系统的一个由来已久的功能)。
事实上,并不是只有SIGSEGV信号产生coredump,还有下面一些信号也产生coredump:SIGABRT(异常终止)、SIGBUS(硬件
故障)、SIGEMT(硬件故障)、SIGFPE(算术异常)、SIGILL(非法硬件指令)、SIGIOT(硬件故
障),SIGQUIT,SIGSYS(无效系统调用),SIGTRAP(硬件故障)等。
在程序的开发调试阶段(尤其是大型软件开发),发生程序异常崩溃时常规的调试方法常常是无比的痛苦:无穷的log中也不见得有什么有意义的信息。好在GDB提供和利用core文件进行调试的途径,大大方便了这类问题的调试。
下面我们通过一个简单的例子来看看怎么通过GDB来调试一个违规访问内存导致的程序崩溃。这里我们顺便讲讲动态库的调试。
/******** mylib.h **********/
#ifndef __MY_LIB_H__
#define __MY_LIB_H__
int add(int x, int y);
#endif // __MY_LIB_H__
/******** end **********/
/******** mylib.c **********/
#include <stdlib.h>
#include "mylib.h"
int add(int x, int y)
{
char* pc = NULL;
*pc = 10;
return x + y;
}
/******** end **********/
/******** main.c **********/
#include <stdio.h>
#include <stdlib.h>
#include "mylib.h"
int main (void)
{
int ret = -1;
int a = 10, b = 20;
ret = add(a, b);
printf("The result is: %d\n", ret);
return 0;
}
/******** end **********/
#####################################
# File Name: Makefile
#
#####################################
CC = gcc
LD = gcc
all:
$(CC) mylib.c -g -I. -fPIC -shared -o libmylib.so
$(CC) main.c -g -I. -L. -lmylib -o test
clean:
rm *.so test
############# END ###############
首先将上面的代码分别存储到相应的目录,名称为:mylib.h、mylib.c、main.c、Makefile。
1)编译测试代码。注)编译时的 -g 选项是必须的。
[xxx@yyy]$ make
gcc mylib.c -g -I. -fPIC -shared -o libmylib.so
gcc main.c -g -I. -L. -lmylib -o t
通过ls命令我们可以看到生成了测试程序test.
[xxx@yyy]$ ls
libmylib.so main.c Makefile mylib.c mylib.h test
2)执行测试程序
[xxx@yyy]$ ./test
./test: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory
这个错误表明程序在运行阶段不能找到相应的动态库文件,此时需要通过环境变量 LD_LIBRARY_PATH 来指定运行期动态库的搜索目录,我们的动态库就在当前目录,如下:
[xxx@yyy]$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
3)再次执行测试程序
[leo@localhost debug]$ ./test
Segmentation fault
[leo@localhost debug]$ ls
libmylib.so main.c Makefile mylib.c mylib.h test
4)设置core文件大小
Segmentation fault如期而至,但是却没有我们更想见到的core文件!
原来系统在默认情况下core文件的大小设置为0,换句话讲也就是不产生core文件。我们可以通过 ulimit 命令来修改core文件的大小,unlimited表示不限制core文件的大小,如下(设置core文件的大小需要root权限):
[root@yyy]# ulimit -c unlimited
[root@yyy]# ./test
Segmentation fault (core dumped)
[root@yyy]# ls
core.2890 libmylib.so main.c Makefile mylib.c mylib.h test
5)设置core文件的格式,输出路径
通过下面命令我们还可以指定core文件的命名格式,路径等(需要root权限):
[root@yyy]# echo "core_%e_%s" >/proc/sys/kernel/core_pattern
[root@yyy]# ./test
Segmentation fault (core dumped)
[root@yyy]# ls
core.2890 core_test_11.2898 libmylib.so main.c Makefile mylib.c mylib.h test
6)调试
[root@yyy]# gdb test core.2890
GNU gdb Red Hat Linux (6.5-8.fc6rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db library "/lib/libthread_db.so.1".
Core was generated by `./test'.
Program terminated with signal 11, Segmentation fault.
Error while mapping shared library sections:
libmylib.so: Success.
Reading symbols from /home/xxx/tst/libmylib.so...done.
Loaded symbols for libmylib.so
Reading symbols from /lib/i686/libc.so.6...done.
Loaded symbols for /lib/i686/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
#0 0x00a8969c in ?? ()
(gdb)
键入GDB命令 where
(gdb) where
#0 0x001ec44c in ?? ()
#1 0x00000000 in ?? ()
?? ()并不是我们想看到的,之所以这样,是因为GDB不能正确加载我们编写的动态库libmylib.so,我们需要在这里设置GDB的动态库搜索路径,如下:
(gdb) set solib-search-path .
Reading symbols from /home/xxx/test/tst/libmylib.so...done.
Loaded symbols for /home/xxx/test/tst/libmylib.so
Reading symbols from /lib/i686/libc.so.6...done.
Loaded symbols for /lib/i686/libc.so.6
Reading symbols from /lib/ld-linux.so.2...done.
Loaded symbols for /lib/ld-linux.so.2
可以看到GDB已经加载了libmylib.so,再次键入where命令:
(gdb) where
#0 0x001ec44c in add (x=10, y=20) at mylib.c:8
#1 0x0804847c in main () at main.c:12
(gdb)
这次我们期待的结果出现了,GDB清楚的列出了错误出现的位置:mylib.c的第8行,好了,到那里去改code吧!