内存问题是C程序员永久的话题,也是最能让C程序员心痛的话题。内存bug即隐秘,危害又大,而且往往当你解决了它之后,你会发现你的错误是多么的低级。以我为例,看下面的两个case:

CASE1

背景: 配置信息读取
Bug现象: 通过打印语句观察到,在配置读取中间时刻,某一指针突然被置为NULL,出core。
耗时: 6小时
问题所在及分析: 经过6小时的不懈努力,终于发现了这一让我哭笑不得的低级错误。问题原因大致是这样的:
我定义了一个存储配置信息的结构体变量指针,并在初始化的时候给该指针在共享内存中分配空间,下面的代码就是我分配空间时的代码
xx_t *p;
…//

p = xx_malloc((void**)&p, sizeof(p));

正确的代码
p = xx_malloc((void**)&p, sizeof(xx_t));
我想我之所以花了那么长时间才找到这个问题,是被一些奇怪的现象所蒙蔽了,也是查找内存误操作问题经验不足所致。

CASE2

背景: Ftp客户端获取文件列表
Bug现象: 当Server端目录下文件个数很多时(如>=1000),以后的ftp操作全部失效,出现errno = 95、134等。
耗时: 7小时
问题所在及分析: 由于在文件少的时候,我们的ftp client工作一切正常,所以我们最开始怀疑的是接收数据缓冲区开得不够大。但是在加大缓冲区之后,问题依然存在。由于对FTP协议并不是很熟悉,导致在一些细节上又耽误了很多时间。之后我们看到一个奇怪的现象就是errno 95的出现,说明我们的ctrl channel socket已无效。我们使用最传统的调试方法使用打印语句,从创建Ctrl Socket开始,一直追踪到问题发生区域,并锁定一块区域的代码,在该代码之前ctrl socket为31, 之后ctrl socket居然变为1, 而且这段代码中并没有操作socket的语句。我们分析有两个可能:
1)这段代码运行时间过长,导致Ftp server关闭link;
2)该段代码有内存误操作,导致内存被污染;

我们使用排除法,首先注掉那段代码,取而待之的是sleep(30),我们想如果sleep 30秒,Ftp server不关闭link的话,那么就是第2种可能了,结果是的确有“内存误操作”,静态检查代码后,锁定在一个给指针数组赋值的语句上,察看上层代码后,发现这就是问题所在。用代码说明问题大致是这样的:
char *flist[200];

…//

int cnt = 0;
while (读取数据不为空) {
 p = malloc(…);
 …//
 flist[cnt] = p;
 cnt++; 
}

显然如果数据超过200条,数组必然越界。

总结:经过两个Case中,发现自己在找“内存误操作”问题上的经验不足,但同时经过这两个case,我总结一下几条,可能对以后的bug查找有所帮助。
a) 虽然“内存bug”不易查找和修改,但是一定要摆正心态,首先确定是“内存误操作”带来的bug;
b) “内存bug”绝大多数是极其低级的错误,所以首先要仔仔细细静态检查代码,可以按下面的顺序检查
 .搜索所有的malloc, memset, memcpy一类的内存操作函数,察看是否有“马虎”错误;
 .察看所有的数组变量,看是否有越界嫌疑
c) 打印语句是最简单,但是却是最有效的debug方法(我是这么认为的^_^),要利用好哟。

[注]:errno 95 — Socket operation on non-socket

© 2005, bigwhite. 版权所有.

Related posts:

  1. Effective Java阅读笔记-item16
  2. 开放与封闭
  3. 同步问题讨论-Tony与Alex的对话系列
  4. 如何编写类中的setter和getter
  5. CppUnit入门实践-Tony与Alex的对话系列