一种应用程序故障的定位方法

文档序号:6423910阅读:147来源:国知局
专利名称:一种应用程序故障的定位方法
技术领域
本发明涉及计算机领域中的基于Microsoft Windows操作系统的C语言应用软件开发,尤其涉及一种应用程序故障的查错方法。
背景技术
由于Microsoft Windows操作系统在个人电脑的广泛应用,大量的软件企业开发了越来越多的Windows应用程序,很多应用是使用C语言开发的,由于软件开发者的技能水平和程序的复杂程度不同,不少程序代码中会有这样或那样的BUG(故障),严重的BUG可能导致应用程序死掉。
现在比较通用的查错方法是使用WINDOWS提供的结构化异常处理或运行调试版本,很多非法地址访问、DIV BY ZERO(被0除)等异常都可以捕获。现有的查错方法是在程序产生故障导致程序退出时,从程序的堆栈中查找当前的EIP(执行指令地址)和RET(返回地址)来判断具体在哪个函数中运行出错,从而实现定位。
但是,即使在调试的环境下,由于有些BUG会将程序的堆栈修改,例如函数局部变量的使用不当有可能导致堆栈区的非法修改,现有的这些方法就很难有所作为了,特别是一些作为服务运行的程序需要长时间运行,出了问题难以定位故障的原因。

发明内容
本发明所要解决的技术问题是提供一种应用程序故障的定位方法,能够定位包括堆栈被破坏在内的更多的故障。
为了解决上述技术问题,本发明提供了一种应用程序故障的定位方法,包括以下步骤(a)在应用程序编译时,为每一个自编码函数做一个在函数入口处调用的钩子函数,并提供一个在自编码函数返回时调用的隐式调用函数;
(b)该应用程序启动后,建立该应用程序的进程共享内存区,在该进程共享内存区中为该应用程序的每一个线程分配一个线程共享内存区;(c)该应用程序运行到所述自编码函数时调用该钩子函数;(d)该钩子函数取得当前执行指令的地址标记和当前运行函数的返回地址标记,压入相应线程共享内存区的堆栈区,同时将该应用程序堆栈中当前运行函数的返回地址修改为该隐式调用函数的入口地址;(e)当前运行函数返回时自动调用该隐式调用函数,该隐式调用函数将压入的当前执行指令的地址标记和当前运行函数的返回地址标记弹出,并在该应用程序堆栈区中恢复当前运行函数的返回地址;(f)程序继续运行,在每一自编码函数调用均采用步骤(c)到步骤(d)的方法处理,如果程序正常退出,释放该应用程序的进程共享内存区,结束;如果发生故障退出,执行下一步;(g)保存该应用程序的进程共享内存的数据,根据所述堆栈区的当前指针获得应用程序正在运行的函数和嵌套层次,对故障进行准确定位。
进一步地,为了获取发生故障时的上下文信息,所述步骤(d)中,还将当前执行指令的地址标记依序保存到相应线程共享内存区的链表区,步骤(g)中,还对该链表区数据分析,获得该线程最近执行过的函数。
进一步地,为了在正常的运行环境下也能实现对应用程序故障的定位,可采用跟踪动态库来实现上述处理,即所述步骤(a)之前先建立一个包含所述钩子函数和隐式调用函数实现代码的跟踪动态库,所述应用程序在编译时静态连接该跟踪动态库,实现对所述钩子函数的调用,在该应用程序启动后,该动态库在处理进程建立消息和线程建立消息时分别创建所述进程共享内存区和线程共享内存区。
进一步地,为了实现对是否启动故障定位的选择,在所述步骤(c)中,所述钩子函数还检查所述进程共享内存区中的标记字,判断是否启动故障定位,如果启动,再执行步骤(d),如果不启动,直接执行步骤(f)。
进一步地,上述定位方法可具有以下特点所述动态库在处理进程建立消息和线程建立消息时还分别建立线程局部存储标识和对应的各线程唯一标识,各线程唯一标识又对应于各自的线程共享内存区,所述钩子函数和隐式调用函数是通过所述线程局部存储标识找到相应的线程唯一标识,再找到该线程的共享内存区的。
进一步地,为了实现对多个应用程序的管理,在步骤(b)建立所述进程共享内存区后,还可将该应用程序的名称注册到一个管理共享内存区,并记录该应用程序名称与该应用程序进程共享内存区的对应关系。该管理共享内存可由所述动态库创建,或者由一个管理程序创建。
进一步地,上述定位方法可具有以下特点所述步骤(g)中可由一个管理程序定时检测所述管理共享内存区内各应用程序的运行状态,如果某一应用程序不再运行,则根据该应用程序名称找到该应用程序的进程共享内存区并保存其数据。
进一步地,为了减少对应用程序的改动,在步骤(a)编译时可采用支持在每个函数入口自动调用钩子函数的编译器,并在跟踪动态库中将钩子函数命名为该编译器为该自动调用的钩子函数指定的名称。
与现有技术Windows中提供的结构化异常处理和调试版本调试环境相比,本发明应用程序故障的定位方法具有以下优点A,通过建立共享内存,能够定位更多的异常,例如堆栈破坏的异常;B,通过在共享内存中建立堆栈区,能够显示故障时函数的嵌套情况;C,通过使用windows动态连接库,能够在正常的运行环境中定位应用程序故障;D,通过在建立多个线程共享内存区,能够对多线程的应用程序进行准确定位;E,由于在链表区按顺序记录了最近执行过的函数,所以能够得到更多的上下文信息;F,对于被跟踪的应用程序的改动很少,编译器支持启用_penter钩子函数时,只需要在跟踪动态库增加钩子函数_penter的实现代码即可;G,采用隐式调用函数来将当前运行函数的EIP和RET弹出堆栈,避免了显示函数调用时须在函数每一个RETURN增加显示函数的繁琐。


