2012年5月17日星期四

linux 程序调试

昨天调一个很简单的程序,就是从一个数据库拉取数据放到另一个数据库,谁知道一直出现 Segmentation Fault 的错误,加了printf打印,发现在mysql.close()的时候出现的错误。心里一凉,不会运气这么背吧,碰到mysql的bug?遂google之,然而还是没有找到合理的解释。只有自己调试看看了,怎么调试呢?以前做路由器的时候,也就是printf来打印,如果严重的话内核会打印堆栈,然后track之,就可以定位了。在linux下?晕了。
然后在网上搜索了半天,找到一篇用gdb调试的文章,简而言之就是编译是加-g选项,然后用gdb ./test 来调试,试了一下,确实是在mysql clsoe的时候,调用free_root的时候,在my_alloc.c里面出现错误,但是居然提示找不到my_alloc.c,叉叉啊。
冷静的想了一下,应该不会是mysql的错误,要不之前的代码岂不是问题很多。接下来采用屏蔽代码的办法,发现确实是一段代码对内存操作存在问题,由于是老代码,当时看了有疑问,但是想如果出问题的话早就有问题了,就没多想。
上代码:
01 while ((row = mysql_fetch_row(res)) != NULL)
02 {
03      k++;
04      pColLen = mysql_fetch_lengths(res);
05
06      if(NULL != row && NULL != pColLen)
07      {
08          for(int i=0; i < num_fields; i++)
09          {
10              memset(aryColValue, 0, 1024);
11              if (pColLen[i] > 1024)
12              {
13                  printf("Error:%d\r\n",pColLen[i]);
14                  printf("Error:%s\r\n",row[i]);
15              }
16              memmove(aryColValue, row[i], pColLen[i]);
17              if(utf8Flag==1)
18              {
19                  strFieldValue = Utf8ToGbk(aryColValue);
20              }
21              else
22              {
23                  strFieldValue = aryColValue;
24              }
25              if(!tmpresult.insert(std::map<std::string, std::string>::value_type(fields[i].name, strFieldValue)).second)
26              {
27                  dblog.WriteLog( "Error:tmpresult.insert(%s)失败\n",strFieldValue.c_str());
28              }
29          }
30          result.push_back(tmpresult);
31          tmpresult.clear();
32      }
33 }
memset(aryColValue, 0, 1024);
memmove(aryColValue, row[i], pColLen[i]);
这两句真是坑爹啊,aryColValue只有1024个字节,但是pCollen表示每个字段的长度,很有可能超过1024字节的,而且我看了数据库里面相关字段,是mediumtext类型,最大长度2的24次方-1个字节。这代码,无语了。而且看了其他一些代码,也是有最多1024*5个字节,这太乱来了吧。就是这段代码,直接导致内存操作越界了。
修改为tmpresult[fields[i].name] = row[i];tmpresult是一个map<string,string>,string长度至少是32位的,够用了。

我发现这老代码真是不可相信啊,各种乱来。预期后续可能还会碰到不少问题,这样看了我还要搞不少时间啊。

没有评论:

发表评论