望普陀而路远,去罗浮以何及!朝受命,夕饮冰,燃灯不熄!

后羿系统VM-Agent模块内存泄露问题抽丝剥茧

上一篇 / 下一篇  2009-11-16 12:10:00 / 个人分类:语言

先来说一下VM-Agent(gcc开发)从原型到当前版本的架构变化:

!o%`0{'Z?@0

原型阶段:VM-Agent有一个进程与后羿控制系统(erlang实现)的NC进行通讯,根据NC传达的请求,进行VM的调度。51Testing软件测试网k*{*P [s.y"c

当前阶段,把VM-Agent的通讯过程全部交给控制系统的NC本身进行处理,VM-Agent以命令行程序的形式供NC调用。51Testing软件测试网$P^0K0~!fL

这样的好处在我看来有三个:

sQ [x!J$WK.]0

1、  从架构上看更合理,所有通讯相关的处理都由控制系统接管。

OO4Z[z+eKz0

2、  降低了C程序员的开发难度,现在可以把更多的精力放在对VM本身的研究与处理上。

)U5rus VO0

3、  在原有架构上则可能出现《高质量C/C++编程指南》说的:“含有内存错误的函数每被调用一次就丢失一块内存。刚开始时系统的内存充足,你看不到错误。终有一次程序突然死掉,系统出现提示:内存耗尽。”,我估计这也可能是开发人员改变架构的原因之一吧;但现在采用命令行方式,就算有内存泄露出现,命令行结束,进程退出,所有分配的内存也将被操作系统收回。51Testing软件测试网g+?G&j,M'U%WgW

那么可能有人要问,既然已经用命令行了,那还管什么内存泄露呢,让它露吧,无所谓,是这样的吗?请仔细看《高质量c/c++编程指南》第7.6章,这一段经典的例子,正是VM-Agent发生内存泄露的相同原因:51Testing软件测试网aysboQ

51Testing软件测试网+^pJqe&H;j4Dq$o

好了,现在进行正题,看看我是如何抽丝剥茧,定位VM-Agent的内存泄露的:

@&rDWS]!_*D$j0i0

一、环境:

u+@d`.`g-p n0

Linux+gcc+VIM+ctag+ valgrind

O0CKa~B$?1Y0

二、简要描述一下测试用例

z.ZL$^s0

调用VM-Agent的./vmcli –methodname=startVm……命令(后面更多参数略),启动(创建一个)虚拟机51Testing软件测试网q Y"Bl8P

两个用例:A.虚拟机已经启动后执行启动命令,启动失败。B.此虚拟机没有启动时执行启动命令,启动成功,可以用xm list查看,也可ssh直接登陆这个新启动的虚拟机,也可以直接看命令返回值。

@_diky@u0

三、过程51Testing软件测试网I~vz5k1{

当这两个用例分别都执行成功后,我对其分别进行了一个内存泄露的检测:

[%N Oq2q'Mc(vl0

1、  valgrind ./vmcli–………(启动VM失败的用例),结果报告:51Testing软件测试网,w'[$he{

 

XR1?Bq0ps.~ [0

可见,没有什么问题,非常正常。

:An(b8y#@7N?B,u0

2、  valgrind ./vmcli–………(成功启动VM的用例),结果报告:

#k K.cvkkw:^4H Iz0

51Testing软件测试网!ml:p"h? g+K

 

*Y7LX Eu0

从上面的红字可以看到更简单的定位方法,一开始没注意到这一句只看上面两句关键的了,真是折腾了我半天,不过也用手工追踪的方式找到了(在VIM下用ctag会帮助你在程序中实现跳转)。51Testing软件测试网b)]!n@ t#_c(FV

好,现在假装我们都没看到红字部份,继续追踪,从main一直ta到关键代码中,直接到这段代码中看:51Testing软件测试网oHN Xu'kUu

 

N)^'g+@7`xL S-m0

                                  51Testing软件测试网(J4N.hcYR,MF*_

继续ta到get_instance_xml_file中,发现只有两个静态的数据,使用了snprintf方式给值,很安全嘛,也没有动态内存的分配,不过 ,这个东东引起了我的注意,看看用在什么地方的:

hp5rLz?x3C6x0

 51Testing软件测试网y.gY3g3nYl6u

而这个char ** xml是从这里申明的,正是main->do_run…->get_instance…->replace_string这个可疑的家伙,ta进去看看吧:51Testing软件测试网w&Z\R(?L

 51Testing软件测试网C.u{ O5t pn ?.pL pz

怎么样,看出什么来了没?是不是和《高质量c/c++编程指南》7.6中的例子惊人的相似?

+m`3\/w S%K0

上图也说明了为什么replace_string被调用了多次,最后只有一块2048内存泄露的原因!

0`E$J Z6E3m0

 51Testing软件测试网U I;d5` n+f

好,现在我们再用valgrind –leak-check=full ./vmcli –methodname=startVm….来看看结果:

V ZQ'Zg\0

        51Testing软件测试网J1v/KRJ].pb

         最后被定位到replace_string,在代码中的第56行,正是动态分配了内存的那一行,唉,早加上这个参数,我就不用这么折腾了!:(

l!i+K~7v6W0

         至于valgrind为什么能做到这种定位,从上面也可看出,是它的桩函数的作用,这项强大的功能,我们也完全可以自己实现,用它来做单元测试,你甚至不需要Cunit这样的工具都行,当然有Cunit还是要用的,不折腾嘛。

%Z{s(q+b;\R2q0

 51Testing软件测试网 F U^7O k0Ej

四、总结

&{}$_-Y{ [A0

写C语言的规范问题,内存应该是谁使用谁申请,谁申请,谁释放,从VM-Agent代码可以看出程序员当时的思路:do_run_instance申明了char *xml,但do_run_instance没有申请内存,当然不用释放,当这个char*xml经过三个函数的不断中转到了最后一个replace_string函数时,replace_string也不能释放,因为调用者还要使用呢,你要释放了,人家用什么,哈哈,真是杯具呀。这段代码让我想起一个笑话(如果你没笑,就当我讲的是故事):

)HX;e|F8Y8qFo0

有一哥儿们叫了一碗豆浆(价值1元),伙计把豆浆给他后,他说他不要了,把豆浆换成两根油条(两根也是1元),这哥儿们吃完后不付钱就要走,伙计说:“你吃的油条还没给钱呢”,他说:“油条是我用豆浆换的,为什么要给钱?”,伙计说:“可是豆浆你也没给钱呀?”,这哥们一甩手说:“我又没喝豆浆,为什么要给钱?”,然后扬长而去!。。。。,于是内存泄露就此发生了,杯具!

;E^)I-E UiP5[[0

TAG: 虚拟机 valgrind VM 内存泄露

 

评分:0

我来说两句

Open Toolbar