图1是本发明实施例共享内存的结构图。
图2是本发明实施例应用程序函数调用过程的示意图。
具体实施例方式
本实施例应用程序的定位方法包括以下步骤步骤一,建立跟踪动态库,其中包含钩子函数和隐式调用函数的实现代码;步骤二,启动管理程序,判断是否已存在管理共享内存区,如没有则建立该内存区;步骤三,被跟踪的应用程序在编译时静态连接所需动态库,为所有自编代码的函数做一个在函数入口处调用的钩子函数,并提供一个在函数返回时调用的隐式调用函数;步骤四,应用程序启动后,动态库在处理进程建立消息时,建立以该应用程序名称命名的进程共享内存区以及线程局部存储标识,该进程共享内存区中设置有一个缺省的启动故障定位标记;步骤五,动态库将应用程序注册到管理共享内存区中,建立该应用程序名称与该应用程序进程共享内存区的对应关系,管理程序修改该进程共享内存区的启动故障定位标记;步骤六,动态库处理线程建立消息时设置该线程的唯一标识值(可有多个,标识可依线程建立次序从0递增),并根据线程唯一标识值从进程共享内存区中为该线程分配一个线程共享内存区;建立的共享内存的结构如图1所示,在管理共享内存区中记录了多个运行的应用程序的名称,每个应用程序名称对应于该程序的进程共享内存区,进程共享内存区中保存有一个启动故障定位标记字(该标志位可以由应用程序写缺省值,也可以由管理程序修改),并为每个线程分配了一个线程共享内存区,每个线程共享内存区又进一步分为链表区和堆栈区,其存储数据的方法将在下面步骤中介绍,同时参照图2。
步骤七,被跟踪的应用程序运行到某个函数时调用钩子函数,钩子函数首先根据进程共享内存区的标记字判断是否启动了故障定位功能,如果是,执行下一步,否则直接运行完当前函数,再执行步骤210;步骤八,钩子函数取得当前的EIP(执行指令的地址)标记和当前运行函数的RET(返回地址)标记,修改被跟踪应用程序的堆栈,将当前运行函数的返回地址修改为隐式调用函数的入口地址;步骤九,钩子函数根据当前线程的局部存储标识得到该线程唯一标识值再找到该线程的共享内存空间,将EIP和RET标记压入线程共享内存区的堆栈区,同时在线程共享内存区的环形链表区保存EIP标记;步骤十,当前运行函数执行完返回时,自动调用隐式调用函数,隐式调用函数根据该线程的局部存储标识得到该线程唯一标识值再找到该线程的共享内存区,将压入的EIP和该函数的RET弹出,在程序堆栈区修改该函数的返回地址为RET;步骤十一,程序继续运行,运行到每个函数都重复上述步骤八至十,直到程序正常结束或发生故障退出,如果是正常退出,执行下一步,如果是发生故障退出,执行步骤十三;步骤十二,释放该应用程序的进程共享内存区和在管理共享内存区中的注册信息,结束;步骤十三,管理程序将该应用程序的进程共享内存的数据记录到文件;步骤十四,根据线程共享内存区中堆栈区的当前指针可以获得应用程序正在运行的函数和嵌套层次,根据环形链表区顺序存储的EIP,可以得到该线程最近执行过的函数,从而对故障进行准确的定位。
如果被跟踪的应用程序有改动,需要在每个函数的入口增加调用钩子函数的代码。但如果编译器支持启用_penter钩子函数,则只需要在跟踪动态库增加钩子函数_penter的实现代码即可,不需要在每个函数中增加调用钩子函数的代码了,函数会自动调用名为_penter钩子函数。因此,应尽量采用支持在每个函数入口自动调用钩子函数的编译器,并在跟踪动态库中将钩子函数命名为编译器为该自动调用的钩子函数指定的名称。
下面用一个应用实例来进一步说明本发明的效果。假设应用程序只有一个线程,包含了A、B、C三个函数且A函数顺序调用B和C函数,在所有共享内存建立完毕后,应用程序在调用A函数时,将执行指令的地址EIP-A和A函数的返回地址RET-A压入线程共享内存的堆栈区,而将执行指令地址EIP-A写入链表区,并修改程序堆栈中A函数的返回地址为隐式调用函数。
在A函数运行过程中,调用B函数时,将此时的执行指令地址EIP-B和B函数返回地址RET-B压入堆栈区,并将EIP-B写入链表区,这时链表区存有EIP-A和EPI-B,堆栈区内存有EIP-A、RET-A和EIP-B、RET-B。即使此时发生异常并修改了程序堆栈,通过对堆栈区记录数据的分析可以定位到B函数。
如果B函数运行正常,在返回时会将EIP-B和RET-B从堆栈区中弹出,但在链表区的EIP-B保留。程序继续运行,如果在调用C函数时发生故障,可以同时从堆栈区数据中找到调用C函数时的EIP-C和C函数的返回地址RET-C,以及原来的EIP-A和RET-A,从而准确得知正在运行的函数和嵌套层次。
在运行时B或C函数也有可能是系统调用或其他动态库提供的函数,这时该函数没有钩子函数。假定在C函数发生异常,应用本实施例方法后,链表区中会记录有EIP-B,而堆栈区中会记录有EIP-A和RET-A,EIP-B和RET-B已被弹出,因而在这种情况下也可以分析出是在调用了B函数之后发生异常。从这里可以看出链表区提供的上下文信息对于准确定位的作用。
很明显,对于有多个线程的应用程序,从各线程共享内存区中也可以得到各自的记录,对各个线程出现的故障快速准确地定位。
此外,为了使应用程序故障定位在管理程序未启动的情况下也能执行,本实施例在动态库处理进程建立消息时,还检查是否存在管理程序使用的共享内存,若不存在则建立,并写入自己的应用程序的名称。应用程序的启动故障定位标记字可以采用缺省选项。该管理共享内存也可以由动态库来创建,管理程序不是必须启动,如果应用程序异常退出时,可以手工启动管理程序来记录堆栈。
权利要求
1.一种应用程序故障的定位方法,包括以下步骤(a)在应用程序编译时,为每一个自编码函数生成一个在函数入口处调用的钩子函数,并提供一个在自编码函数返回时调用的隐式调用函数;(b)该应用程序启动后,建立该应用程序的进程共享内存区,在该进程共享内存区中为该应用程序的每一个线程分配一个线程共享内存区;(c)该应用程序运行到所述自编码函数时调用该钩子函数;(d)该钩子函数取得当前执行指令的地址标记和当前运行函数的返回地址标记,压入相应线程共享内存区的堆栈区,同时将该应用程序堆栈中当前运行函数的返回地址修改为该隐式调用函数的入口地址;(e)当前运行函数返回时自动调用该隐式调用函数,该隐式调用函数将压入的当前执行指令的地址标记和当前运行函数的返回地址标记弹出,并在该应用程序堆栈区中恢复当前运行函数的返回地址;(f)程序继续运行,在每一自编码函数调用均采用步骤(c)到步骤(d)的方法处理,如果程序正常退出,释放该应用程序的进程共享内存区,结束;如果发生故障退出,执行下一步;(g)保存该应用程序的进程共享内存的数据,根据所述堆栈区的当前指针获得应用程序正在运行的函数和嵌套层次,对故障进行准确定位。
2.如权利要求1或2所述的定位方法,其特征在于,所述步骤(d)中,还将当前执行指令的地址标记依序保存到相应线程共享内存区的链表区,步骤(g)中,还对该链表区数据分析,获得该线程最近执行过的函数。
3.如权利要求1或2所述的定位方法,其特征在于,所述步骤(a)之前先建立一个包含所述钩子函数和隐式调用函数实现代码的跟踪动态库,所述应用程序在编译时静态连接该跟踪动态库,实现对所述钩子函数的调用,在该应用程序启动后,该动态库在处理进程建立消息和线程建立消息时分别创建所述进程共享内存区和线程共享内存区。
4.如权利要求3所述的定位方法,其特征在于,所述步骤(c)中,所述钩子函数还检查所述进程共享内存区中的标记字,判断是否启动故障定位,如果启动,再执行步骤(d),如果不启动,直接执行步骤(f)。
5.如权利要求3所述的定位方法,其特征在于,所述动态库在处理进程建立消息和线程建立消息时还分别建立线程局部存储标识和对应的各线程唯一标识,各线程唯一标识又对应于各自的线程共享内存区,所述钩子函数和隐式调用函数是通过所述线程局部存储标识找到相应的线程唯一标识,再找到该线程的共享内存区的。
6.如权利要求1或2所述的定位方法,其特征在于,在步骤(b)建立所述进程共享内存区后,还将该应用程序的名称注册到一个管理共享内存区,并记录该应用程序名称与该应用程序进程共享内存区的对应关系。
7.如权利要求6所述的方法,其特征在于,所述管理共享内存是由所述动态库创建的,或者由一个管理程序创建的。
8.如权利要求1或2所述的定位方法,其特征在于,所述步骤(g)中是由一个管理程序定时检测所述管理共享内存区内各应用程序的运行状态,如果某一应用程序不再运行,则根据该应用程序名称找到该应用程序的进程共享内存区并保存其数据。
9.如权利要求4所述的定位方法,其特征在于,在步骤(a)编译时采用支持在每个函数入口自动调用钩子函数的编译器,并在跟踪动态库中将钩子函数命名为该编译器为该自动调用的钩子函数指定的名称。
全文摘要
本发明公开一种应用程序故障的定位方法,包括以下步骤应用程序编译时,为自编码函数做一个在入口处调用的钩子函数;程序启动后,建立该程序的进程共享内存区,并为每一个线程分配线程共享内存区;运行到所述自编码函数时调用钩子函数,将当前的执行指令地址标记(EIP)和当前运行函数返回地址标记(RET)压入相应线程共享内存区的堆栈区,并将程序堆栈中的RET修改为隐式调用函数的入口地址;函数返回时自动调用隐式调用函数,将压入的EIP和RET弹出,并恢复程序堆栈的RET;程序发生故障退出后,根据所述堆栈区的当前指针可获得应用程序正在运行的函数和嵌套层次,即使应用程序堆栈被破坏,也能实现对故障的准确定位。
文档编号G06F11/36GK1728106SQ20041007076
公开日2006年2月1日 申请日期2004年7月26日 优先权日2004年7月26日
发明者董伟杰, 王新余 申请人:中兴通讯股份有限公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1