4954|11

222

帖子

2

TA的资源

一粒金砂(高级)

楼主
 

低级bug耗费12小时Fix [复制链接]

 
本帖最后由 lzwml 于 2016-5-4 16:34 编辑

调试某程序非常简单的程序,简单到认为不可能存在缺陷,但该BUG处理时间超过12小时:
  • 程序属于后台进程,监控系统每隔15秒检查外设IO状态,IO异常后发出报警或复位外设,外设都在linux下有/sys/class等文件节点

程序有规律性4-5小时后崩溃




程序崩溃原因也非常简单:
  • 某文件反复打开未释放,打开文件数超过linux单进程最大打开文件数。
  • 未对文件打开成功检查,持无效文件描述符写文件导致Segmentation fault


下面是排除过程
下面是程序运行打印信息,可看到文件在调用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的主要代码

  1. int ti_ck_mutil(struct ck_self *ptiem, int len)
  2. {
  3.         FILE *stream;
  4.         char strout[256];
  5.         int ret;
  6.         int failcount = 0;

  7.         for (int i = 0; i < len; i++) {
  8.                 printf("%s()%d\n", __FUNCTION__, __LINE__);          //226行
  9.                 stream = popen(ptiem[i].dir, "r");                                //未检查文件是否成功
  10.                 ret = fread( strout, sizeof(char), sizeof(strout), stream);        
  11.                 strout[ret] = '\0';
  12.                 pclose(stream);
  13.                 // ...

  14.         }
  15.         return failcount;
  16. }
复制代码

fread输入参数只有4个,可能存在的错误无非是3点:
  • 被编译器优化后strout的缓存不是256(纯属胡乱猜测,可以通过查看map文件看到具体大小)
  • fread写入最后一个字符时溢出(实际我读写的文件不超过64byte,不应该超过256)
  • stream文件描述符无效


于是首先对fread修改,保证最后一个字符不被fread填写。
ret = fread( strout, sizeof(char), sizeof(strout) - 1, stream);        
测试依旧崩溃

接着对stream检查,代码如下
  1. int ti_ck_mutil(struct ck_self *ptiem, int len)
  2. {
  3.         FILE *stream;
  4.         char strout[256];
  5.         int ret;
  6.         int failcount = 0;

  7.         for (int i = 0; i < len; i++) {
  8.                 printf("%s()%d\n", __FUNCTION__, __LINE__);  //226行
  9.                 stream = popen(ptiem[i].dir, "r");                  //未检查文件是否成功
  10.                 if (NULL == stream) {
  11.                         continue;
  12.                 }
  13.                 // 读出内容,并在末尾添加字符串终结符号
  14.                 //存在溢出,应该用 sizeof(strout) - 1
  15.                 ret = fread( strout, sizeof(char), sizeof(strout) - 1, stream);        
  16.                 strout[ret] = '\0';
  17.                 pclose(stream);
  18.                 // ...

  19.         }
  20.         return failcount;
  21. }
复制代码

测试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//fd
果不其然文件个数每15s递增一个。当达到最大限制1024后文件打开失败,ti_ck_mutil只能continue,也就是原始程序在大约4小时崩溃的原因(15s打开新文件,大约4.2小时达到1024上限)

全工程搜索open关键词,看各调用后是否存在close,果然有个opendir后忘记closedir
加上后bug解决




此帖出自Linux开发论坛

最新回复

感谢分享经验  详情 回复 发表于 2016-6-28 11:46
点赞 关注

回复
举报

2万

帖子

71

TA的资源

管理员

沙发
 
解决问题的过程真心不容易
此帖出自Linux开发论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身
 
个人签名

加油!在电子行业默默贡献自己的力量!:)

 

回复

1976

帖子

0

TA的资源

五彩晶圆(初级)

板凳
 
学习楼主的经验
此帖出自Linux开发论坛
 
 
 

回复

5979

帖子

8

TA的资源

版主

4
 
这个调试的过程 值得学习
此帖出自Linux开发论坛
 
个人签名生活就是油盐酱醋再加一点糖,快活就是一天到晚乐呵呵的忙
===================================
做一个简单的人,踏实而务实,不沉溺幻想,不庸人自扰
 
 

回复

2

帖子

0

TA的资源

一粒金砂(初级)

5
 
学习一下,好东西
此帖出自Linux开发论坛
 
 
 

回复

6423

帖子

17

TA的资源

版主

6
 
复现时间太长,搞三遍十二小时就没了,真心伤不起
此帖出自Linux开发论坛
 
个人签名training
 
 

回复

3238

帖子

5

TA的资源

五彩晶圆(中级)

7
 
能重复性复现问题还好,就担心有些问题概率性出现,那就很麻烦了
此帖出自Linux开发论坛

点评

也是运气好,如果最初的程序没有崩溃,而是无限continue,定位就更加难,都不清楚为什么程序在跑着而不工作  详情 回复 发表于 2016-5-5 07:57
 
个人签名淘宝:https://viiot.taobao.com/Q群243090717
多年专业物联网行业经验,个人承接各类物联网外包项目
 
 

回复

222

帖子

2

TA的资源

一粒金砂(高级)

8
 


也是运气好,如果最初的程序没有崩溃,而是无限continue,定位就更加难,都不清楚为什么程序在跑着而不工作
此帖出自Linux开发论坛
 
 
 

回复

5

帖子

0

TA的资源

一粒金砂(初级)

9
 
原来如此!!
此帖出自Linux开发论坛
 
 
 

回复

456

帖子

2

TA的资源

一粒金砂(中级)

10
 
感谢分享经验
此帖出自Linux开发论坛
 
 
 

回复

58

帖子

0

TA的资源

一粒金砂(初级)

11
 

感谢分享经验
此帖出自Linux开发论坛
 
 
 

回复

58

帖子

0

TA的资源

一粒金砂(初级)

12
 

感谢分享经验
此帖出自Linux开发论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表