CN1329836C - 定位程序异常的方法 - Google Patents
定位程序异常的方法 Download PDFInfo
- Publication number
- CN1329836C CN1329836C CNB2004100428237A CN200410042823A CN1329836C CN 1329836 C CN1329836 C CN 1329836C CN B2004100428237 A CNB2004100428237 A CN B2004100428237A CN 200410042823 A CN200410042823 A CN 200410042823A CN 1329836 C CN1329836 C CN 1329836C
- Authority
- CN
- China
- Prior art keywords
- file
- code
- unusual
- executable file
- map
- Prior art date
- Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
- Expired - Fee Related
Links
Images
Landscapes
- Debugging And Monitoring (AREA)
Abstract
本发明公开了一种定位程序异常的方法,包括下述步骤:1)生成第一可执行文件以及代码信息文件;2)在所述第一可执行文件中加载所述代码信息文件,生成含有代码信息文件信息块的第二可执行文件;3)启动所述第二可执行文件,加载异常捕获与定位动态库;4)当第二可执行文件发生异常时,产生系统核心数据,所述系统核心数据包含函数调用的内存地址;5)从所述第二可执行文件读取代码信息文件,比较前述系统核心数据和代码信息文件,确定程序异常的位置。应用本发明提供的定位程序异常的方法可以高效、便捷、准确地对发生异常的程序进行自动定位,并提供充足信息供程序维护人员查找异常原因。
Description
技术领域
本发明涉及数据处理领域,特别是涉及一种当程序出现异常时,对程序的异常进行准确定位的方法。
背景技术
随着计算机的日益普及和快速发展,人们不仅在工作和生活中越来越多地依赖于各种各样的程序,而且对程序的功能提出越来越高的要求。伴随着功能的提高与加强,程序的复杂度也随之增加,由此导致程序的稳定性和可靠性下降,使得程序在运行过程中会出现各种异常状况。为了跟踪、查找程序发生异常的确切位置,人们开发了多种捕获程序异常和定位程序异常的方法。
目前,较为流行的定位程序异常的方法大都采用二次分析的方法。首先,由维护人员分别保存编译过程中产生的可执行文件和被跟踪程序对应的程序符号库。所述程序符号库含有程序代码和符号信息。然后,由维护人员事先进行捕获程序异常的设置,选定被跟踪程序。当被跟踪程序出现异常时,首先生成异常发生的内存地址、异常发生的模块信息和函数调用栈等系统核心数据;然后由维护人员将程序符号库导入内存,在内存中比较、分析所述系统核心数据与程序符号库,并确定程序发生异常的位置;最终生成含有上述位置信息的异常代码定位文件。
实际使用中,常采用的程序符号库为含有程序调试信息的pdb(programdatabase,程序数据库)文件。再者,所述系统核心数据的具体表现形式和格式因所采用的捕获程序异常和定位程序异常的工具和方法的不同而不同。
目前,Windows操作系统下较为常用的定位程序异常的方法是采用Userdump工具和WinDebug工具来实现对程序发生的异常进行定位。
首先,维护人员使用Userdump工具选定被跟踪程序,设置需要跟踪的异常种类;保存程序编译过程中生成的pdb文件。
然后,维护人员启动被跟踪程序,Userdump工具在被跟踪程序上注册异常捕获钩子,用于捕获被跟踪程序发生的异常。被跟踪程序运行过程中,Userdunmp工具始终驻留在内存中,一旦被跟踪程序发生异常,立即捕获异常并记录系统核心数据文件。所述系统核心数据可以是dump文件。
然后,维护人员利用WinDebug工具导入dump文件和pdb文件,由WinDebug工具判断dump文件中记录的发生异常的内存地址是否在pdb文件中。
然后,根据dump文件对应的函数调用栈中的函数从pdb文件中分析出异常函数的函数名,并根据发生异常的内存地址计算异常发生在第几行代码。
最后,生成异常代码定位报告。所述异常代码定位报告包括发生异常的代码位置、发生异常的代码的模块名(例如:kernel32.dll)、文件名、函数调用栈、函数名等信息。
尽管,现有技术提供的定位程序异常的工具和方法可以实现对程序异常的跟踪与定位,但是在实际应用中仍然不可避免地存在下述缺陷:
其一,采用现有技术提供的定位程序异常的方法,需要由维护人员单独保存程序编译过程中生成的程序符号库。当程序出现异常时,维护人员取出程序符号库文件,并利用异常定位工具将其导入,然后由异常定位工具比较系统核心数据和程序符号库,最终生成异常代码定位文件。因而采用现有技术中提供的定位程序异常的方法不能自动地对程序发生的异常进行定位。同时,由于人工的介入,导致人力资源浪费,并使得定位分析时间较长、工作效率较低。
其二,现有技术提供的方法和工具需要由维护人员将程序编译过程中生成的各个pdb文件,以及生成的pdb文件的程序代码作为各个分立的文件分别保存。由于系统核心数据对应于被跟踪程序,而被跟踪程序重新编译将重新生成pdb文件。因此,只有程序代码和相应的pdb文件版本保持一致,才能通过系统核心数据与pdb文件判断出程序出现异常的确切位置。也就是,若程序代码出现更改,必须重新生成pdb文件并与相同版本的程序代码一同保存。然而,若由人工或采用简单的版本管理机制来分别保存程序代码和相应的pdb文件,将难以保证两者的版本绝对一致,尤其是对于含有多个动态库组件的程序。因此,在查找程序发生异常的确切位置时,很难找到与发生异常的程序代码对应的pdb文件,也就难以确定程序发生异常的确切位置。为此,必须使用一套高级的版本管理机制,来保持pdb文件版本与程序代码版本之间的对应关系。但是,若自行开发完善的版本管理机制将加大程序开发的成本,同时增加开发的技术难度;若直接采用现有的高级版本管理机制,由于高级版本管理机制的价格较高,同样会导致成本的提高。
其三,现有技术中经常借助Userdump工具来判断程序是否有异常发生。而Userdump工具需要安装在用户侧的计算机上,并且需要在启动被跟踪程序之前进行参数设定,通常用户无法自行完成设定,需要由Userdump工具供应商提供相应的服务,因而使用Userdump等工具的成本较高。
其四,一旦被跟踪的程序启动,Userdump工具始终驻留在内存中等待被跟踪程序发生异常,而不会自行退出内存。因此在被跟踪程序运行过程中,Userdump工具始终占用内存资源。
发明内容
本发明解决的技术问题在于:提供了一种定位程序异常的方法,能够无需单独保存代码信息文件就可自动地对程序发生的异常进行定位。
为此,本发明解决技术问题的技术方案是:
提供一种定位程序异常的方法,包括步骤:
1)生成第一可执行文件以及代码信息文件,所述第一可执行文件是通过编译源程序生成的原始可执行文件;
2)在所述第一可执行文件中加载所述代码信息文件,生成含有代码信息文件信息块的第二可执行文件;
3)启动所述第二可执行文件,加载异常捕获与定位动态库;
4)当第二可执行文件发生异常时,产生系统核心数据,所述系统核心数据包含函数调用的内存地址;
5)从所述第二可执行文件读取代码信息文件,比较前述系统核心数据和代码信息文件,确定程序异常的位置。
所述第二可执行文件包括第一可执行文件、代码信息文件、分割符和第一可执行文件长度数据;所述分割符位于所述第一可执行文件和所述代码信息文件之间、相邻的两个代码信息文件之间,以及代码信息文件和第一可执行文件长度之间,用于分割第一可执行文件、各个代码信息文件以及第一可执行文件长度。
所述步骤1)具体包括在程序编译单元中设置加载单元,并通过程序编译单元生成第一可执行文件以及代码信息文件;
在所述步骤2)中,通过加载单元将代码信息文件添加至第一可执行文件。
所述步骤3)中加载异常捕获与定位动态库的过程包括:将异常捕获钩子添加到第二可执行文件中,用于捕获异常;将异常代码定位单元嵌入第二可执行文件中,用于生成异常代码定位文件。
所述步骤3)中异常捕获钩子添加到第二可执行文件中的异常捕获链表中。
所述步骤5)具体包括步骤:
41)生成异常代码定位文件的文件名;
42)将系统核心数据写入异常代码定位文件;
43)读取第二可执行文件中的代码信息文件信息块;
44)比较系统核心数据和代码信息文件,确定程序异常对应的文件名、函数名和代码行。
所述步骤43)具体包括:从第二可执行文件中分离出各个代码信息文件,并依据内存映射格式将代码信息文件读入内存。
所述代码信息文件是MAP文件。
所述MAP文件在内存中以MAP文件信息列表类格式存放。
所述MAP文件信息列表类对应MAP文件信息类以及文件名和代码行类;
所述文件名和代码行类存放文件名和代码行;
所述MAP文件信息类存放MAP文件模块名、模块代码加载起始地址、模块代码加载终止地址、文件名和代码行对应表以及函数名对应表;
所述文件名和代码行对应表存放读取MAP文件时生成的文件名和代码行的哈希值;
所述函数名对应表存放读取MAP文件时生成的函数名的哈希值,键值为函数地址。
所述步骤44)中进一步包括:通过比较函数调用的内存地址与所述模块代码加载起始地址和模块代码加载终止地址,来分析、查找对应于函数调用的内存地址的MAP文件信息块;
通过函数调用的内存地址和函数名对应表确定函数名。
所述步骤44)进一步包括:
通过比较函数调用的内存地址与所述模块代码加载起始地址和模块代码加载终止地址,来分析、查找对应于函数调用的内存地址的MAP文件信息块;
将函数调用的内存地址减去模块代码加载起始地址以及偏移地址,得到代码行偏移地址;
通过代码行偏移地址与文件名和代码行对应表查找发生异常的文件名和代码行类结构,依据所述文件名和代码行类结构确定发生异常的代码行号和文件名。
相对于现有技术,本发明的有益效果是:
其一,本发明提供了一种定位程序异常的方法,无需单独保存代码信息文件,而是在编译过程中,将相应的代码信息文件加载到第一可执行文件中,形成第二可执行文件。当程序出现异常后,自动捕获程序的异常,并生成系统核心数据。通过比较系统核心数据和加载于第二可执行文件的代码信息文件来自动确定程序发生异常的模块名、函数名和代码行。因而,本发明提供的方法无需人工干预就可以自动定位异常代码,提高了效率。
其二,由于本发明提供的一种定位程序异常的方法,可以无需单独保存诸如pdb文件的任何程序符号库文件,而只保存加载有代码信息文件的第二可执行文件,就能够在程序出现异常时自动生成异常定位信息文件。因而简化了软件版本管理机制,降低了开发成本。
其三,由于本发明在编译过程中将代码信息文件加载到可执行文件中。同时,本发明提供的定位程序异常的方法在可执行文件启动后,可以加载异常捕获与定位动态库,因而无需借助Userdump工具和WinDebug工具,也不需要进行特殊配置,就可以实现自动捕获程序的异常以及自动定位程序异常,并自动生成异常代码定位文件,因此降低了成本。
其四,由于代码信息文件加载到可执行文件的尾部,没有改变原始可执行文件的长度,因而不影响源程序的正常执行和内存的占用情况。
附图说明
图1是定位程序异常的方法的流程图;
图2是具有异常定位功能的可执行文件格式的示意图;
图3是MAP文件格式的示意图;
图4是异常捕获与分析流程的示意图;
图5是MAP文件内存映射格式的示意图;
图6是异常代码定位文件的示意图。
具体实施方式
本发明提供的定位程序异常的方法,当程序发生异常时,可以自动捕获程序的异常以及定位程序异常,并自动生成上述异常代码定位文件;当程序未发生异常时,不会影响程序的正常执行,也不会额外占用内存资源。
请参阅图1,本发明提供的定位程序异常的方法主要包括下述步骤:
步骤210,编译单元编译源程序,生成代码信息文件,以及原始可执行文件(EXE文件)(以下称第一可执行文件)。
步骤220,将代码信息文件嵌入到第一可执行文件中,生成新的可执行文件(以下称第二可执行文件)。所述第二可执行文件中包含多个代码信息文件,多个代码信息文件构成代码信息文件信息块。
步骤230,启动第二可执行文件,加载异常捕获与定位动态库。所述异常捕获与定位动态库包括异常捕获钩子和异常代码定位单元。第二可执行文件启动后,所述异常捕获钩子挂接到被跟踪的第二可执行文件中,用于捕获异常。
步骤240,当第二可执行文件发生异常后,异常捕获钩子捕获到程序异常,生成异常代码定位文件的文件名。此外,还生成含有发生异常的内存地址、发生异常的模块信息和函数调用栈等定位信息的系统核心数据。所述系统核心数据含有的信息用于对程序出现的异常进行定位。将系统核心数据写入异常代码定位文件中。
步骤250,将第二可执行文件中包含的代码信息文件读入内存,比较、分析同在内存中的系统核心数据和代码信息文件,从而确定程序异常所对应的文件名、函数名和代码行。
步骤260,将包含程序异常对应的文件名、函数名和代码行代码定位信息写入异常代码定位文件中,生成最终的异常代码定位文件。
最终生成的异常代码定位文件包括发生异常时间、发生异常的线程标识、发生异常的内存地址、发生异常所在的可执行体和偏移量、异常编码、异常标志、发生异常时的函数调用栈信息、函数从属的文件名、函数从属的执行体和具体的行数。
需要指出的是,本发明提供的定位程序异常的方法不是在第二可执行文件一启动就读入代码信息文件,而是在程序出现异常时,由异常捕获与定位动态库自动从第二可执行文件中取出代码信息文件以进行定位。因此,只有当程序出现异常时,第二可执行文件才会多占用内存,而在正常执行时不额外占用内存。
请参阅图2,本发明定位程序异常的方法提供一种文件格式规范。依据文件格式规范产生的具有异常定位功能的第二可执行文件包括下述信息:第一可执行文件、分割符、代码信息文件、第一可执行文件长度。
其中,依据文件格式规范产生的具有异常定位功能的第二可执行文件格式为:首先为第一可执行文件、分割符;其次为第一个代码信息文件、分割符、第二个代码信息文件、分割符,直至第N个代码信息文件(最后一个代码信息文件),其后跟随分割符;最后为第一可执行文件长度。
其中,所述分割符设于第一可执行文件和代码信息文件之间,两个代码信息文件相互之间,以及代码信息文件和第一可执行文件长度之间。
可以理解,本发明提供文件格式规范的分割符不局限于某一种形式,只要能够实现将第一可执行文件、代码信息文件以及第一可执行文件长度之间相互分割开来即可。
需要指出的是,由于可执行文件本身的格式是PE(Partical Execute,部分可执行)格式,为了不破坏可执行文件的PE(Partical Execute,部分可执行)格式,将代码信息文件嵌入到第一可执行文件中时,加载的代码信息文件放在可执行文件的尾部。这样,PE格式信息中包含的第一可执行文件的代码段开始位置、终止位置、代码段的大小等信息均不会变化。当操作系统加载被跟踪的第一可执行文件时,将读取上述信息来预分配一定的内存空间。由于上述信息没有变化,因而分配的内存空间也无需变化。因此将代码信息文件嵌入到第一可执行文件中不会扩大程序执行时占用的内存空间。
当然,代码信息文件也可以不加载到第一可执行文件的尾部,只要加载到第一可执行文件中,同样可以实现定位程序异常的功能。
还需要指出的是,所述代码信息文件可以是MAP文件,也可以是包含代码信息的pdb文件,或者其他类型的代码信息文件。当采用pdb文件时,也可以将pdb文件加载于第一可执行文件的尾部,只是异常捕获与定位动态库需要调整相应的分析方法来实现对程序异常的定位。
请参阅图3,以MAP文件为例来说明代码信息文件的格式。所述MAP文件即为编译器在编译过程中产生的各个动态库组件和可执行文件对应的MAP文件(可访问存储区)。所述MAP文件中保存下述定位信息:MAP文件对应的模块名称、产生MAP文件的时间、模块加载的起始地址、各个段的偏移地址、公共函数的偏移地址、代码文件名和代码行偏移地址。
下面以MAP文件为例来说明依据文件格式规范如何在第一可执行文件中加载代码信息文件,而形成第二可执行文件;以及如何在第二可执行文件中删除代码信息文件。
所述MAP文件的加载和删除通过编译程序在编译过程中实现。通常,编译程序是一个自动执行脚本,编译器通过所述自动执行脚本来完成编译过程。
编译程序中,通过加载单元AppendMAP和删除单元DeleteMAP来实现所述MAP文件的加载和删除。加载单元AppendMAP用来将MAP文件加载到第一可执行文件中;删除单元DeleteMAP用来将所有MAP文件从第二可执行文件中删除。
加载单元AppendMAP将MAP文件嵌入第一可执行文件中时,只要在程序编译单元中设置加载单元,即,将加载单元AppendMAP加入编译源程序的自动执行脚本中,并在项目的工程中添加dtrace++.cpp文件,一起编译。在源程序编译完成之后,加载单元AppendMAP将编译过程中产生的MAP文件加载于同期生成的第一可执行文件中,从而形成第二可执行文件。这样就实现了加载MAP文件的过程。
加载单元AppendMAP所采用的格式是“AppendMAP xxx.exe xxx.MAPxxx.MAP”,可以将相同文件名(xxx)的MAP文件加入到相同文件名(xxx)的第一可执行文件(第一EXE文件)中。加载单元AppendMAP可以将多个MAP文件加入到相同文件名(xxx)的第一可执行文件,也可以将一个MAP文件加入到相同文件名(xxx)的第一可执行文件中,不限于上述例子中所示的追加两个MAP文件到相同文件名(xxx)的第一可执行文件中。例如,一个可执行文件本身对应一个MAP文件,如果所述可执行文件使用三个动态库DLL,所述三个DLL也分别有自己的MAP文件,因此就要在第一可执行的尾部追加4个MAP文件。
因此,利用加载单元AppendMAP可以依据文件格式规范将包含程序文件和代码行数定位信息的MAP文件加入到第一可执行文件中,并生成第二可执行文件,从而能够保证异常发生时第二可执行文件中含有足够的信息进行异常代码的定位。
删除单元DeleteMAP从第二可执行文件中删除MAP文件时,只要将删除单元DeleteMAP加入自动执行脚本,并重新编译,即将可执行文件中的Map文件删除。删除单元DeleteMAP所采用的格式是“DeleteMAP xxx.exe”。采用上述格式可以将所有MAP文件从第二可执行文件中删除。
请一并参阅图4和图5。异常捕获与异常定位的流程为:
第一步,启动第二可执行文件,执行第二可执行文件的操作。
第二步,加载异常捕获与定位动态库。第二可执行文件启动后,由dtrace++.cpp文件自动加载异常捕获与定位动态库。通常,异常捕获与定位动态库就是实际项目中通用的动态库“dtrace++.dll”,加载时需要将所述异常捕获与定位动态库放到硬盘中特定的目录下。
第三步,将异常捕获钩子挂接到第二可执行文件。异常捕获与定位动态库在被加载时会自动添加一个异常捕获钩子到被跟踪程序的异常捕获链表的最上端,所述异常捕获链表中含有处理程序异常的异常捕获单元。所述异常捕获钩子指的是一个用来捕获异常的单元(函数)指针,通常调用Windows API“SetUnhandledExceptionFilter”单元来传入上述异常捕获钩子(钩子函数指针)。
第四步,当被跟踪第二可执行程序发生异常,从异常捕获链表中逐个寻找处理此异常的异常捕获钩子,如果没有任何异常捕获钩子处理此异常,则该异常会传到异常捕获链表的最上端,由本发明提供的异常捕获钩子来处理。记录当时的函数调用栈信息,从异常捕获钩子的传入参数中可以得到异常捕获钩子函数指针(异常捕获钩子函数的形式如下:LONG WINAPIExceptionFilter(PEXCEPTION_POINTERS pException),EXCEPTION_POINTERS结构在Windows中为struct{PEXCEPTION_RECORD ExceptionRecord;PCONTEXT ContextRecord})。然后逐个将函数调用的内存地址取出来分析,如前所述,最上层的函数调用内存地址就是发生异常的内存地址。当程序发生任何未处理的异常之后都会被异常捕获与定位动态库安置的异常捕获钩子所截获。
生成系统核心数据,所述系统核心数据包括异常发生时的寄存器数据、中断信息、函数调用堆栈、异常发生的内存地址、异常发生的模块信息、异常发生的线程ID、其他线程的函数调用堆栈、程序数据段快照、程序虚拟地址空间各个模块地址分配快照等信息。所述系统核心数据中含有的异常发生的内存地址、模块信息和函数调用堆栈信息,用于后期对程序发生异常的定位分析。
需要说明的是,程序本身会在设计阶段考虑某些异常的处理,对于这些异常会在程序中采取相应的措施予以处理。本发明提供的方法只捕获程序设计阶段不予处理的异常。
第五步,异常捕获与定位动态库中含有的自行编写的代码,用来完成异常代码定位功能。当程序发生异常时,异常捕获与定位动态库自动生成异常代码定位文件,此时异常代码定位文件的内容为空,只生成文件名。
其中,所述异常代码定位文件的命名规则是:文件名与可执行文件的名称相同,只是扩展名不同。异常代码定位文件需要在可执行文件的扩展名的后部追加“log”字符和异常发生的时间。所述时间采用年、月、日、时、分、秒的记录形式。例如,可执行文件为abc.exe,当发生异常时,异常代码定位文件的命名为abc.exelog20030213092312,表示的是本文件为可执行文件abc.exe对应的异常代码定位文件,程序异常发生的时间为2003年02月13日09时23分12秒。
第六步,将包含有发生异常的内存地址、模块信息和函数调用堆栈信息等定位信息的系统核心数据写入所述异常代码定位文件。
第七步,首先,异常捕获与定位动态库以只读的方式打开现有的第二可执行文件,读取第二可执行文件尾部的第二可执行文件长度;然后,定位到MAP文件区域的起始位置,即第一个分割符位置,读取第一个MAP文件,直到遇到第二个分割符;然后,通过搜索分割符来分析每一个MAP文件起始和终止位置,并逐个读取MAP文件并放入内存中,直至将第二可执行文件中的MAP文件全部读入内存。
所述MAP文件读入内存时,以图6所示格式生成MAP文件的内存映射。所述内存映射为CMAPInfolst(MAP文件信息列表类)列表。所述CMAPInfolst列表包含多个CMAPInfo(MAP文件信息类),每一个CMAPInfo对应一个加载在EXE文件中的MAP文件信息块。每一个CMAPInfo都包含在所述CMAPInfolst列表中,换言之,CMAPInfolst列表中包含加载在EXE文件中所有的MAP文件信息块。读取MAP文件信息块时,以分割符为界来分别获取各MAP文件信息块。对应于MAP文件信息块的每一个CMAPInfo存放有包括MAP文件模块名、模块代码加载起始地址、模块代码加载终止地址、函数地址到函数名的映射表、代码段偏移地址到代码行、文件名的映射表。CFileNameAndLine(文件名和代码行类)结构中存放所述代码行和文件名。每一个代码行和文件名对应一个CfileNameAndLine。每一个CMAPInfo包含多个CFileNameAndLine(文件名和代码行类)。
第八步,根据系统核心数据和内存中的MAP文件,确定发生异常的函数名以及代码行号和文件名,生成异常代码定位信息,并写入异常代码定位文件。
以下说明如何确定发生异常的函数名:
首先,判断与函数调用的内存地址对应的MAP文件信息块。通过遍历CMAPInfolst中的CMAPInfo对应的MAP文件信息块,计算并判断函数调用的内存地址是否大于CMAPInfo.m_dLoadStartAddr(模块代码加载起始地址)而小于CMAPInfo.m_dLoadEndAddr(模块代码加载终止地址),如果是,则说明内存地址在这个MAP文件信息块中;否则内存地址不在此MAP文件信息块中,然后重复上述步骤继续判断函数调用的内存地址是否在下一个CMAPInfo对应的MAP文件信息块中。如果在整个遍历过程中始终找不到与函数调用的内存地址对应的MAP文件信息块,则退出。
然后,通过CMAPInfo.m_decorateNameMAP(函数名对应表)和函数调用的内存地址计算出函数名称。其中,CMAPInfo.m_decorateNameMAP的键值为函数的调用地址,CMAPInfo.m_decorateNameMAP存放函数名的哈希值,所述哈希值在MAP文件读入内存时生成的。
以下说明如何判断发生异常的代码行号和文件名:
首先,通过判断函数的偏移地址是否大于模块代码加载起始地址而小于模块代码加载的终止地址而确定与函数调用的内存地址对应的MAP文件信息块。
然后,用函数调用的内存地址减去CMAPInfo.m_dLoadStartAddr,再减去固定的偏移地址0x1000,就得到了代码行偏移地址。
最后,通过代码行偏移地址与CMAPInfo.m_FileNameAndLineMAP对应表计算出发生异常在哪一个CFileNameAndLine结构,其中包括代码行号和文件名。
将包含文件名和代码行号的信息写入到异常代码定位文件。
第九步,包含文件名和代码行号的信息写入到异常代码定位文件后,异常捕获与定位动态库通过自行编写的代码关闭异常代码定位文件。
第十步,根据异常捕获与定位动态库中自行设定的信息,来判断第二可执行文件是退出还是继续执行。所述判断结果将根据第二可执行文件和发生异常的不同而不同。
需要指出的是,在CMAPInfolst中比较发生异常的地址和每一个CMAPInfo中代码加载的起始、终止地址。所述起始、终止地址就是记录在MAP文件中的各个模块在内存中的起始和终止地址,每一个模块的起始、终止地址都不相同也不能重叠。
通常,可以在程序启动时由操作系统自动分配足够大小的内存空间以供各个模块使用,而无需在编译阶段指定所述模块的起始、终止地址。但是如果由操作系统自动指定起始地址,那么将难以保证实际程序运行的地址和MAP文件中的地址一致,从而无法对程序异常进行定位分析。而且,在编译期间指定起始地址可以使得程序加载速度变快。因此,在本发明中,必须要求各个模块的加载起始地址在编译阶段配置好,并根据模块大小计算出终止地址。这样才能保证生成的MAP文件(MAP文件在编译阶段生成)中的模块地址和实际程序运行中的模块地址是一致的,从而可以进行异常代码定位的分析。因此,本发明提供的方法采用在程序编译期间指定起始地址的地址分配方式。
下面举例说明,当程序出现异常时采用本发明提供的方法如何实现程序异常的自动定位。
第二可执行文件启动后加载异常定位动态库dtrace++.dll,然后dtrace++.dll调用SetUnhandledExceptionFilter函数来挂结ExceptionFilter异常捕获钩子函数。
当发生异常时,调用ExceptionFilter异常捕获钩子函数以传入EXCEPTION_POINTERS结构,
函数中首先生成异常代码定位文件,此时只生成该文件的文件名,文件内容为空。
然后通过EXCEPTION_POINTERS结构分析出系统核心数据,即发生异常的模块名、线程ID、在模块中的偏移地址、异常ID、异常标志,记录到文件中(如图6:
2003-02-13 09:23:12:Caused an exception in thread[00000CEC] at00601099 0001:00000099H:\Log\MAPEXE\Debug\MAPDLL.dll
[Exception Code:0xC0000005]
[Exception Flags:0x00000000]
)。
之后,以只读的方式打开EXE文件,首先读取EXE文件末尾的原始EXE文件长度,然后定位到MAP文件区域的起始位置,然后通过搜索图中分割符来分析每一个MAP文件起始和终止位置,逐个取出MAP文件并放入CMAPInfo结构中,每一个CManInfo中包含多个代码信息块CFileNameAndLine,最终构成一个CMAPInfolst列表。
再后,逐个分析发生异常时EXCEPTION_POINTERS结构中函数调用栈中每一个函数的代码行信息,包括模块名、异常在内存中的地址,在模块中的偏移地址、发生异常的函数名、发生异常的文件名和发生异常的代码行号,栈顶的函数就是发生异常的函数,发生异常的内存地址就在这个函数中。
分析代码行信息的具体算法如下:
首先判断函数调用的内存地址落在哪一个MAP文件信息块中,通过遍历CMAPInfolst中的CMAPInfo,计算函数调用的内存地址是否大于CMAPInfo.m_dLoadStartAddr而小于CMAPInfo.m_dLoadEndAddr。如果是,就说明落在这个MAP文件信息块中;然后通过CMAPInfo.m_decorateNameMAP对应表和函数调用的内存地址计算出函数名称;然后用函数调用的内存地址减去CMAPInfo.m_dLoadStartAddr再减去0x1000,就得到了代码行偏移地址。然后通过CMAPInfo.m_FileNameAndLineMAP对应表和代码行偏移地址计算出是哪一个CFileNameAndLine结构,其中包括代码行号和文件名。这样,就将发生异常的文件名和代码行号找到。
之后,将上述文件名和代码行号等信息添加到异常代码定位文件中,并关闭所述异常代码定位文件。
最后,判断第二可执行文件是退出还是继续执行。
请参阅图6,是由WinCordDump工具根据以上方法生成的异常代码定位文件。
所述WinCoreDump工具包括文件格式规范中加载单元AppendMAP和删除单元DeleteMAP,以及异常捕获与定位动态库中异常捕获钩子和异常代码定位单元。
所述异常代码定位文件包括:发生异常时间、发生异常的线程ID、发生异常的内存地址、发生异常的模块名称及所在模块中的偏移地址、异常ID、异常标志(用以标志所述异常是否可以让程序继续执行,如果收到一个不可继续执行(noncontinuable)的异常后还要继续执行文件,会产生一个异常终止程序)、发生异常时的函数调用栈信息、函数从属的文件名、函数从属的执行体和代码行号。
图6中第一行“2003-02-13 09:23:12:Caused an exception in thread[00000CEC]at00601099 0001:00000099H:\Log\MAPEXE\Debug\MAPDLL.dll”固定是发生异常的时间、线程ID、发生异常的内存地址、在模块中的偏移地址和模块名称;第二行“[Exception Code:0xC0000005]”是异常ID;第三行“[Exception Flags:0x00000000]”是异常标志;从第四行起是发生异常时线程的函数调用栈信息(所述函数调用栈信息的行数多少视具体调用情况而定:调用的函数多,则函数调用栈中的行数较多;调用的函数少,则函数调用栈中的行述较少)。比如图6中的信息如下:第四行用于说明从第5行起是线程ID00000CEC的函数调用栈信息,第5行之后的[1]和[4]分别是栈顶和栈底,可以看出,系统首先调用的是kernel32.dll的某个函数(由于kernel32.dll不是自行开发的,没有相应的MAP文件,因而只能够知道内存地址和偏移量,却无法确切地定位到相应的代码行),然后调用的是MAPEXE1.EXE的某个函数,然后又是MAPEXE1.EXE的另一个函数,最后调用的是MAPDLL.dll,由于所述MAPDLL.dll是自行开发的,有对应的MAP文件,并且MAP文件已通过AppendMAP命令追加到了MAPEXE1.EXE中,因此可以确切地将异常代码信息定位到代码行,即图6中第5行中所示的信息:异常发生在MAPDLL.cpp文件的38行,位于MAPDLLHappyFunc函数中。由于MAPDLLHappyFunc函数在栈顶,那么说明异常是发生在该MAPDLLHappyFunc函数中以及MAPDLL.cpp文件的第38行。图6所示的异常代码定位文件就清楚表明了发生异常的函数名、文件名以及在所述文件中的具体代码行号。
以上所述仅是本发明的优选实施方式,应当指出,对于本技术领域的普通技术人员来说,在不脱离本发明原理的前提下,还可以作出若干改进和润饰,这些改进和润饰也应视为本发明的保护范围。
Claims (12)
1.一种定位程序异常的方法,其特征在于包括步骤:
1)生成第一可执行文件以及代码信息文件,所述第一可执行文件是通过编译源程序生成的原始可执行文件;
2)在所述第一可执行文件中加载所述代码信息文件,生成含有代码信息文件信息块的第二可执行文件;
3)启动所述第二可执行文件,加载异常捕获与定位动态库;
4)当第二可执行文件发生异常时,产生系统核心数据,所述系统核心数据包含函数调用的内存地址;
5)从所述第二可执行文件读取代码信息文件,比较前述系统核心数据和代码信息文件,确定程序异常的位置。
2.根据权利要求1所述的定位程序异常的方法,其特征在于,所述第二可执行文件包括第一可执行文件、代码信息文件、分割符和第一可执行文件长度数据;
所述分割符位于所述第一可执行文件和所述代码信息文件之间、相邻的两个代码信息文件之间,以及代码信息文件和第一可执行文件长度之间,用于分割第一可执行文件、各个代码信息文件以及第一可执行文件长度。
3.根据权利要求1所述的定位程序异常的方法,其特征在于,所述步骤1)具体包括在程序编译单元中设置加载单元,并通过程序编译单元生成第一可执行文件以及代码信息文件;
在所述步骤2)中,通过加载单元将代码信息文件添加至第一可执行文件。
4.根据权利要求1所述的定位程序异常的方法,其特征在于,所述步骤3)中加载异常捕获与定位动态库的过程包括:
将异常捕获钩子添加到第二可执行文件中,用于捕获异常;
将异常代码定位单元嵌入第二可执行文件中,用于生成异常代码定位文件。
5.根据权利要求4所述的定位程序异常的方法,其特征在于,所述步骤3)中异常捕获钩子添加到第二可执行文件中的异常捕获链表中。
6.根据权利要求1至5中任一项所述的定位程序异常的方法,其特征在于,所述步骤5)具体包括步骤:
41)生成异常代码定位文件的文件名;
42)将系统核心数据写入异常代码定位文件;
43)读取第二可执行文件中的代码信息文件信息块;
44)比较系统核心数据和代码信息文件,确定程序异常对应的文件名、函数名和代码行。
7.根据权利要求6所述的定位程序异常的方法,其特征在于,所述步骤43)具体包括:从第二可执行文件中分离出各个代码信息文件,并依据内存映射格式将代码信息文件读入内存。
8.根据权利要求7所述的定位程序异常的方法,其特征在于,所述代码信息文件是MAP文件。
9.根据权利要求8所述的定位程序异常的方法,其特征在于,所述MAP文件在内存中以MAP文件信息列表类格式存放。
10.根据权利要求9所述的定位程序异常的方法,其特征在于,所述MAP文件信息列表类对应MAP文件信息类以及文件名和代码行类;
所述文件名和代码行类存放文件名和代码行;
所述MAP文件信息类存放MAP文件模块名、模块代码加载起始地址、模块代码加载终止地址、文件名和代码行对应表以及函数名对应表;
所述文件名和代码行对应表存放读取MAP文件时生成的文件名和代码行的哈希值;
所述函数名对应表存放读取MAP文件时生成的函数名的哈希值,键值为函数地址。
11.根据权利要求10所述的定位程序异常的方法,其特征在于,所述步骤44)中进一步包括:
通过比较函数调用的内存地址与所述模块代码加载起始地址和模块代码加载终止地址,来分析、查找对应于函数调用的内存地址的MAP文件信息块;
通过函数调用的内存地址和函数名对应表确定函数名。
12.根据权利要求10所述的定位程序异常的方法,其特征在于,所述步骤44)进一步包括:
通过比较函数调用的内存地址与所述模块代码加载起始地址和模块代码加载终止地址,来分析、查找对应于函数调用的内存地址的MAP文件信息块;
将函数调用的内存地址减去模块代码加载起始地址以及偏移地址,得到代码行偏移地址;
通过代码行偏移地址与文件名和代码行对应表查找发生异常的文件名和代码行类结构,依据所述文件名和代码行类结构确定发生异常的代码行号和文件名。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CNB2004100428237A CN1329836C (zh) | 2004-05-26 | 2004-05-26 | 定位程序异常的方法 |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CNB2004100428237A CN1329836C (zh) | 2004-05-26 | 2004-05-26 | 定位程序异常的方法 |
Publications (2)
Publication Number | Publication Date |
---|---|
CN1704908A CN1704908A (zh) | 2005-12-07 |
CN1329836C true CN1329836C (zh) | 2007-08-01 |
Family
ID=35577217
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CNB2004100428237A Expired - Fee Related CN1329836C (zh) | 2004-05-26 | 2004-05-26 | 定位程序异常的方法 |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN1329836C (zh) |
Families Citing this family (28)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN101231599B (zh) * | 2008-02-02 | 2010-07-14 | 中兴通讯股份有限公司 | 一种定位特定内存被函数非法改写的方法 |
CN103246602B (zh) * | 2012-02-14 | 2017-03-01 | 阿里巴巴集团控股有限公司 | 代码覆盖率确定方法及系统、代码覆盖检测方法及系统 |
CN102902599B (zh) | 2012-09-17 | 2016-08-24 | 华为技术有限公司 | 虚拟机内部故障处理方法、装置及系统 |
CN103164322B (zh) * | 2013-04-15 | 2016-02-17 | 腾讯科技(深圳)有限公司 | 一种程序崩溃的签名方法和装置 |
US9442825B2 (en) | 2013-04-15 | 2016-09-13 | Tencent Technology (Shenzhen) Company Limited | Method and device for signing program crash |
CN104731696B (zh) * | 2013-12-19 | 2017-10-10 | 腾讯科技(深圳)有限公司 | 定位程序代码中bug的方法及相关装置 |
CN105204990B (zh) * | 2015-08-21 | 2017-11-28 | 上海斐讯数据通信技术有限公司 | 一种异常调试方法及系统 |
CN105512015B (zh) * | 2015-12-15 | 2018-09-04 | 北京奇虎科技有限公司 | 一种安卓目标应用崩溃统计方法和装置 |
CN106161087A (zh) * | 2016-06-28 | 2016-11-23 | 浪潮(北京)电子信息产业有限公司 | 一种Linux系统的网卡错误事件收集方法及系统 |
CN105893102B (zh) * | 2016-06-29 | 2019-11-12 | 珠海豹趣科技有限公司 | 一种反病毒安全软件触发蓝屏的处理方法、装置及电子设备 |
CN106339303A (zh) * | 2016-08-23 | 2017-01-18 | 浪潮电子信息产业股份有限公司 | 一种运行日志异常分析方法 |
CN108111328B (zh) * | 2016-11-24 | 2021-03-26 | 腾讯科技(深圳)有限公司 | 一种异常处理方法及装置 |
CN106598871A (zh) * | 2016-12-29 | 2017-04-26 | 山东鲁能智能技术有限公司 | Linux下的崩溃文件自动化分析方法及系统 |
CN108334515B (zh) * | 2017-01-20 | 2022-07-15 | 阿里巴巴集团控股有限公司 | 一种处理崩溃文件中堆栈地址的方法、装置及系统 |
CN106940681B (zh) * | 2017-03-11 | 2020-07-21 | 苏州浪潮智能科技有限公司 | 一种利用跟踪函数和解析工具调试存储软件的方法 |
CN107704332B (zh) * | 2017-09-28 | 2021-06-15 | 努比亚技术有限公司 | 冻屏解决方法、移动终端及计算机可读存储介质 |
CN108089977B (zh) * | 2017-11-28 | 2020-07-31 | 维沃移动通信有限公司 | 一种应用程序的异常处理方法、装置及移动终端 |
CN110032502B (zh) * | 2018-01-11 | 2023-05-26 | 广州市康锦信息技术有限公司 | 一种异常处理的方法、装置及电子设备 |
CN108549602B (zh) * | 2018-03-30 | 2022-03-08 | 深圳市江波龙电子股份有限公司 | 一种软件调试方法 |
CN110147294B (zh) * | 2019-05-23 | 2023-10-03 | Oppo广东移动通信有限公司 | 调试信息的获取方法、装置、终端及计算机可读存储介质 |
CN112231520A (zh) * | 2019-07-15 | 2021-01-15 | 北京达佳互联信息技术有限公司 | 数据处理方法、装置、电子设备及存储介质 |
CN110532178A (zh) * | 2019-08-09 | 2019-12-03 | 四川虹美智能科技有限公司 | 一种安卓系统库文件崩溃位置定位方法及装置 |
CN111966576A (zh) * | 2020-06-29 | 2020-11-20 | 北京百度网讯科技有限公司 | 异常代码的定位方法、装置、电子设备及存储介质 |
CN111858359B (zh) * | 2020-07-23 | 2024-01-30 | 珠海豹趣科技有限公司 | 可执行文件的工程代码位置获取方法和装置 |
CN112069056B (zh) * | 2020-07-31 | 2023-09-01 | 江苏航天龙梦信息技术有限公司 | 一种uefi固件富调试的方法 |
CN112035185A (zh) * | 2020-09-01 | 2020-12-04 | 网易传媒科技(北京)有限公司 | 数据还原方法、装置、存储介质和计算设备 |
CN112764963A (zh) * | 2021-01-29 | 2021-05-07 | 恒鸿达科技有限公司 | 一种异常日志信息自动收集的方法、装置、设备和介质 |
CN112860473B (zh) * | 2021-04-27 | 2021-07-30 | 武汉深之度科技有限公司 | 程序运行错误时定位源代码的方法、装置及计算设备 |
Citations (5)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
US5822589A (en) * | 1996-12-06 | 1998-10-13 | Hewlett-Packard Company | Method for locating errors in a computer program |
US6202173B1 (en) * | 1992-05-29 | 2001-03-13 | British Telecommunications Public Limited Company | Software fault location |
US6493834B1 (en) * | 1999-08-24 | 2002-12-10 | International Business Machines Corporation | Apparatus and method for dynamically defining exception handlers in a debugger |
CN1414711A (zh) * | 2002-05-23 | 2003-04-30 | 华为技术有限公司 | 一种程序状态的故障定位方法 |
CN1492320A (zh) * | 2002-10-25 | 2004-04-28 | 华为技术有限公司 | Windows程序异常捕获及定位方法 |
-
2004
- 2004-05-26 CN CNB2004100428237A patent/CN1329836C/zh not_active Expired - Fee Related
Patent Citations (5)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
US6202173B1 (en) * | 1992-05-29 | 2001-03-13 | British Telecommunications Public Limited Company | Software fault location |
US5822589A (en) * | 1996-12-06 | 1998-10-13 | Hewlett-Packard Company | Method for locating errors in a computer program |
US6493834B1 (en) * | 1999-08-24 | 2002-12-10 | International Business Machines Corporation | Apparatus and method for dynamically defining exception handlers in a debugger |
CN1414711A (zh) * | 2002-05-23 | 2003-04-30 | 华为技术有限公司 | 一种程序状态的故障定位方法 |
CN1492320A (zh) * | 2002-10-25 | 2004-04-28 | 华为技术有限公司 | Windows程序异常捕获及定位方法 |
Also Published As
Publication number | Publication date |
---|---|
CN1704908A (zh) | 2005-12-07 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
CN1329836C (zh) | 定位程序异常的方法 | |
CN103593216B (zh) | 将ubi格式的系统文件制作成工厂烧录映像文件方法 | |
US6496979B1 (en) | System and method for managing application installation for a mobile device | |
US6973646B1 (en) | Method for compiling program components in a mixed static and dynamic environment | |
US8468501B2 (en) | Partial recording of a computer program execution for replay | |
US5623661A (en) | System for and method of providing delta-versioning of the contents of PCTE file objects | |
US20090100410A1 (en) | System and method for tracking software changes | |
CN111756575A (zh) | 存储服务器的性能分析方法及装置、电子设备 | |
CN103077043B (zh) | 一种快速启动及运行Linux的方法 | |
CN104598823A (zh) | 一种安卓系统中内核级rootkit检测方法及其系统 | |
JP2001511918A (ja) | 階層的エラー・レポーティング・システム | |
CN101308471B (zh) | 一种恢复数据的方法及装置 | |
CN100349131C (zh) | 一种应用程序故障的定位方法 | |
CN108829465B (zh) | 一种基于直接读写flash的本地动态加载系统及方法 | |
CN103677937B (zh) | 升级软件和运行软件的方法及装置 | |
US20080229282A1 (en) | Patch-aware editor | |
CN107391112A (zh) | 一种文件版本检测方法及其专用装置 | |
US20050268283A1 (en) | Audited builds based upon separate class dependency records | |
JP4884480B2 (ja) | Sqlプロシージャを配備するためのシステム及び方法 | |
WO2021169163A1 (zh) | 一种文件数据存取方法、装置和计算机可读存储介质 | |
US8667035B2 (en) | Method of converting a filesystem while the filesystem remains in an active state | |
US9477496B2 (en) | Method and apparatus for loading classes and re-organizing class archives | |
CN114860654A (zh) | 一种基于Flink数据流的Iceberg表Schema动态变更方法及系统 | |
CN109032929A (zh) | 一种程序日志记录获取方法、装置及电子设备 | |
CN100570579C (zh) | 系统故障定位方法 |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
C06 | Publication | ||
PB01 | Publication | ||
C10 | Entry into substantive examination | ||
SE01 | Entry into force of request for substantive examination | ||
C14 | Grant of patent or utility model | ||
GR01 | Patent grant | ||
CF01 | Termination of patent right due to non-payment of annual fee |
Granted publication date: 20070801 Termination date: 20150526 |
|
EXPY | Termination of patent right or utility model |