[发明专利]基于流敏感上下文敏感指向图的内存泄漏检测方法有效
申请号: | 200910227074.8 | 申请日: | 2009-12-01 |
公开(公告)号: | CN101710303A | 公开(公告)日: | 2010-05-19 |
发明(设计)人: | 王戟;马晓东;董威;徐厚峰;刘万伟 | 申请(专利权)人: | 中国人民解放军国防科学技术大学 |
主分类号: | G06F11/36 | 分类号: | G06F11/36 |
代理公司: | 国防科技大学专利服务中心 43202 | 代理人: | 郭敏 |
地址: | 410073 *** | 国省代码: | 湖南;43 |
权利要求书: | 查看更多 | 说明书: | 查看更多 |
摘要: | |||
搜索关键词: | 基于 敏感 上下文 指向 内存 泄漏 检测 方法 | ||
1.一种基于流敏感上下文敏感指向图的内存泄漏检测方法,其特征在于包括以下步骤:
第一步,利用编译器对被检测程序做词法分析、语法分析,生成编译器自身支持的中间文件;
第二步,使用编译器自带的指针分析器或其他独立的指针分析器对中间文件做流敏感、上下文敏感的指针分析,在程序控制流图中的每个代表程序语句的结点上生成指向图,指向图是程序运行时指针变量之间、或者结构中的域指针之间、或者指针变量和域指针之间指向关系的一种表示形式,它是一个有向图,图中每个结点代表一个位置集合;位置集合是内存的一种抽象表示,一个位置集合代表程序运行时的一个或者多个内存;在指向图中,如果从结点u到v之间有一条有向边,则u中存在一个元素所代表的内存中保存的地址指向v中某一元素所代表的内存;生成指向图的步骤如下:
2.1获取指向图中的结点:每个指针变量都代表一个位置集合,并作为结点在指向图中出现,结点名称就是变量名字;对于动态分配的内存,每一条分配语句产生一个结点,所有同一语句分配的动态内存使用一个结点来表示,用语句所在的行数作为结点的名字;
2.2逐一分析程序中的语句,每条指针赋值语句产生一个指向关系,即赋值语句中指针变量指向表达式的值,由此产生指向图的一条边;
2.3合并指向图中的指向关系:若某一条语句s有多个直接前驱语句可直接抵达该语句,则合并这些前驱语句传递过来的指向关系所代表的边,使得指向图成为流敏感的;所述直接前驱语句是指该语句执行完后下一步可直接执行s;
2.4在程序的过程间生成指向关系:通过计算形参实参的值,在程序的入口处合并过程在不同调用上下文中所产生的指向关系,使得指向图成为上下文敏感的;
2.5判定当前语句是否导致新的指向关系出现,若出现,转2.3;若不再出现新的指向关系,则得到了被检测程序的流敏感和上下文敏感的指向图,转第三步;
第三步,利用流敏感和上下文敏感的指向图检测被检测程序的每个过程内是否存在内存泄漏,方法是:
3.1生成过程内初始化信息:
3.1.1基于流敏感和上下文敏感的指向图,针对被测语句s中值被改变的表达式e建立位置集合的集合,该位置集合的集合由左值位置集合和访问位置集合构成,其中左值指保存表达式e的值的内存位置,左值位置集合是e在指向图中的所有左值构成的集合,访问位置集合包含要得到e在指向图中的左值所必须访问的位置集合;
3.1.2建立“被测语句s引发了内存泄漏错误”的前提,从该前提得到下述有用信息:被s所改写的表达式的值是一个指针,并且唯一指向某个将被泄漏的内存;
3.1.3生成数据流事实的初始化信息,数据流事实用一个四元组(d,S,H,M)表示,其中d是被泄漏掉的内存的地址;S代表所有可能指向被泄漏掉的内存的指针的集合;H是必然指向被泄漏掉的内存的表达式集合;M是一定不指向被泄漏掉的内存的表达式集合;对于一条语句s,用init(s,G)表示在指向图G中为s生成的数据流事实的初始化信息;为被测的赋值语句、动态内存分配语句、释放语句、返回语句这四种类型语句生成数据流事实的初始化信息:
3.1.3.1对于赋值语句e0=e1,生成初始化信息的方法为:init(e0=e1,G)=(l,hold(l,G)∩ll(e0,G),{e0},{e1}),其中,hold(l,G)代表指向图G中指向l的那些内存位置;ll(e0,G)代表表达式e0在指向图G中的左值;
3.1.3.2对于动态内存分配语句e0=malloc(),初始化信息的获取方法为:init(e0=malloc(),G)=init(e0=NULL,G);
3.1.3.3对于释放语句free(e),如果e指向一个指针,则检查这个指针是否是某一内存块的唯一持有者,这种情况下采用3.1.3.1的方法为赋值语句*e=NULL生成初始化信息;如果e指向一个结构,则逐一检查该结构中所有的指针域,对于每一个指针域n,采用3.1.3.1的方法为赋值语句e→n=NULL生成初始化信息;
3.1.3.4对于返回语句return e,初始化信息的获取分为两个步骤:第一个步骤是在返回语句之前加入一系列的赋值语句v1=NULL;…;vm=NULL,其中vi是该过程中所有的局部变量,1≤i≤m,然后用3.1.3.1的方法获取这些赋值语句的初始化信息;第二个步骤是对于调用该过程的语句,实际上是把e赋给该调用语句中赋值号左端的表达式,因此把该调用语句看作是一个赋值语句,按照3.1.3.1赋值语句的方式来获取初始化信息;
3.2利用流敏感和上下文敏感的指向图,从当前程序语句处反向找到在其之前执行的前驱语句s,针对s的类型采用下述方法逐步生成新的数据流事实:
3.2.1 s是赋值语句e0=e1时:
3.2.1.1如果一个表达式的左值位置集合和访问位置集合中的元素都不被赋值语句s改写,则该表达式的值在s执行前后保持不变;
3.2.1.2如果e1在s执行之前不指向被泄漏的内存,且某个表达式e在s执行之后指向被泄漏的内存,并且e的左值没有被s所改变,则e在执行之前仍然指向被泄漏的内存;
3.2.1.3如果某个表达式e和e1的左值不被修改,e在执行之后指向被泄漏的内存,e1在执行之后不指向被泄漏的内存,则e执行之前指向被泄漏的内存;
3.2.1.4如果某个表达式e和e1的左值不被修改,e在执行之后不指向被泄漏的内存,e1在执行之后指向被泄漏的内存,则e执行之前不指向被泄漏的内存;
3.2.1.5若表达式e1的左值不被语句改变,则其右值也不会改变,右值指一个表达式的实际值;
3.2.1.6若e0的左值没有被语句改变,则e0在语句s执行之后与e1在语句s执行之前的值相同;
3.2.2 s是动态内存分配语句或释放语句,则s被看作源数值为NULL的赋值语句,然后使用3.2.1中赋值语句的方法生成新的数据流事实;
3.2.3 s是比较语句:e0=e1或e0≠e1,对于e0=e1,将e1中与e0中相同的表达式指向的内存位置设置为e0中相应表达式指向的内存位置;对于e0≠e1,将e1中与e0中相同的表达式指向的内存位置设置为与e0中相应表达式指向的内存位置不同;
第四步,获取MOD信息,MOD信息是调用语句中被调用过程可能产生的修改副作用,获取MOD信息就是获取数据流事实中出现的表达式在被调用过程中被修改的结果;MOD信息分显式和隐式两种:一个过程的显式MOD信息是指该过程所有赋值语句以及调用语句中赋值号左端表达式的左值的集合;一个过程的隐式MOD信息是被该过程调用的过程所修改的内存位置;获取MOD信息的方法是:先计算出每个过程可能修改的内存位置集合,然后利用调用点的上下文信息,计算出在每个过程调用点上会被调用过程所修改的内存位置的集合;具体步骤如下:
4.1对于过程p,通过找到p中所有赋值语句的左端表达式,获得显式修改的内存位置的集合,将这个集合传回到那些调用p的过程中的调用点;
4.2在回传过程中,根据调用点的上下文信息对传回的内存位置进行削减,去掉那些不可能在当前调用环境下被修改的内存位置,对于任何一个调用了p的过程q而言,这初步求得了过程q隐式修改的内存位置;
4.3判定过程q被修改的内存位置集合是否发生了变化,若变化则转4.1,若过程q被修改的内存位置集合不再发生变化,则内存位置集合到达一个稳定状态,这时即得到了MOD信息,计算结束;
第五步,对被检测程序检测过程间的内存泄漏,方法是:
5.1若过程调用语句e=p(a0,...,an)所在的过程为q,通过以下方式来获取e=p(a0,..., an)的初始化信息:
5.1.1 e是一个全局表达式时,逐一把p(a0,...,an)中的return reti语句替换为e=reti,通过3.1.3.1的方法逐一计算被调用过程p(a0,...,an)中语句e=reti的初始化信息来获取语句e=p(a0,...,an)的初始化信息,其中reti代表p(a0,...,an)中第i个返回语句的返回值,0≤i≤K,K为p(a0,...,an)中返回语句的数目,K为整数;
5.1.2 e是过程q中的一个局部表达式,并且在p中被修改时,不获取初始化信息,直接报出一个内存泄漏错误;
5.1.3 e是过程q中的一个局部表达式,并且没有被p修改时,针对每个可能被泄漏的内存和p(a0,...,an)中的每个返回语句return reti,分别计算该返回语句的初始化信息和需要保存的信息:初始化信息为init(return reti,Gri)=(d,S,H,M),其中d是可能被泄漏掉的内存的地址,S=hold(d,Gri)∩(ll(e,Gs)∪local(p)),hold(d,Gri)代表指向图Gri中指向d的那些内存位置,ll(e,Gs)代表表达式e在指向图Gs中的左值,local(p)代表p中所有的局部变量,Gs是语句e=p(a0,...,an)执行之前的指向图,Gri是p中每个返回语句si之前的指向图,
5.1.4在从调用过程进入被调用过程之前,采用函数catch(c,D,s)获取要保存的信息,即数据流事实中那些在当前过程中可见的表达式,其中c是返回语句所处的调用上下文,D是进入被调用过程之前的数据流事实,s代表调用语句e=p(a0,...,an),catch函数的初始化信息计算过程如下:
catch(c,D,s)=(d,S,H,M),其中:
●S=hold(d,Gs)∩ll(e,Gs),hold(d,Gs)代表指向图Gs中指向d的那些内存位置;
●H={e},
从被调用过程返回之后,计算出的(d,S,H,M)被合并到返回的数据流事实中;
5.2逐步计算生成新的数据流事实,包括以下步骤:
5.2.1计算需要保存的信息:使用s代表e=p(a0,...,an),该语句执行之后的数据流事实为(d,S’,H’,M’),当前的调用上下文为c,则需要保存的信息catch(c,(d,S’,H’,M’),s)=(d,S,H,M),其中:
S=hold(d,Gs)∩(S’-modp(p)),modp(p)是过程p所修改的位置集合;
ll(em,Gs)代表表达式em在指向图Gs中的左值,al(em,Gs)表示表达式em在指向图Gs中的访问位置集合;em是a0,...,an中存在的任意一个表达式;
5.2.2把调用语句处的数据流信息映射到被调用过程的出口处,如果调用语句处的数据流信事实为(d,S’,H’,M’)),则被调用过程出口处的数据流事实为(d,S’,H,M),其中H和M分别只包含H’和M’中的全局表达式;
5.2.3当在被调用过程中后向分析到达过程的入口处时,就要返回调用者中,此时需处理返回过程中的信息映射,步骤是:
5.2.3.1把形参的值赋给调用语句处对应的实参;
5.2.3.2删除当前数据流事实中H和M中p的局部表达式;
5.2.3.3把此处的数据流事实和5.1.4步中计算的调用语句e=p(a0,...,an)处使用catch函数所保存的信息做合并操作,即直接对两个数据流事实中对应元素做集合的并操作,生成的新数据流事实再和该语句之前程序点上的数据流事实进行合并操作;
第六步,判断新得到的数据流事实是否矛盾以检测是否有内存泄漏,步骤如下:
6.1使用第3.2步的过程内生成新的数据流事实的方法更新数据流事实;
6.2使用第5.2步过程间生成新的数据流事实的方法,通过形参实参的计算,在过程间传播信息,更新数据流事实;
6.3当新的数据流事实与原数据流事实不同时,转6.1;当新的数据流事实与原数据流事实相同,即新的数据流事实不再变化时,执行6.4;
6.4逐个检查每个数据流事实中是否有矛盾:对于数据流事实(d,S,H,M),如果一定不指向被泄漏掉内存的表达式集合M与必然指向被泄漏掉的内存的表达式集合H或可能指向被泄漏内存的指针集合S的交集不为空,则存在矛盾;当存在矛盾时,所检查的语句s不会引发内存泄漏;当不存在矛盾时,则存在内存泄漏,检测结束。
2. 如权利要求1所述的基于流敏感上下文敏感指向图的内存泄漏检测方法,其特征在于计算不可达的内存位置,在MOD计算结果中去掉这些不可达的内存位置,如果内存位置l对下面三个条件均不满足,则l是不可达的:
(1)l属于全局变量;
(2)l是动态分配的内存;
(3)l是调用语句执行之前的指向图中从实参的左值对应的结点出发沿指向边可达的结点,其中,对于实参r,若r在指向图中的左值存在,则求出从这些左值出发可达的内存位置;若某一实参r的左值不存在,则在指向图中沿着指向边得到从*r的左值出发可达的内存位置,*r是r所指向的值。
该专利技术资料仅供研究查看技术是否侵权等信息,商用须获得专利权人授权。该专利全部权利属于中国人民解放军国防科学技术大学,未经中国人民解放军国防科学技术大学许可,擅自商用是侵权行为。如果您想购买此专利、获得商业授权和技术合作,请联系【客服】
本文链接:http://www.vipzhuanli.com/pat/books/200910227074.8/1.html,转载请声明来源钻瓜专利网。
- 上一篇:伸缩式阀门开关器
- 下一篇:一种获得冲击响应、频偏估计的方法及装置