调试某程序非常简单的程序,简单到认为不可能存在缺陷,但该BUG处理时间超过12小时:
程序有规律性4-5小时后崩溃 程序崩溃原因也非常简单:
下面是排除过程 下面是程序运行打印信息,可看到文件在调用ti_ck_mutil函数第266行时崩溃 TickStatusIO():124 ti_ck_mutil():266 ti_ck_mutil():272 ti_ck_mutil():276 TickStatusIO():105 ti_ck_mutil():266 Segmentation fault (core dumped)
gdb打开coredump查看栈状态 (gdb) bt #0 0x401b28e0 in vfwprintf () from /lib/libc.so.6 #1 0xe92d47f0 in ?? () #2 0x00009d10 in ti_ck_mutil (ptiem=0xbebffa4c, len=1) at src/ckself.c:268 #3 0x00008e2c in TickStatusIO () at src/initgpio.c:106 #4 0x00009238 in main (argc=1, argv=0xbebffbf4) at src/initgpio.c:304
最终显示在c库函数vfwprintf里崩溃,并在在二行的栈地址以及崩溃了,显示" in ?? ()",他的调用者正是ti_ck_mutil,并且更进一步显示崩溃点在268行而不是266行。
如下是ti_ck_mutil的主要代码
int ti_ck_mutil(struct ck_self *ptiem, int len)
{
FILE *stream;
char strout[256];
int ret;
int failcount = 0;
for (int i = 0; i < len; i++) {
printf("%s()%d\n", __FUNCTION__, __LINE__); //226行
stream = popen(ptiem[i].dir, "r"); //未检查文件是否成功
ret = fread( strout, sizeof(char), sizeof(strout), stream);
strout[ret] = '\0';
pclose(stream);
// ...
}
return failcount;
}
复制代码 fread输入参数只有4个,可能存在的错误无非是3点:
于是首先对fread修改,保证最后一个字符不被fread填写。 ret = fread( strout, sizeof(char), sizeof(strout) - 1, stream); 测试依旧崩溃
接着对stream检查,代码如下 int ti_ck_mutil(struct ck_self *ptiem, int len)
{
FILE *stream;
char strout[256];
int ret;
int failcount = 0;
for (int i = 0; i < len; i++) {
printf("%s()%d\n", __FUNCTION__, __LINE__); //226行
stream = popen(ptiem[i].dir, "r"); //未检查文件是否成功
if (NULL == stream) {
continue;
}
// 读出内容,并在末尾添加字符串终结符号
//存在溢出,应该用 sizeof(strout) - 1
ret = fread( strout, sizeof(char), sizeof(strout) - 1, stream);
strout[ret] = '\0';
pclose(stream);
// ...
}
return failcount;
}
复制代码 测试4小时后程序不崩溃,但当IO异常后也既不报警也不复位,相当于程序不工作了,由上面代码可知必定是popen失败导致,函数内部一直continue。
查看man popen
RETURN VALUE The popen() function returns NULL if the fork(2) or pipe(2) calls fail, or if it cannot allocate memory.
|
man fork
RETURN VALUE On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is set appropriately. |
问题很明显popen的失败是由于fork函数失败或内存不足,而这两个失败的原因是不能创建子进程。 该程序运行内存极小,不可能引发内存不足。 根据经验linux对每个用户默认打开文件数做了限制,也预示着代码里有未释放的文件描述符
为了证明再次运行应用程序,接着每秒查看程序打开的文件个数 watch -n 1 ls /proc/<pid>/fd 果不其然文件个数每15s递增一个。当达到最大限制1024后文件打开失败,ti_ck_mutil只能continue,也就是原始程序在大约4小时崩溃的原因(15s打开新文件,大约4.2小时达到1024上限)
全工程搜索open关键词,看各调用后是否存在close,果然有个opendir后忘记closedir。 加上后bug解决
欢迎微博@EEWORLD
|