前面写过通过注册信号处理函数定位SEGV问题。其实Linux coredump机制也是比较好的debug手段。
进程由于某种异常或者bug导致在运行过程中异常退出或者中止,有时会产生一个叫做core的文件。在此文件中包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息还有各种函数调用堆栈信息。
core文件一般在以下几种情况下产生:
1,内存访问越界
a) 由于使用错误的下标,导致数组访问越界。
b) 搜索字符串时,依靠字符串结束符来判断字符串是否结束,但是字符串没有正常的使用结束符。
c) 使用strcpy, strcat, sprintf, strcmp,strcasecmp等字符串操作函数,将目标字符串读/写爆。应该使用strncpy, strlcpy, strncat, strlcat, snprintf, strncmp, strncasecmp等函数防止读写越界。
2,多线程程序使用了线程不安全的函数。
3,多线程读写的数据未加锁保护。对于会被多个线程同时访问的全局数据,应该注意加锁保护,否则很容易造成coredump
4,非法指针
a) 使用空指针
b) 随意使用指针转换。一个指向一段内存的指针,除非确定这段内存原先就分配为某种结构或类型,或者这种结构或类型的数组,否则不要将它转换为这种结构或类型的指针,而应该将这段内存拷贝到一个这种结构或类型中,再访问这个结构或类型。这是因为如果这段内存的开始地址不是按照这种结构或类型对齐的,那么访问它时就很容易因为bus error而core dump。
5,堆栈溢出
不要使用大的局部变量(因为局部变量都分配在栈上),这样容易造成堆栈溢出,破坏系统的栈和堆结构,导致出现莫名其妙的错误。
默认情况下一般系统不会产生core文件。
如果ulimit -c的结果为0,不会产生core文件。可以用ulimit -c filesize,filesize 是生成文件的大小,以blocks为单位,1个block为512bytes。可以将其设置unlimited(ulimit -c unlimited)那么core文件大小不受限制。可以将命令加到/etc/profile中。也可以在程序中用setrlimit函数来设置。
我们还可以设置core文件的生成路径。
通过echo /mnt/hgfs/share/test/core.%e.%p > /proc/sys/kernel/core_pattern
生成的core文件名字为“core.程序文件名.进程ID”
可以在代码中打开core_pattern文件,将路径名字写到文件中,也可以将修改路径的命令加到rc.local中。
下面以一个简单的sample来演示怎么用core文件定位问题。
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <fcntl.h>
void func()
{
printf("func!\n");
int *p = NULL;
*p = 8;
printf("func! %d\n",*p);
}
int main(void)
{
const char *path = "/mnt/hgfs/share/test/core.%e.%p";
int fd = open("/proc/sys/kernel/core_pattern", O_WRONLY);
if (fd < 0)
{
printf("open fail, errno:%d, %s\n", errno, strerror(errno));
return -1;
}
write(fd, path, strlen(path));
close(fd);
#if 1
struct rlimit rl = {RLIM_INFINITY, RLIM_INFINITY};
if (0 != setrlimit(RLIMIT_CORE, &rl))
{
printf("setrlimit fail, errno:%d, %s\n", errno, strerror(errno));
return -1;
}
#endif
func();
return 0;
}
gcc -g coredump.c -o coredump
运行程序后在指定的路径下生成了core文件。
成功将路径写到/proc/sys/kernel/core_pattern
我们可以看到core文件其实也是一种ELF格式。
通过gdb来查看core文件。可以看出在coredump.c的第13行发生了SEGV