面向Xen环境的运行时内存泄漏检测方法及其实现系统的制作方法

文档序号:6382634阅读:566来源:国知局

专利名称::面向Xen环境的运行时内存泄漏检测方法及其实现系统的制作方法
技术领域
:本发明涉及虚拟化软件测试
技术领域
,特别是一种面向Xen虚拟化环境的运行时内存泄漏检测方法及系统。
背景技术
:Intel与AMD分别提供了硬件辅助虚拟化技术(Virtualization),使原本互不兼容的各类大型主机虚拟化技术逐步惠及X86架构微型计算机系统,通过虚拟机管理器(VMM)虚拟所有硬件接口,基于物理硬件之上生成可以独立运行客户操作系统(GuestOS)的虚拟机(VM),以VMM为中心使PC服务器平台虚拟化,虚拟化技术为各种应用提供高性能和高可靠的廉价服务器,也为当前云计算提供了可行的技术支持,比如VMware、Citrix的各种云计算解决方案。虚拟化应用环境的代价越来越低,虚拟化技术向人们展示了广阔的应用前景。虚拟机(VM)是指在一个硬件平台上模拟多个独立的、ISA结构和实际硬件相同的虚拟硬件系统,在每一个虚拟硬件系统上都可以运行不同的操作系统,即客户操作系统(GuestOS),这些GuestOS通过VMM访问实际的物理资源。因而整个虚拟机系统是以VMM为中心来实施资源的管理与控制。在各种虚拟机环境中运行的系统大都是长时间不停机系统,如通信领域软件、嵌入式敏感系统等行业性软件、运行在云集群服务器上的服务及各种应用程序,存在一个非常严重的挑战尚缺少可以在虚拟计算环境下检测系统可靠性,特别是最可能使系统崩溃的内存泄漏可行的检测方法。内存泄漏会带来严重的后果,导致系统提供服务延迟缓慢,经常会发生资源耗竭而导致整个系统崩溃。鉴于运行时内存泄漏检测的重要性,国内外做了很多这方面的工作。这些工作根据研究对象所处的状态,基本分为两类第一类是基于用户态的运行时动态检测。这类方法都是基于一种具体的编程语言,在某种平台操作系统上对应用系统进行插桩或嵌入诊断信息或嵌入对操作系统相关操作的监控,都需要重新编译才能对可能的内存泄漏进行判断分析。国内典型的有南京大学徐宝文等人给出了基于源代码插桩的C程序内存使用错误动态检测方法;王勇等人提出了一种统一的内存错误检测模型和接口;西安交通大学冯博琴等人实现了一个Linux平台下针对C/C++语言的动态内存检测模块,以检测内存泄漏、内存写溢出、释放野指针和内存管理函数的不匹配等问题;中国科学技术大学孔德光等人实现了一个堆内存泄漏检测工具,采用红黑树管理所分配的堆内存,动态跟踪程序在执行过程中堆的分配释放情况;华南理工大学刘发贵等人实现一个嵌入式Linux平台下数据采集和测试分离的交叉测试的嵌入式软件动态内存检测工具。以上工作大多只是针对特定的语言、特定的操作系统或特定的硬件环境。国外最典型的工作有Purify、Jprobe、Jprofiler、Insure++、BoundsChecker、CTrigger>PENELOPE等等。另一类是基于系统内核的运行时动态检测。这类方法认为内存泄漏的本质是应用程序的不合理的内存资源使用造成的,通过对操作系统内核中内存资源的系统调用分析进行内存泄漏的检测。这种检测要通过捕捉系统调用因而相对更困难。伊利诺伊大学香槟分校YuanyuanZhou等提出一种SafeMem的方法检测内存泄漏,需要结合内存控制器ECC的纠错功能,对程序的内存泄漏进行快速有效的动态监视。在程序运行时记录程序动态分配的内存资源和释放信息,然后分析是否存在内存泄露。通过在Linux中加入三个系统调用WatchMemory()>DisableffatchMemory()和RegisterECCFaultHanderO,调用WatchMemory时disableECC功能,并将内存地址对应的内存控制器缓冲的32位或64位数据修改,但不改ECCcode,这样下次再访问该内存地址位置时,内存控制器检验ECC得出的ECCcode与控制器中的原ECCcode不一致,并且是多位不一致,所以给CPU发中断报错,使用ECC注册的RegisterECCFaultHanderO处理程序进行处理,最后调用DisableWatchMemory恢复ECC功能。实验结果比使用Purify检测提高了很多。使用ECC的方法很新颖,但是它需要针对特定的内存控制器设计,目前通用性还不好。哈佛大学的Bungale在文献提出了一种在虚拟机管理器之上的操作系统内核级的调试机制PinOS。该方法不仅实现了用户态的监控,也扩展到了内核级的全状态监控,使得其性能下降很大,大多数情况下降低到5060倍,这限制了其在真实环境中使用。还有Swaminathan提出基于虚拟化可以优化软件开发的测试过程,北京大学的汪小林提出了一种基于虚拟机管理器进行内存泄漏监控的方法,并在KVM虚拟平台上进行了实现。为了解决这一科学问题,需要考虑虚拟化计算环境平台兼具开放性、复杂性和动态性的特征。Xen起源于2002年由剑桥大学主持的开源虚拟化项目,2007年Citrix公司发布了Xen3.1版,标志着Xen真正走向成熟,Xen已获得了业界的广泛认可与应用。传统的运行时内存泄漏检测方法受到虚拟计算平台应用复杂、硬件需求千差万别等各种条件的约束。本发明在研究现有内存泄漏动态检测方法的基础上,采用Xen虚拟化技术,提出一种面向Xen虚拟计算环境的内存泄漏检测VMLD(VirtualizationMemoryLeakDetecting)方法。
发明内容本发明的目的在于提供一种面向Xen虚拟化环境的运行时内存泄漏检测方法及系统,该不仅适用范围广,可以实现跨平台的运行时内存泄漏检测,而且实现方法简单,对现有操作系统改动小,适用强。为实现上述目的,本发明的技术方案是一种面向Xen虚拟化环境的运行时内存泄漏检测方法,包括以下步骤步骤1:动态分析受监控程序的内存使用行为,对内存对象进行分组,形成多个内存对象组,并对各内存对象组相关信息进行维护和更新;所述内存对象是指经系统调用分配的内存块;所述内存对象生命期是指内存对象从被分配到被释放的这段时间;所述内存对象组是内存对象的集合,每个内存对象都采用一个元组(size,callChain)来表示,size表示内存对象的大小,callChain表示在分配内存对象时的栈调用(call_stack);步骤2:基于观测到的行为特征检测潜在的内存泄漏;步骤3:依据相关规则进行内存泄漏判定。在本发明一实施例中,动态分析受监控程序的内存使用行为,对各内存对象组相关信息进行维护和更新的方法为步骤1.1:对于每一个内存对象组,对其内存分配与释放的行为实施收集,记录相关信息,包括生命期信息、内存使用信息;所述生命期信息包括内存对象组的最大生命期maxLifeTime、最大生命期的稳定时间stableTime,所有的时间都以CPU时间来度量;所述内存使用信息包括内存对象组内的活内存对象信息、最后分配时间、内存对象组占用的总内存空间大小;步骤1.2:对于内存对象组内的每一个活内存对象,记录其内存分配时间,同一个内存对象组内所有活内存对象都以双向链表的形式表示,以便快速查找;步骤1.3:对于内存对象组内的每一次内存分配,即一个新的活内存对象添加到所述双向链表后,对内存对象组的关联信息进行更新,包括活内存对象信息、最后分配时间、内存对象组占用的总内存空间大小;步骤1.4:对于内存对象组内的每一次内存释放,也对内存对象组的关联信息进行相应的更新,包括活内存对象信息、内存对象组占用的总空间大小,同时通过计算被释放内存的分配时间与当前时间的差值,求得被释放内存的生命期IifeTime;步骤1.5:如果被释放内存的IifeTime少于所在内存对象组的maxLifeTime,则所述内存对象组的maxLifeTime不作改变,且stableTime随着被释放内存的CPU处理时间而增力口,否则,对maxLifeTime进行更新,且将stableTime重置为O。在本发明一实施例中,检测潜在的内存泄漏的方法为定义在程序所有可能的执行路径上都不会释放的内存对象为总是泄漏内存对象ALeak;定义在程序有些执行路径上会释放分配,但在程序其他路径上不会释放分配的内存对象为有时泄漏内存对象SLeak;根据内存对象的分组,针对ALeak、SLeak两种不同的泄漏类型,分别采用如下的方法进行检测检测ALeak包括以下步骤步骤2.1:检测每一个内存对象组中活内存对象的数量是否超过了预设的阈值,是则转至步骤2.2,进一步检查该内存对象组使用的内存是否连续增长,否则转至步骤2.3;步骤2.2:如果该内存对象组的最后分配时间离当前时间较近,内存使用仍在增长,则该内存对象组是泄漏嫌疑对象;如果最后分配时间离当前时间较远,即在一段较长时间内都没有内存使用增长,则不会发生内存泄漏;步骤2.3:如果没有超过,则可能是在初始时程序分配了较多内存对象,在整个执行过程中这些对象都在使用;检测SLeak的方法为检测每一个内存对象组中活内存对象的生命期lifeTime,如果所述活内存对象的活动时间超过所在内存对象组的maxLifeTime的两倍,且??,且所述活内存对象在合法的maxLifeTime内动态内存部分不释放,但持续申请,导致所占用的内存越来越多,且所述活内存对象在合法的maxLifeTime内已释放,但多次重复释放,导致不可预计的行为发生,则判断所述内存对象组是泄漏嫌疑对象。在本发明一实施例中,判定内存泄漏的方法为步骤3.1:对于在检测过程中已标记为泄漏嫌疑对象的内存对象,在预定的时间阈值内,如果所述内存对象被再次访问,则所述内存对象为可能泄漏内存对象PLeak,否则所述内存对象确定为泄漏对象;步骤3.2:如果内存对象为PLeak,则重置这个内存对象的分配时间为当前时间;如果这个内存对象重新变为泄漏嫌疑对象,则确认该内存对象为泄漏对象。本发明还提供了一种面向Xen虚拟化环境的运行时内存泄漏检测方法的实现系统,包括Xen内部缓冲区维护模块、控制模块、拦截模块和监视模块。在本发明一实施例中,所述Xen内部缓冲区维护模块定义了若干个有关的超级调用来辅助完成对内存相关操作的捕捉,并获到相关数据,用于泄漏对象分组和判定。在本发明一实施例中,所述控制模块用以控制被测进程,使被测进程主动加载拦截模块,并向拦截模块发送指令,使拦截模块能对被测进程进行修改,拦截被测进程内存的申请和释放操作。在本发明一实施例中,所述拦截模块对被测进程内存的申请和释放操作进行拦截,并通过超级调用将拦截到的数据传送到Xen内部缓冲区维护模块,等待进一步处理。在本发明一实施例中,所述监视模块与拦截模块协同作用,拦截模块负责拦截内存操作行为,监视模块对拦截到的行为进行处理,包括分组确认泄漏对象和可视化处理。与现有技术相比,本发明的有益效果是1、跨平台性。不仅适用于GNU/Linux操作系统,而且对Windows也适用,不仅适用于用户程序,对操作系统内核也同样适用。2、动态性。能够在程序运行时对内存泄漏进行检测,能够在长时间不间断运行的系统中使用,而且效率并不会降低太多。3、透明性。本发明只需要对支撑虚拟机运行的虚拟机管理器进行修改,通过提供超级调用,对内存泄漏进行了有效地检测,不需要修改现有的操作系统,也无需修改被检测程序的代码,对被测程序是透明的。4、有效性。本发明在公认的存在内存泄漏的开源项目中能有效的检测出运行时的内存泄漏。图1为本发明基于Xen平台的VMLD体系结构图。图2为本发明在Xen虚拟计算环境中部署的自定义超级调用示意图。图3为本发明的超级调用StartWrite不可重入情况的流程图。图4为本发明的超级调用StartWrite可重入情况的流程图。图5为本发明监视开始后被测进程中malloc的调用流程图。图6为本发明面向Xen虚拟化环境的运行时内存泄漏检测方法的流程图。具体实施例方式本发明面向Xen虚拟化环境的运行时内存泄漏检测方法,如图6所示,包括以下步骤步骤1:动态分析受监控程序的内存使用行为,根据内存对象所属事件的不同或其它分组原则对内存对象进行分组,形成多个内存对象组,并对各内存对象组相关信息进行维护和更新;所述内存对象是指经系统调用如malloc、realloc、calloc等分配的内存块;所述内存对象生命期是指内存对象从被分配到被释放的这段时间,所述内存对象生命期的取值在一个数学意义上的开区间(0,内;所述内存对象组是指具有某种共性的内存对象的集合,每个内存对象都采用一个元组(size,callChain)来表示,size表示内存对象的大小,callChain表示在分配内存对象时的栈调用(call_stack);步骤2:基于观测到的行为特征检测潜在的内存泄漏;步骤3:依据相关规则进行内存泄漏判定。在步骤I中,动态分析受监控程序的内存使用行为,对各内存对象组相关信息进行维护和更新的方法为步骤1.1:对于每一个内存对象组,对其内存分配与释放的行为实施收集,记录相关信息,包括生命期信息、内存使用信息;所述生命期信息包括内存对象组的最大生命期maxLifeTime、最大生命期的稳定时间stableTime,所有的时间都以CPU时间来度量;所述内存使用信息包括内存对象组内的活内存对象信息、最后分配时间、内存对象组占用的总内存空间大小;步骤1.2:对于内存对象组内的每一个活内存对象,记录其内存分配时间,同一个内存对象组内所有活内存对象都以双向链表的形式表示,以便快速查找;步骤1.3:对于内存对象组内的每一次内存分配,即一个新的活内存对象添加到所述双向链表后,对内存对象组的关联信息进行更新,包括活内存对象信息、最后分配时间、内存对象组占用的总内存空间大小等等;步骤1.4:对于内存对象组内的每一次内存释放,也对内存对象组的关联信息进行相应的更新,包括活内存对象信息、内存对象组占用的总空间大小等等,同时通过计算被释放内存的分配时间与当前时间的差值,求得被释放内存的生命期lifeTime;步骤1.5:如果被释放内存的IifeTime少于所在内存对象组的maxLifeTime,则所述内存对象组的maxLifeTime不作改变,且stableTime随着被释放内存的CPU处理时间而增力口,否则,对maxLifeTime进行更新,且将stableTime重置为O。步骤1.6:在这个过程中,涉及到mallocO、callocO、reallocO、free0等内存调用函数,统一称之为内存分配(allocation)与释放(deallocation),或仅以mallocO、freeO表达。在步骤2中,检测潜在的内存泄漏的方法为定义在程序所有可能的执行路径上都不会释放的内存对象为总是泄漏内存对象ALeak;定义在程序有些执行路径上会释放分配,但在程序其他路径上不会释放分配的内存对象为有时泄漏内存对象SIeak;根据内存对象的分组,针对Aleak、SIeak两种不同的泄漏类型,分别采用如下的方法进行检测检测Aleak包括以下步骤步骤2.1:检测每一个内存对象组中活内存对象的数量是否超过了预设的阈值,是则转至步骤2.2,进一步检查该内存对象组使用的内存是否连续增长,否则转至步骤2.3;步骤2.2:如果该内存对象组的最后分配时间离当前时间较近,内存使用仍在增长,则该内存对象组是泄漏嫌疑对象;如果最后分配时间离当前时间较远,即在一段较长时间内都没有内存使用增长,则不会发生内存泄漏;步骤2.3:如果没有超过,则可能是在初始时程序分配了较多内存对象,在整个执行过程中这些对象都在使用;检测Sleak的方法为检测每一个内存对象组中活内存对象的生命期lifeTime,如果所述活内存对象的活动时间超过所在内存对象组的maxLifeTime的两倍,且??,且所述活内存对象在合法的maxLifeTime内动态内存部分不释放,但持续申请,导致所占用的内存越来越多,且所述活内存对象在合法的maxLifeTime内已释放,但多次重复释放,导致不可预计的行为发生,则判断所述内存对象组是泄漏嫌疑对象。以上两种情况的检测,都只是在程序运行过程中有内存申请与释放行为时才触发这个检测过程,而且这个检测只是在一个预设的检测周期内进行,因而所占用的内存开销不大。在步骤3中,判定内存泄漏的方法为步骤3.1:对于在检测过程中已标记为泄漏嫌疑对象的内存对象,在预定的时间阈值内,如果所述内存对象被再次访问,则所述内存对象为可能泄漏内存对象PLeak,否则所述内存对象确定为泄漏对象;步骤3.2:如果内存对象为Pleak,则重置这个内存对象的分配时间为当前时间;如果这个内存对象重新变为泄漏嫌疑对象,则确认该内存对象为泄漏对象。以上判定过程并没有占用很大的额外开销,仅仅是对少数嫌疑泄漏对象进行确认,因此程序的运行性能并不会受到大的影响。下面结合具体实施例对本发明面向Xen虚拟化环境的运行时内存泄漏检测方法的实现系统进行说明。本发明的实现是基于开源的虚拟机管理器Xen来完成的。我们分别在虚拟机内进行监控,在虚拟机管理器内确认内存泄漏。不同于已有的方法,不仅适用于GNU/Linux操作系统,而且对Windows也适用,不仅适用于用户程序,对操作系统内核也同样适用,从而可以实现真正跨平台的检测运行时内存泄漏。同时实现方法简单,该方法不需要修改现有的操作系统,只需对支撑虚拟机运行的虚拟机管理器进行修改,通过编制虚拟机管理器环境下的超级调用来实现对内存泄漏对象的检测、标识可能的泄漏嫌疑对象,并进行虚拟管理器环境下的泄漏确认。本实施例在`以下实验环境中完成=IntelHM61,双核酷睿二代i3处理器(i3-2120CPU),3.3GHz,3MBCache,2GB内存。Xen4·1.2版本,Linux内核版本Fedora16Linux-3.1.0-7.fcl6.686.PAE,CentOS6,所用编译器GCC的版本4.4.6,SATA串行硬盘。因为Xen支持全虚拟化,我们将相关工作定位在硬件虚拟域HVM上,在Xen虚拟机管理器VMM系统中存在一个轻量级的软件层,向之上的虚拟机提供虚拟硬件资源,同时分配和管理这些资源,并保证虚拟机之间的相互隔离。在Xen系统中,VMM又称为XenHypervisor,简称为Xen,而虚拟机则被称为虚拟域(Domain),每一个Domain用来安装客户操作系统(GuestOS),由Xen控制,以高效的利用CPU的物理资源。每个客户操作系统可以管理它自身的应用。这种管理包括每个程序在规定时间内的响应到执行,是通过Xen调度到虚拟机中实现。特权域DomainO(Dom0)用来辅助VMM管理其他虚拟域,是其它虚拟主机的管理者和控制者。Xen向HVM及DomU提供了管理和虚拟硬件的API抽象层,使DomO拥有真实的设备驱动区(原生设备驱动,nativedevicedriver),能够直接访问物理硬件,它负责与Xen提供的管理工作API交互,可以管理和控制其他域。如图1所示,本发明面向Xen虚拟化环境的运行时内存泄漏检测方法的实现系统由4个功能模块组成,包括Xen内部缓冲区维护模块、控制模块、拦截模块和监视模块。在被监控端,被测进程是未经修改的正在运行的一个进程。如图2,所述Xen内部缓冲区维护模块定义了若干个有关的超级调用来辅助完成对内存相关操作的捕捉,并获到相关数据,用于泄漏对象分组和判定。所述控制模块用以控制被测进程,使被测进程主动加载拦截模块,并向拦截模块发送指令,使拦截模块能对被测进程进行修改,拦截被测进程内存的申请和释放操作;所述控制模块能够动态地控制拦截模块的启动与停止,而不需要重启被测进程等操作,以便整个检测过程不会对被测进程提供的服务造成负面影响。所述拦截模块对被测进程内存的申请和释放操作进行拦截,并通过超级调用将拦截到的数据传送到Xen内部缓冲区维护模块,等待进一步处理。所述监视模块与拦截模块协同作用,拦截模块负责拦截内存操作行为,监视模块对拦截到的行为进行处理,包括分组确认泄漏对象和可视化处理。由于Xen提供的服务并不是和操作系统直接相关的,遇到超级调用的时,无论是Windows系统还是Linux系统,都能直接返回Xen。利用这个特性,可以屏蔽掉软件实现的一些系统相关的细节,以便用更加统一的策略来完成内存申请和释放的拦截。下面进一步阐述上述各个模块的实现方法。l、Xen内部缓冲区维护模块自定义6个有关的超级调用来辅助完成对内存相关操作的捕捉,并获到有关的数据(l)intenvTestO:调用号为0x100。功能是检测运行环境,是否是修改过的Xen内核,运行环境中是否包含这些自定义的超级调用。如果是,则返回0x12345678,否则就不是。具体实现形式如;regs_>eax=0x12345678。(2)voidclearO:调用号为0x102,作用是重置key,重置缓冲区锁定状态,清空缓冲区。在Xen添加的代码中,维护着一些数据。主要用于标志当前有权限的写入者的CurrentKey、分配给写入者权限的Key、保存写入数据的Buffer、用来标识当前Buffer使用状态的ReadPos与WritePos。clear的作用就是把以上数据全部置O。(3)intstartffrite():调用号为0x103。功能是向Xen内核申请缓冲区的写入。为防止多个写入者同时访问缓冲区出现写冲突,破坏数据暂存缓冲区的一致性。具体的方法是,写入者在需要写入数据之前,先向修改过的Xen内核通过超级调用申请一个写入key,这个写入key作为有权限向暂存缓冲区写入数据的证明。拿到了有效的key以后,就可以开始向缓冲区内写入数据。在写入完毕后,释放所获取的key,释放之后key即失效。在这个操作中,涉及到了共享的变量“当前有写入权限的key”。若不考虑key获取函数的可重入性,则可能会出现多个写者同时进行数据写入的情况。在图3中,最后两边都返回了key,是错误的。利用Intelx86/64架构CPU提供的指令,可以实现加锁的原子操作,重新设计后,得到流程图4。所以利用LOCK前缀和CMPXCHG指令,可以实现比较并赋值的原子操作。若返回值非0,则获取到了对Xen内缓冲区的写入权限。(4)voidendffrite(intkey):调用号为0x105。功能是结束写入操作,释放key。该操作中,也涉及到比较与赋值的问题,故同StartWrite调用类似,使用了LOCK和CMPXCHG指令实现原子操作比较参数中的key是不是当前有写入权限的key,如果是则把当前有权限写入的key设置为O(未使用),否则返回失败。(5)intsend(intkey,void*buf,intlen):调用号为0x104。功能是向Xen的暂存缓冲区写入数据,返回O代表写入失败,I代表写入成功。如果要求写入的数据超过Xen缓冲区的空闲大小,则写入失败。(6)intrecv(void*buf,intlen):调用号为0x106。功能是从Xen读取数据,返回读取的字节数。2、控制模块在被检测程序运行时,控制模块可以单独运行,在虚拟机内部对被测进程进行控制。它可以将拦截模块动态地载入到运行中的被测进程里,使得拦截被测进程中内存的申请与释放操作变为可能。在把拦截模块动态载入内存之后,它能够动态地控制拦截模块的启动与停止,且不需要重启进程等操作,实现整个检测过程中不影响被检测进程继续提供服务。要对一个进程的内存空间的数据进行读写访问,需要通过操作系统提供的系统调用来实现。在Windows下,可以使用DebugActiveProcess、WaitForDebugEvent>Get/SetThreadContext和Write/ReadProcessMemory等函数对运行中的进程进行调试控制。在Linux下,可以通过ptrace函数对运行中的进程进行调试控制。许多在x86构架上运行的程序,函数调用的时候使用了cdecl调用约定。该调用约定将参数从右到左压入堆栈,在函数返回的时候将返回值保存在EAX寄存器中,参数出栈由主调者完成,函数调用过程中,EAX、ECX、EDX寄存器的值不需要进行保留。cdecl调用约定属于应用程序二进制接口(ApplicationBinaryInterface)的一部分。在x86的程序执行环境中,需要关注的几个寄存器有EIP、ESP、EAX。EIP寄存器是指令指针,它保存的是下一条需要执行的指令在内存中的位置,要执行某个函数的代码,只要把EIP指向该函数的入口即可。ESP寄存器指向当前执行环境中堆栈的顶端在内存中的位置。前面提到,x86上cdecl调用约定首先是将参数压入堆栈的,那么就可以通过直接操纵堆栈的方式,来写入自己想要的参数。然后是函数调用,函数调用的时候,用CALL语句改变EIP寄存器的值。CALL有好几种nearcall、farcalI>inter-privilegefarcall、taskswitch。Windows或Linux中调用C语言运行库的内存申请释放函数的时候用的是nearcall。可以看出,整个调用过程,是将EIP压入栈,然后将EIP设置为目标地址。由于之前所述,堆栈可以通过更改ESP寄存器和内存直接进行控制,EIP寄存器在调试器权限下也可以更改(属于线程上下文)。故可以通过调试器控制进程进行函数调用。函数调用结束后会执行RET语句返回。由上可知,函数返回的时候,会从栈顶取出一个元素,作为新的EIP。在由调试器控制函数调用的情况下,如果函数返回之后立即恢复执行原有代码,那么会很难判断函数什么时候调用完毕,以进行进一步控制。于是就在调整为函数调用时刚到函数入口时的状态的时候,将O压入栈顶作为最后的返回值,这样在函数执行完毕后EIP会变为0,产生异常,由调试器捕捉到,此时便可以进行进一步控制。3、拦截模块拦截模块负责将C语言运行支持库(runtime)中的malloc函数和free函数进行重定向。在C语言中,堆上内存的分配与释放分别对应这两个函数,如果接管了这两个函数,也就对堆上内存的分配和释放行为发生的时候能够进行控制了。拦截模块在使用时以共享函数库的身份,借助控制模块载入到被测进程中。拦截模块负责从进程内部拦截内存的申请和释放操作。它查找进程已载入的模块的符号,找到malloc和free后,获取这两个函数的入口地址,随后将这两个函数的入口重定向到自己的替代代码中。在替代代码里,它记录这次函数的调用行为、返回值等信息,然后将信息通过超级调用传递给Xen。它也可以在控制模块的操纵下,对函数的重定向进行取消,停止对内存申请和释放的监控。实现拦截模块的关键点在于能够对已经编译成机器代码并加载到内存中的进程进行修改,从而接管malloc和free函数。在代码还未进行编译的时候,可以通过定义一个宏,将malloc和free替换成自己的函数名,从而使代码在预处理的时候所有malloc和free就被替换。在程序运行的时候,这些代码已经被载入内存。如果需要修改内存中的这些代码,在malloc和free的函数开头可以加上一个类似C语言goto语句的指令,那么就可以跳转到自定义函数中。类似goto语句的机器代码就是0xe9,对应的汇编指令是JMP,具体的指令形式为JMPrel320整个指令占用5个字节,因此,需要覆盖原函数开头的5个字节才能顺利将这条指令写入。而rel32占用4个字节,也就是说,若自己的代码(替代函数)所在的内存地址为F2,要拦截的函数(比如malloc)所在的内存地址为F1,那么立即数rel32的值就是rel32=F2_Fl+5。虽然在原函数的开头加入了JMP语句,但是原函数并不能因此而废弃,因为申请内存最终还是要调用原malloc,释放内存还是要调用原free。由于原函数入口已经被破坏(原有代码被JMP覆盖),故原函数事实上已经处于不可用状态。所以在覆盖原函数起始处代码的时候,需要考虑这个函数的调用问题。解决的思路如下。因为原函数只有入口处的几条机器代码被覆盖,所以只要让CPU先执行被破坏的这几条指令,然后接着从没有被覆盖的指令执行下去,就可以正确调用这些函数。故在覆盖原函数入口的时候,先将那些代码备份到其他地方,然后在备份的代码末尾加上JMP语句跳回原函数未被破坏的部分,调用的时候直接朝着备份的地方调用,问题就可以解决。复制机器代码需要了解被复制的代码是不是可以直接复制并被使用的。因为要更改的函数入口只有5个字节,所以要复制的代码只要满足能腾出5个字节的空间即可。必须要解决以下问题代码的长度未知、代码是否可以直接复制、代码运行的结果是否不变。下面对这3个问题分别加以解决。第一个问题可以通过一个反汇编器来进行机器代码的分析,以解决问题。获得代码长度是反汇编器最基本的功能,知道了代码长度,就知道了有多少代码要复制(因为并不总是正好5个字节),知道了跳转回来的时候应该跳转到什么地址。这里选用的反汇编器是C语言版本的HDE32。第二个问题,主要涉及到JMP、CALL等立即数为相对地址的指令。使用HDE32进行代码分析之后,可以得知操作数是不是立即数,如果是立即数那么是不是相对地址。如果是相对地址,那么根据原指令所在地址和相对地址得到该指令引用的实际地址(绝对地址),然后根据将要存放该指令的目标地址和引用的实际地址计算出立即数应该是多少。这一步是指令复制时的修正。第三个问题,对于进行函数调用的CALL指令,正常情况下遇上使用相对地址立即数的,修正立即数即可解决问题。但是有一类函数比较特殊,它们会用到调用函数时压入栈里的返回地址。例如在glibc的free函数中,前5个字节就遇上了这样的函数调用,以实现共享函数库能够与模块载入位置无关(例如是静态变量、全局变量等资源的引用)而正常运行。但是将这样的CALL语句复制到别处以后,执行结果就和复制之前不同(因为实质上获取的是CALL语句的下一句的地址)。这个问题通过模拟CALL语句来实现,根据CALL语句的行为,包含一个压栈和一个跳转,那么用一个I3USH语句和一个JMP语句替换原有的CALL语句,即可达到目的。因为一条CALL语句的长度已经足够5个字节,故覆盖CALL语句之后,也不用担心函数调用返回的时候自己新增的代码失去控制权的问题,因为CALL语句之后不用再有需要执行的代码了,可以接下去执行原函数中未被破坏的部分。如图5展示了监视开始后被测进程中malloc的调用流程。整个过程都是动态完成的,操作对进程透明,使用之前不需要对进程进行特别的处理。如果在拦截到内存申请、释放操作的时候进入ringO然后再进行超级调用,那么频繁的ring3与ringO之间的切换会导致性能很大的下降。因此,若对Xen现有机制进行一些修改,使得该工具的拦截模块能直接从用户态向Xen传递数据,可以得到一定性能上的提升。Xen原本提供的超级调用限制较多,如果在用户态进行超级调用的话,则调用会失败。故对Xen修改的时候,可直接将新增的超级调用加入检测虚拟机是不是处于内核态的代码之前,从而跳过Xen的检测。4、监视模块监视模块和拦截模块协同作用,对被测进程中的堆内存申请和释放行为进行监视。拦截模块负责拦截malloc和free等调用行为,监视模块则对拦截到的这些行为进行处理。如果用设计模式中的观察者模式进行解释的话,被测进程就是被观察的目标,而拦截模块就是观察者,并且仅仅观察,并不行动。具体对观察到的行为作出响应的则是监视模块。监视模块主要是获取调用时的参数以及函数返回值等信息,其中原函数的调用由拦截模块负责。在拦截模块修改了malloc和free的函数入口以后,调用malloc和free就被重定向到监视模块中。此时可以直接通过读取栈中的地址,得知程序要申请多大的内存空间或者释放哪里的内存空间。读取部分可以直接通过编写汇编语言实现,当然更简单的方法是自己编写一个具有同样声明的函数,这样对于被测进程来说,调用malloc就相当于直接调用监视模块中的相关函数,于是获取参数的方式就变得和编写C语言代码时完全一样直接访问形参。监视模块只负责接收消息,将消息以XML的格式输出,这样它的接口灵活,方便各种程序对数据进行进一步处理。以上是本发明的较佳实施例,凡依本发明技术方案所作的改变,所产生的功能作用未超出本发明技术方案的范围时,均属于本发明的保护范围。权利要求1.一种面向Xen虚拟化环境的运行时内存泄漏检测方法,其特征在于包括以下步骤步骤1:动态分析受监控程序的内存使用行为,对内存对象进行分组,形成多个内存对象组,并对各内存对象组相关信息进行维护和更新;所述内存对象是指经系统调用分配的内存块;所述内存对象生命期是指内存对象从被分配到被释放的这段时间;所述内存对象组是内存对象的集合,每个内存对象都采用一个元组(size,callChain)来表示,size表示内存对象的大小,callChain表示在分配内存对象时的栈调用(call_stack);步骤2:基于观测到的行为特征检测潜在的内存泄漏;步骤3:依据相关规则进行内存泄漏判定。2.根据权利要求1所述的面向Xen虚拟化环境的运行时内存泄漏检测方法,其特征在于动态分析受监控程序的内存使用行为,对各内存对象组相关信息进行维护和更新的方法为步骤1.1:对于每一个内存对象组,对其内存分配与释放的行为实施收集,记录相关信息,包括生命期信息、内存使用信息;所述生命期信息包括内存对象组的最大生命期maxLifeTime、最大生命期的稳定时间stableTime,即maxLifeTime持续为一个值的持续时间,所有的时间都以CPU时间来度量;所述内存使用信息包括内存对象组内的活内存对象信息、最后分配时间、内存对象组占用的总内存空间大小;步骤1.2:对于内存对象组内的每一个活内存对象,记录其内存分配时间,同一个内存对象组内所有活内存对象都以双向链表的形式表示,以便快速查找;步骤1.3:对于内存对象组内的每一次内存分配,即一个新的活内存对象添加到所述双向链表后,对内存对象组的关联信息进行更新,包括活内存对象信息、最后分配时间、内存对象组占用的总内存空间大小;步骤1.4:对于内存对象组内的每一次内存释放,也对内存对象组的关联信息进行相应的更新,包括活内存对象信息、内存对象组占用的总空间大小,同时通过计算被释放内存的分配时间与当前时间的差值,求得被释放内存的生命期IifeTime;步骤1.5:如果被释放内存的IifeTime少于所在内存对象组的maxLifeTime,则所述内存对象组的maxLifeTime不作改变,且stableTime随着CPU处理时间而增加,否则,对maxLifeTime进行更新,且将stableTime重置为O。3.根据权利要求2所述的面向Xen虚拟化环境的运行时内存泄漏检测方法,其特征在于检测潜在的内存泄漏的方法为定义在程序所有可能的执行路径上都不会释放的内存对象为总是泄漏内存对象ALeak;定义在程序有些执行路径上会释放分配,但在程序其他路径上不会释放分配的内存对象为有时泄漏内存对象SLeak;根据步骤I对内存对象的分组,针对ALeak、SLeak两种不同的泄漏类型,分别采用如下的方法进行检测检测ALeak包括以下步骤步骤2.1:检测每一个内存对象组中活内存对象的数量是否超过了预设的阈值,是则转至步骤2.2,进一步检查该内存对象组使用的内存是否连续增长,否则转至步骤2.3;步骤2.2:如果该内存对象组的最后分配时间离当前时间较近,内存使用仍在增长,则该内存对象组是泄漏嫌疑对象;如果最后分配时间离当前时间较远,即在一段较长时间内都没有内存使用增长,则不会发生内存泄漏;步骤2.3:如果没有超过,则可断定是在初始时程序分配了较多内存对象,在整个执行过程中这些对象都在使用,不属于内存泄漏;检测SLeak的方法为检测每一个内存对象组中活内存对象的生命期lifeTime,如果所述活内存对象的活动时间超过所在内存对象组的maxLifeTime的两倍,且最大生命期maxLifeTime的稳定时间stableTime比预设阀值还长,且所述活内存对象在maxLifeTime内动态内存部分不释放,但持续申请,导致所占用的内存越来越多,且所述活内存对象在maxLifeTime内已释放,但多次重复释放,导致不可预计的行为发生,则所述内存对象组是泄漏嫌疑对象。4.根据权利要求3所述的面向Xen虚拟化环境的运行时内存泄漏检测方法,其特征在于判定内存泄漏的方法为步骤3.1:对于在检测过程中已标记为泄漏嫌疑对象的内存对象,在预定的时间阈值内,如果所述内存对象被再次访问,则所述内存对象为可能泄漏内存对象PLeak,否则所述内存对象确定为泄漏对象;步骤3.2:如果内存对象为PLeak,则重置这个内存对象的分配时间为当前时间;如果这个内存对象重新变为泄漏嫌疑对象,则确认该内存对象为泄漏对象。5.一种面向Xen虚拟化环境的运行时内存泄漏检测方法的实现系统,其特征在于包括Xen内部缓冲区维护模块、控制模块、拦截模块和监视模块。6.根据权利要求5所述的面向Xen虚拟化环境的运行时内存泄漏检测系统,其特征在于所述Xen内部缓冲区维护模块定义了若干个有关的超级调用来辅助完成对内存相关操作的捕捉,并获到相关数据,用于泄漏对象分组和判定。7.根据权利要求5所述的面向Xen虚拟化环境的运行时内存泄漏检测系统,其特征在于所述控制模块用以控制被测进程,使被测进程主动加载拦截模块,并向拦截模块发送指令,使拦截模块能对被测进程进行修改,拦截被测进程内存的申请和释放操作。8.根据权利要求5所述的面向Xen虚拟化环境的运行时内存泄漏检测系统,其特征在于所述拦截模块对被测进程内存的申请和释放操作进行拦截,并通过超级调用将拦截到的数据传送到Xen内部缓冲区维护模块,等待进一步处理。9.根据权利要求5所述的面向Xen虚拟化环境的运行时内存泄漏检测系统,其特征在于所述监视模块与拦截模块协同作用,拦截模块负责拦截内存操作行为,监视模块对拦截到的行为进行处理,包括分组确认泄漏对象和可视化处理。全文摘要本发明涉及一种面向Xen虚拟化环境的运行时内存泄漏检测方法及系统,通过对Xen虚拟机内存管理技术的分析,给出了Xen虚拟化计算环境下内存泄漏的运行时检测方法,该方法的主要步骤为(1)动态分析受监控程序的内存使用行为,对内存对象进行分组,并对各内存对象组相关信息进行维护和更新;(2)基于观测到的行为特征检测潜在的内存泄漏;(3)依据相关规则进行内存泄漏判定。与现有技术相比,本发明能跨操作系统平台、跨语言开发环境对运行时内存泄漏检测,适用范围广,具有较好的性能,能提升软件开发行业特别是内存受限的软件产品质量与开发效率,缩短软件开发的工期,直接节约了开发成本,能带来良好的经济效益和社会效益。文档编号G06F11/36GK103064784SQ20121049948公开日2013年4月24日申请日期2012年11月29日优先权日2012年11月29日发明者肖如良,姜军,胡耀,李鹏澎,倪友聪,杜欣申请人:福建师范大学
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1