專利名稱:定位程序異常的方法
技術(shù)領(lǐng)域:
本發(fā)明涉及數(shù)據(jù)處理領(lǐng)域,特別是涉及一種當(dāng)程序出現(xiàn)異常時,對程序的異常進(jìn)行準(zhǔn)確定位的方法。
背景技術(shù):
隨著計算機(jī)的日益普及和快速發(fā)展,人們不僅在工作和生活中越來越多地依賴于各種各樣的程序,而且對程序的功能提出越來越高的要求。伴隨著功能的提高與加強(qiáng),程序的復(fù)雜度也隨之增加,由此導(dǎo)致程序的穩(wěn)定性和可靠性下降,使得程序在運(yùn)行過程中會出現(xiàn)各種異常狀況。為了跟蹤、查找程序發(fā)生異常的確切位置,人們開發(fā)了多種捕獲程序異常和定位程序異常的方法。
目前,較為流行的定位程序異常的方法大都采用二次分析的方法。首先,由維護(hù)人員分別保存編譯過程中產(chǎn)生的可執(zhí)行文件和被跟蹤程序?qū)?yīng)的程序符號庫。所述程序符號庫含有程序代碼和符號信息。然后,由維護(hù)人員事先進(jìn)行捕獲程序異常的設(shè)置,選定被跟蹤程序。當(dāng)被跟蹤程序出現(xiàn)異常時,首先生成異常發(fā)生的內(nèi)存地址、異常發(fā)生的模塊信息和函數(shù)調(diào)用棧等系統(tǒng)核心數(shù)據(jù);然后由維護(hù)人員將程序符號庫導(dǎo)入內(nèi)存,在內(nèi)存中比較、分析所述系統(tǒng)核心數(shù)據(jù)與程序符號庫,并確定程序發(fā)生異常的位置;最終生成含有上述位置信息的異常代碼定位文件。
實際使用中,常采用的程序符號庫為含有程序調(diào)試信息的pdb(programdatabase,程序數(shù)據(jù)庫)文件。再者,所述系統(tǒng)核心數(shù)據(jù)的具體表現(xiàn)形式和格式因所采用的捕獲程序異常和定位程序異常的工具和方法的不同而不同。
目前,Windows操作系統(tǒng)下較為常用的定位程序異常的方法是采用Userdump工具和WinDebug工具來實現(xiàn)對程序發(fā)生的異常進(jìn)行定位。
首先,維護(hù)人員使用Userdump工具選定被跟蹤程序,設(shè)置需要跟蹤的異常種類;保存程序編譯過程中生成的pdb文件。
然后,維護(hù)人員啟動被跟蹤程序,Userdump工具在被跟蹤程序上注冊異常捕獲鉤子,用于捕獲被跟蹤程序發(fā)生的異常。被跟蹤程序運(yùn)行過程中,Userdump工具始終駐留在內(nèi)存中,一旦被跟蹤程序發(fā)生異常,立即捕獲異常并記錄系統(tǒng)核心數(shù)據(jù)文件。所述系統(tǒng)核心數(shù)據(jù)可以是dump文件。
然后,維護(hù)人員利用WinDebug工具導(dǎo)入dump文件和pdb文件,由WinDebug工具判斷dump文件中記錄的發(fā)生異常的內(nèi)存地址是否在pdb文件中。
然后,根據(jù)dump文件對應(yīng)的函數(shù)調(diào)用棧中的函數(shù)從pdb文件中分析出異常函數(shù)的函數(shù)名,并根據(jù)發(fā)生異常的內(nèi)存地址計算異常發(fā)生在第幾行代碼。
最后,生成異常代碼定位報告。所述異常代碼定位報告包括發(fā)生異常的代碼位置、發(fā)生異常的代碼的模塊名(例如kernel32.dll)、文件名、函數(shù)調(diào)用棧、函數(shù)名等信息。
盡管,現(xiàn)有技術(shù)提供的定位程序異常的工具和方法可以實現(xiàn)對程序異常的跟蹤與定位,但是在實際應(yīng)用中仍然不可避免地存在下述缺陷其一,采用現(xiàn)有技術(shù)提供的定位程序異常的方法,需要由維護(hù)人員單獨(dú)保存程序編譯過程中生成的程序符號庫。當(dāng)程序出現(xiàn)異常時,維護(hù)人員取出程序符號庫文件,并利用異常定位工具將其導(dǎo)入,然后由異常定位工具比較系統(tǒng)核心數(shù)據(jù)和程序符號庫,最終生成異常代碼定位文件。因而采用現(xiàn)有技術(shù)中提供的定位程序異常的方法不能自動地對程序發(fā)生的異常進(jìn)行定位。同時,由于人工的介入,導(dǎo)致人力資源浪費(fèi),并使得定位分析時間較長、工作效率較低。
其二,現(xiàn)有技術(shù)提供的方法和工具需要由維護(hù)人員將程序編譯過程中生成的各個pdb文件,以及生成的pdb文件的程序代碼作為各個分立的文件分別保存。由于系統(tǒng)核心數(shù)據(jù)對應(yīng)于被跟蹤程序,而被跟蹤程序重新編譯將重新生成pdb文件。因此,只有程序代碼和相應(yīng)的pdb文件版本保持一致,才能通過系統(tǒng)核心數(shù)據(jù)與pdb文件判斷出程序出現(xiàn)異常的確切位置。也就是,若程序代碼出現(xiàn)更改,必須重新生成pdb文件并與相同版本的程序代碼一同保存。然而,若由人工或采用簡單的版本管理機(jī)制來分別保存程序代碼和相應(yīng)的pdb文件,將難以保證兩者的版本絕對一致,尤其是對于含有多個動態(tài)庫組件的程序。因此,在查找程序發(fā)生異常的確切位置時,很難找到與發(fā)生異常的程序代碼對應(yīng)的pdb文件,也就難以確定程序發(fā)生異常的確切位置。為此,必須使用一套高級的版本管理機(jī)制,來保持pdb文件版本與程序代碼版本之間的對應(yīng)關(guān)系。但是,若自行開發(fā)完善的版本管理機(jī)制將加大程序開發(fā)的成本,同時增加開發(fā)的技術(shù)難度;若直接采用現(xiàn)有的高級版本管理機(jī)制,由于高級版本管理機(jī)制的價格較高,同樣會導(dǎo)致成本的提高。
其三,現(xiàn)有技術(shù)中經(jīng)常借助Userdump工具來判斷程序是否有異常發(fā)生。而Userdump工具需要安裝在用戶側(cè)的計算機(jī)上,并且需要在啟動被跟蹤程序之前進(jìn)行參數(shù)設(shè)定,通常用戶無法自行完成設(shè)定,需要由Userdump工具供應(yīng)商提供相應(yīng)的服務(wù),因而使用Userdump等工具的成本較高。
其四,一旦被跟蹤的程序啟動,Userdump工具始終駐留在內(nèi)存中等待被跟蹤程序發(fā)生異常,而不會自行退出內(nèi)存。因此在被跟蹤程序運(yùn)行過程中,Userdump工具始終占用內(nèi)存資源。
發(fā)明內(nèi)容
本發(fā)明解決的技術(shù)問題在于提供了一種定位程序異常的方法,能夠無需單獨(dú)保存代碼信息文件就可自動地對程序發(fā)生的異常進(jìn)行定位。
為此,本發(fā)明解決技術(shù)問題的技術(shù)方案是提供一種定位程序異常的方法,包括步驟1)成第一可執(zhí)行文件以及代碼信息文件;2)在所述第一可執(zhí)行文件中加載所述代碼信息文件,生成含有代碼信息文件信息塊的第二可執(zhí)行文件;3)啟動所述第二可執(zhí)行文件,加載異常捕獲與定位動態(tài)庫;4)當(dāng)?shù)诙蓤?zhí)行文件發(fā)生異常時,產(chǎn)生系統(tǒng)核心數(shù)據(jù),所述系統(tǒng)核心數(shù)據(jù)包含函數(shù)調(diào)用的內(nèi)存地址;5)從所述第二可執(zhí)行文件讀取代碼信息文件,比較前述系統(tǒng)核心數(shù)據(jù)和代碼信息文件,確定程序異常的位置。
所述第二可執(zhí)行文件包括第一可執(zhí)行文件、代碼信息文件、分割符和第一可執(zhí)行文件長度數(shù)據(jù);所述分割符位于所述第一可執(zhí)行文件和所述代碼信息文件之間、相鄰的兩個代碼信息文件之間,以及代碼信息文件和第一可執(zhí)行文件長度之間,用于分割第一可執(zhí)行文件、各個代碼信息文件以及第一可執(zhí)行文件長度。
所述步驟1)具體包括在程序編譯單元中設(shè)置加載單元,并通過程序編譯單元生成第一可執(zhí)行文件以及代碼信息文件;在所述步驟2)中,通過加載單元將代碼信息文件添加至第一可執(zhí)行文件。
所述步驟3)中加載異常捕獲與定位動態(tài)庫的過程包括將異常捕獲鉤子添加到第二可執(zhí)行文件中,用于捕獲異常;將異常代碼定位單元嵌入第二可執(zhí)行文件中,用于生成異常代碼定位文件。
所述步驟3)中異常捕獲鉤子添加到第二可執(zhí)行文件中的異常捕獲鏈表中。
所述步驟5)具體包括步驟41)生成異常代碼定位文件的文件名;42)將系統(tǒng)核心數(shù)據(jù)寫入異常代碼定位文件;43)讀取第二可執(zhí)行文件中的代碼信息文件信息塊;44)比較系統(tǒng)核心數(shù)據(jù)和代碼信息文件,確定程序異常對應(yīng)的文件名、函數(shù)名和代碼行。
所述步驟43)具體包括從第二可執(zhí)行文件中分離出各個代碼信息文件,并依據(jù)內(nèi)存映射格式將代碼信息文件讀入內(nèi)存。
所述代碼信息文件是MAP文件。
所述MAP文件在內(nèi)存中以MAP文件信息列表類格式存放。
所述MAP文件信息列表類對應(yīng)MAP文件信息類以及文件名和代碼行類;所述文件名和代碼行類存放文件名和代碼行;所述MAP文件信息類存放MAP文件模塊名、模塊代碼加載起始地址、模塊代碼加載終止地址、文件名和代碼行對應(yīng)表以及函數(shù)名對應(yīng)表;所述文件名和代碼行對應(yīng)表存放讀取MAP文件時生成的文件名和代碼行的哈希值;所述函數(shù)名對應(yīng)表存放讀取MAP文件時生成的函數(shù)名的哈希值,鍵值為函數(shù)地址。
所述步驟44)中進(jìn)一步包括通過比較函數(shù)調(diào)用的內(nèi)存地址與所述模塊代碼加載起始地址和模塊代碼加載終止地址,來分析、查找對應(yīng)于函數(shù)調(diào)用的內(nèi)存地址的MAP文件信息塊;通過函數(shù)調(diào)用的內(nèi)存地址和函數(shù)名對應(yīng)表確定函數(shù)名。
所述步驟44)進(jìn)一步包括通過比較函數(shù)調(diào)用的內(nèi)存地址與所述模塊代碼加載起始地址和模塊代碼加載終止地址,來分析、查找對應(yīng)于函數(shù)調(diào)用的內(nèi)存地址的MAP文件信息塊;將函數(shù)調(diào)用的內(nèi)存地址減去模塊代碼加載起始地址以及偏移地址,得到代碼行偏移地址;通過代碼行偏移地址與文件名和代碼行對應(yīng)表查找發(fā)生異常的文件名和代碼行類結(jié)構(gòu),依據(jù)所述文件名和代碼行類結(jié)構(gòu)確定發(fā)生異常的代碼行號和文件名。
相對于現(xiàn)有技術(shù),本發(fā)明的有益效果是其一,本發(fā)明提供了一種定位程序異常的方法,無需單獨(dú)保存代碼信息文件,而是在編譯過程中,將相應(yīng)的代碼信息文件加載到第一可執(zhí)行文件中,形成第二可執(zhí)行文件。當(dāng)程序出現(xiàn)異常后,自動捕獲程序的異常,并生成系統(tǒng)核心數(shù)據(jù)。通過比較系統(tǒng)核心數(shù)據(jù)和加載于第二可執(zhí)行文件的代碼信息文件來自動確定程序發(fā)生異常的模塊名、函數(shù)名和代碼行。因而,本發(fā)明提供的方法無需人工干預(yù)就可以自動定位異常代碼,提高了效率。
其二,由于本發(fā)明提供的一種定位程序異常的方法,可以無需單獨(dú)保存諸如pdb文件的任何程序符號庫文件,而只保存加載有代碼信息文件的第二可執(zhí)行文件,就能夠在程序出現(xiàn)異常時自動生成異常定位信息文件。因而簡化了軟件版本管理機(jī)制,降低了開發(fā)成本。
其三,由于本發(fā)明在編譯過程中將代碼信息文件加載到可執(zhí)行文件中。同時,本發(fā)明提供的定位程序異常的方法在可執(zhí)行文件啟動后,可以加載異常捕獲與定位動態(tài)庫,因而無需借助Userdump工具和WinDebug工具,也不需要進(jìn)行特殊配置,就可以實現(xiàn)自動捕獲程序的異常以及自動定位程序異常,并自動生成異常代碼定位文件,因此降低了成本。
其四,由于代碼信息文件加載到可執(zhí)行文件的尾部,沒有改變原始可執(zhí)行文件的長度,因而不影響源程序的正常執(zhí)行和內(nèi)存的占用情況。
圖1是定位程序異常的方法的流程圖;圖2是具有異常定位功能的可執(zhí)行文件格式的示意圖;圖3是MAP文件格式的示意圖;圖4是異常捕獲與分析流程的示意圖;圖5是MAP文件內(nèi)存映射格式的示意圖;圖6是異常代碼定位文件的示意圖。
具體實施例方式
本發(fā)明提供的定位程序異常的方法,當(dāng)程序發(fā)生異常時,可以自動捕獲程序的異常以及定位程序異常,并自動生成上述異常代碼定位文件;當(dāng)程序未發(fā)生異常時,不會影響程序的正常執(zhí)行,也不會額外占用內(nèi)存資源。
請參閱圖1,本發(fā)明提供的定位程序異常的方法主要包括下述步驟步驟210,編譯單元編譯源程序,生成代碼信息文件,以及原始可執(zhí)行文件(EXE文件)(以下稱第一可執(zhí)行文件)。
步驟220,將代碼信息文件嵌入到第一可執(zhí)行文件中,生成新的可執(zhí)行文件(以下稱第二可執(zhí)行文件)。所述第二可執(zhí)行文件中包含多個代碼信息文件,多個代碼信息文件構(gòu)成代碼信息文件信息塊。
步驟230,啟動第二可執(zhí)行文件,加載異常捕獲與定位動態(tài)庫。所述異常捕獲與定位動態(tài)庫包括異常捕獲鉤子和異常代碼定位單元。第二可執(zhí)行文件啟動后,所述異常捕獲鉤子掛接到被跟蹤的第二可執(zhí)行文件中,用于捕獲異常。
步驟240,當(dāng)?shù)诙蓤?zhí)行文件發(fā)生異常后,異常捕獲鉤子捕獲到程序異常,生成異常代碼定位文件的文件名。此外,還生成含有發(fā)生異常的內(nèi)存地址、發(fā)生異常的模塊信息和函數(shù)調(diào)用棧等定位信息的系統(tǒng)核心數(shù)據(jù)。所述系統(tǒng)核心數(shù)據(jù)含有的信息用于對程序出現(xiàn)的異常進(jìn)行定位。將系統(tǒng)核心數(shù)據(jù)寫入異常代碼定位文件中。
步驟250,將第二可執(zhí)行文件中包含的代碼信息文件讀入內(nèi)存,比較、分析同在內(nèi)存中的系統(tǒng)核心數(shù)據(jù)和代碼信息文件,從而確定程序異常所對應(yīng)的文件名、函數(shù)名和代碼行。
步驟260,將包含程序異常對應(yīng)的文件名、函數(shù)名和代碼行代碼定位信息寫入異常代碼定位文件中,生成最終的異常代碼定位文件。
最終生成的異常代碼定位文件包括發(fā)生異常時間、發(fā)生異常的線程標(biāo)識、發(fā)生異常的內(nèi)存地址、發(fā)生異常所在的可執(zhí)行體和偏移量、異常編碼、異常標(biāo)志、發(fā)生異常時的函數(shù)調(diào)用棧信息、函數(shù)從屬的文件名、函數(shù)從屬的執(zhí)行體和具體的行數(shù)。
需要指出的是,本發(fā)明提供的定位程序異常的方法不是在第二可執(zhí)行文件一啟動就讀入代碼信息文件,而是在程序出現(xiàn)異常時,由異常捕獲與定位動態(tài)庫自動從第二可執(zhí)行文件中取出代碼信息文件以進(jìn)行定位。因此,只有當(dāng)程序出現(xiàn)異常時,第二可執(zhí)行文件才會多占用內(nèi)存,而在正常執(zhí)行時不額外占用內(nèi)存。
請參閱圖2,本發(fā)明定位程序異常的方法提供一種文件格式規(guī)范。依據(jù)文件格式規(guī)范產(chǎn)生的具有異常定位功能的第二可執(zhí)行文件包括下述信息第一可執(zhí)行文件、分割符、代碼信息文件、第一可執(zhí)行文件長度。
其中,依據(jù)文件格式規(guī)范產(chǎn)生的具有異常定位功能的第二可執(zhí)行文件格式為首先為第一可執(zhí)行文件、分割符;其次為第一個代碼信息文件、分割符、第二個代碼信息文件、分割符,直至第N個代碼信息文件(最后一個代碼信息文件),其后跟隨分割符;最后為第一可執(zhí)行文件長度。
其中,所述分割符設(shè)于第一可執(zhí)行文件和代碼信息文件之間,兩個代碼信息文件相互之間,以及代碼信息文件和第一可執(zhí)行文件長度之間。
可以理解,本發(fā)明提供文件格式規(guī)范的分割符不局限于某一種形式,只要能夠?qū)崿F(xiàn)將第一可執(zhí)行文件、代碼信息文件以及第一可執(zhí)行文件長度之間相互分割開來即可。
需要指出的是,由于可執(zhí)行文件本身的格式是PE(Partical Execute,部分可執(zhí)行)格式,為了不破壞可執(zhí)行文件的PE(Partical Execute,部分可執(zhí)行)格式,將代碼信息文件嵌入到第一可執(zhí)行文件中時,加載的代碼信息文件放在可執(zhí)行文件的尾部。這樣,PE格式信息中包含的第一可執(zhí)行文件的代碼段開始位置、終止位置、代碼段的大小等信息均不會變化。當(dāng)操作系統(tǒng)加載被跟蹤的第一可執(zhí)行文件時,將讀取上述信息來預(yù)分配一定的內(nèi)存空間。由于上述信息沒有變化,因而分配的內(nèi)存空間也無需變化。因此將代碼信息文件嵌入到第一可執(zhí)行文件中不會擴(kuò)大程序執(zhí)行時占用的內(nèi)存空間。
當(dāng)然,代碼信息文件也可以不加載到第一可執(zhí)行文件的尾部,只要加載到第一可執(zhí)行文件中,同樣可以實現(xiàn)定位程序異常的功能。
還需要指出的是,所述代碼信息文件可以是MAP文件,也可以是包含代碼信息的pdb文件,或者其他類型的代碼信息文件。當(dāng)采用pdb文件時,也可以將pdb文件加載于第一可執(zhí)行文件的尾部,只是異常捕獲與定位動態(tài)庫需要調(diào)整相應(yīng)的分析方法來實現(xiàn)對程序異常的定位。
請參閱圖3,以MAP文件為例來說明代碼信息文件的格式。所述MAP文件即為編譯器在編譯過程中產(chǎn)生的各個動態(tài)庫組件和可執(zhí)行文件對應(yīng)的MAP文件(可訪問存儲區(qū))。所述MAP文件中保存下述定位信息MAP文件對應(yīng)的模塊名稱、產(chǎn)生MAP文件的時間、模塊加載的起始地址、各個段的偏移地址、公共函數(shù)的偏移地址、代碼文件名和代碼行偏移地址。
下面以MAP文件為例來說明依據(jù)文件格式規(guī)范如何在第一可執(zhí)行文件中加載代碼信息文件,而形成第二可執(zhí)行文件;以及如何在第二可執(zhí)行文件中刪除代碼信息文件。
所述MAP文件的加載和刪除通過編譯程序在編譯過程中實現(xiàn)。通常,編譯程序是一個自動執(zhí)行腳本,編譯器通過所述自動執(zhí)行腳本來完成編譯過程。
編譯程序中,通過加載單元AppendMAP和刪除單元DeleteMAP來實現(xiàn)所述MAP文件的加載和刪除。加載單元AppendMAP用來將MAP文件加載到第一可執(zhí)行文件中;刪除單元DeleteMAP用來將所有MAP文件從第二可執(zhí)行文件中刪除。
加載單元AppendMAP將MAP文件嵌入第一可執(zhí)行文件中時,只要在程序編譯單元中設(shè)置加載單元,即,將加載單元AppendMAP加入編譯源程序的自動執(zhí)行腳本中,并在項目的工程中添加dtrace++.cpp文件,一起編譯。在源程序編譯完成之后,加載單元AppendMAP將編譯過程中產(chǎn)生的MAP文件加載于同期生成的第一可執(zhí)行文件中,從而形成第二可執(zhí)行文件。這樣就實現(xiàn)了加載MAP文件的過程。
加載單元AppendMAP所采用的格式是“AppendMAP xxx.exe xxx.MAPxxx.MAP”,可以將相同文件名(xxx)的MAP文件加入到相同文件名(xxx)的第一可執(zhí)行文件(第一EXE文件)中。加載單元AppendMAP可以將多個MAP文件加入到相同文件名(xxx)的第一可執(zhí)行文件,也可以將一個MAP文件加入到相同文件名(xxx)的第一可執(zhí)行文件中,不限于上述例子中所示的追加兩個MAP文件到相同文件名(xxx)的第一可執(zhí)行文件中。例如,一個可執(zhí)行文件本身對應(yīng)一個MAP文件,如果所述可執(zhí)行文件使用三個動態(tài)庫DLL,所述三個DLL也分別有自己的MAP文件,因此就要在第一可執(zhí)行的尾部追加4個MAP文件。
因此,利用加載單元AppendMAP可以依據(jù)文件格式規(guī)范將包含程序文件和代碼行數(shù)定位信息的MAP文件加入到第一可執(zhí)行文件中,并生成第二可執(zhí)行文件,從而能夠保證異常發(fā)生時第二可執(zhí)行文件中含有足夠的信息進(jìn)行異常代碼的定位。
刪除單元DeleteMAP從第二可執(zhí)行文件中刪除MAP文件時,只要將刪除單元DeleteMAP加入自動執(zhí)行腳本,并重新編譯,即將可執(zhí)行文件中的Map文件刪除。刪除單元DeleteMAP所采用的格式是“DeleteMAP xxx.exe”。采用上述格式可以將所有MAP文件從第二可執(zhí)行文件中刪除。
請一并參閱圖4和圖5。異常捕獲與異常定位的流程為第一步,啟動第二可執(zhí)行文件,執(zhí)行第二可執(zhí)行文件的操作。
第二步,加載異常捕獲與定位動態(tài)庫。第二可執(zhí)行文件啟動后,由dtrace++.cpp文件自動加載異常捕獲與定位動態(tài)庫。通常,異常捕獲與定位動態(tài)庫就是實際項目中通用的動態(tài)庫“dtrace++.dll”,加載時需要將所述異常捕獲與定位動態(tài)庫放到硬盤中特定的目錄下。
第三步,將異常捕獲鉤子掛接到第二可執(zhí)行文件。異常捕獲與定位動態(tài)庫在被加載時會自動添加一個異常捕獲鉤子到被跟蹤程序的異常捕獲鏈表的最上端,所述異常捕獲鏈表中含有處理程序異常的異常捕獲單元。所述異常捕獲鉤子指的是一個用來捕獲異常的單元(函數(shù))指針,通常調(diào)用Windows API“SetUnhandledExceptionFilter”單元來傳入上述異常捕獲鉤子(鉤子函數(shù)指針)。
第四步,當(dāng)被跟蹤第二可執(zhí)行程序發(fā)生異常,從異常捕獲鏈表中逐個尋找處理此異常的異常捕獲鉤子,如果沒有任何異常捕獲鉤子處理此異常,則該異常會傳到異常捕獲鏈表的最上端,由本發(fā)明提供的異常捕獲鉤子來處理。記錄當(dāng)時的函數(shù)調(diào)用棧信息,從異常捕獲鉤子的傳入?yún)?shù)中可以得到異常捕獲鉤子函數(shù)指針(異常捕獲鉤子函數(shù)的形式如下LONG WINAPIExceptionFilter(PEXCEPTION_POINTERS pException),EXCEPTION_POINTERS結(jié)構(gòu)在Windows中為struct{PEXCEPTION_RECORD ExceptionRecord;PCONTEXT ContextRecord})。然后逐個將函數(shù)調(diào)用的內(nèi)存地址取出來分析,如前所述,最上層的函數(shù)調(diào)用內(nèi)存地址就是發(fā)生異常的內(nèi)存地址。當(dāng)程序發(fā)生任何未處理的異常之后都會被異常捕獲與定位動態(tài)庫安置的異常捕獲鉤子所截獲。
生成系統(tǒng)核心數(shù)據(jù),所述系統(tǒng)核心數(shù)據(jù)包括異常發(fā)生時的寄存器數(shù)據(jù)、中斷信息、函數(shù)調(diào)用堆棧、異常發(fā)生的內(nèi)存地址、異常發(fā)生的模塊信息、異常發(fā)生的線程ID、其他線程的函數(shù)調(diào)用堆棧、程序數(shù)據(jù)段快照、程序虛擬地址空間各個模塊地址分配快照等信息。所述系統(tǒng)核心數(shù)據(jù)中含有的異常發(fā)生的內(nèi)存地址、模塊信息和函數(shù)調(diào)用堆棧信息,用于后期對程序發(fā)生異常的定位分析。
需要說明的是,程序本身會在設(shè)計階段考慮某些異常的處理,對于這些異常會在程序中采取相應(yīng)的措施予以處理。本發(fā)明提供的方法只捕獲程序設(shè)計階段不予處理的異常。
第五步,異常捕獲與定位動態(tài)庫中含有的自行編寫的代碼,用來完成異常代碼定位功能。當(dāng)程序發(fā)生異常時,異常捕獲與定位動態(tài)庫自動生成異常代碼定位文件,此時異常代碼定位文件的內(nèi)容為空,只生成文件名。
其中,所述異常代碼定位文件的命名規(guī)則是文件名與可執(zhí)行文件的名稱相同,只是擴(kuò)展名不同。異常代碼定位文件需要在可執(zhí)行文件的擴(kuò)展名的后部追加“l(fā)og”字符和異常發(fā)生的時間。所述時間采用年、月、日、時、分、秒的記錄形式。例如,可執(zhí)行文件為abc.exe,當(dāng)發(fā)生異常時,異常代碼定位文件的命名為abc.exelog20030213092312,表示的是本文件為可執(zhí)行文件abc.exe對應(yīng)的異常代碼定位文件,程序異常發(fā)生的時間為2003年02月13日09時23分12秒。
第六步,將包含有發(fā)生異常的內(nèi)存地址、模塊信息和函數(shù)調(diào)用堆棧信息等定位信息的系統(tǒng)核心數(shù)據(jù)寫入所述異常代碼定位文件。
第七步,首先,異常捕獲與定位動態(tài)庫以只讀的方式打開現(xiàn)有的第二可執(zhí)行文件,讀取第二可執(zhí)行文件尾部的第二可執(zhí)行文件長度;然后,定位到MAP文件區(qū)域的起始位置,即第一個分割符位置,讀取第一個MAP文件,直到遇到第二個分割符;然后,通過搜索分割符來分析每一個MAP文件起始和終止位置,并逐個讀取MAP文件并放入內(nèi)存中,直至將第二可執(zhí)行文件中的MAP文件全部讀入內(nèi)存。
所述MAP文件讀入內(nèi)存時,以圖6所示格式生成MAP文件的內(nèi)存映射。所述內(nèi)存映射為CMAPInfolst(MAP文件信息列表類)列表。所述CMAPInfolst列表包含多個CMAPInfo(MAP文件信息類),每一個CMAPInfo對應(yīng)一個加載在EXE文件中的MAP文件信息塊。每一個CMAPInfo都包含在所述CMAPInfolst列表中,換言之,CMAPInfolst列表中包含加載在EXE文件中所有的MAP文件信息塊。讀取MAP文件信息塊時,以分割符為界來分別獲取各MAP文件信息塊。對應(yīng)于MAP文件信息塊的每一個CMAPInfo存放有包括MAP文件模塊名、模塊代碼加載起始地址、模塊代碼加載終止地址、函數(shù)地址到函數(shù)名的映射表、代碼段偏移地址到代碼行、文件名的映射表。CFileNameAndLine(文件名和代碼行類)結(jié)構(gòu)中存放所述代碼行和文件名。每一個代碼行和文件名對應(yīng)一個CfileNameAndLine。每一個CMAPInfo包含多個CFileNameAndLine(文件名和代碼行類)。
第八步,根據(jù)系統(tǒng)核心數(shù)據(jù)和內(nèi)存中的MAP文件,確定發(fā)生異常的函數(shù)名以及代碼行號和文件名,生成異常代碼定位信息,并寫入異常代碼定位文件。
以下說明如何確定發(fā)生異常的函數(shù)名首先,判斷與函數(shù)調(diào)用的內(nèi)存地址對應(yīng)的MAP文件信息塊。通過遍歷CMAPInfolst中的CMAPInfo對應(yīng)的MAP文件信息塊,計算并判斷函數(shù)調(diào)用的內(nèi)存地址是否大于CMAPInfo.m_dLoadStartAddr(模塊代碼加載起始地址)而小于CMAPInfo.m_dLoadEndAddr(模塊代碼加載終止地址),如果是,則說明內(nèi)存地址在這個MAP文件信息塊中;否則內(nèi)存地址不在此MAP文件信息塊中,然后重復(fù)上述步驟繼續(xù)判斷函數(shù)調(diào)用的內(nèi)存地址是否在下一個CMAPInfo對應(yīng)的MAP文件信息塊中。如果在整個遍歷過程中始終找不到與函數(shù)調(diào)用的內(nèi)存地址對應(yīng)的MAP文件信息塊,則退出。
然后,通過CMAPInfo.m_decorateNameMAP(函數(shù)名對應(yīng)表)和函數(shù)調(diào)用的內(nèi)存地址計算出函數(shù)名稱。其中,CMAPInfo.m_decorateNameMAP的鍵值為函數(shù)的調(diào)用地址,CMAPInfo.m_decorateNameMAP存放函數(shù)名的哈希值,所述哈希值在MAP文件讀入內(nèi)存時生成的。
以下說明如何判斷發(fā)生異常的代碼行號和文件名首先,通過判斷函數(shù)的偏移地址是否大于模塊代碼加載起始地址而小于模塊代碼加載的終止地址而確定與函數(shù)調(diào)用的內(nèi)存地址對應(yīng)的MAP文件信息塊。
然后,用函數(shù)調(diào)用的內(nèi)存地址減去CMAPInfo.m_dLoadStartAddr,再減去固定的偏移地址0x1000,就得到了代碼行偏移地址。
最后,通過代碼行偏移地址與CMAPInfo.m_FileNameAndLineMAP對應(yīng)表計算出發(fā)生異常在哪一個CFileNameAndLine結(jié)構(gòu),其中包括代碼行號和文件名。
將包含文件名和代碼行號的信息寫入到異常代碼定位文件。
第九步,包含文件名和代碼行號的信息寫入到異常代碼定位文件后,異常捕獲與定位動態(tài)庫通過自行編寫的代碼關(guān)閉異常代碼定位文件。
第十步,根據(jù)異常捕獲與定位動態(tài)庫中自行設(shè)定的信息,來判斷第二可執(zhí)行文件是退出還是繼續(xù)執(zhí)行。所述判斷結(jié)果將根據(jù)第二可執(zhí)行文件和發(fā)生異常的不同而不同。
需要指出的是,在CMAPInfolst中比較發(fā)生異常的地址和每一個CMAPInfo中代碼加載的起始、終止地址。所述起始、終止地址就是記錄在MAP文件中的各個模塊在內(nèi)存中的起始和終止地址,每一個模塊的起始、終止地址都不相同也不能重疊。
通常,可以在程序啟動時由操作系統(tǒng)自動分配足夠大小的內(nèi)存空間以供各個模塊使用,而無需在編譯階段指定所述模塊的起始、終止地址。但是如果由操作系統(tǒng)自動指定起始地址,那么將難以保證實際程序運(yùn)行的地址和MAP文件中的地址一致,從而無法對程序異常進(jìn)行定位分析。而且,在編譯期間指定起始地址可以使得程序加載速度變快。因此,在本發(fā)明中,必須要求各個模塊的加載起始地址在編譯階段配置好,并根據(jù)模塊大小計算出終止地址。這樣才能保證生成的MAP文件(MAP文件在編譯階段生成)中的模塊地址和實際程序運(yùn)行中的模塊地址是一致的,從而可以進(jìn)行異常代碼定位的分析。因此,本發(fā)明提供的方法采用在程序編譯期間指定起始地址的地址分配方式。
下面舉例說明,當(dāng)程序出現(xiàn)異常時采用本發(fā)明提供的方法如何實現(xiàn)程序異常的自動定位。
第二可執(zhí)行文件啟動后加載異常定位動態(tài)庫dtrace++.dll,然后dtrace++.dll調(diào)用SetUnhandledExceptionFilter函數(shù)來掛結(jié)ExceptionFilter異常捕獲鉤子函數(shù)。
當(dāng)發(fā)生異常時,調(diào)用ExceptionFilter異常捕獲鉤子函數(shù)以傳入EXCEPTION_POINTERS結(jié)構(gòu),函數(shù)中首先生成異常代碼定位文件,此時只生成該文件的文件名,文件內(nèi)容為空。
然后通過EXCEPTION_POINTERS結(jié)構(gòu)分析出系統(tǒng)核心數(shù)據(jù),即發(fā)生異常的模塊名、線程ID、在模塊中的偏移地址、異常ID、異常標(biāo)志,記錄到文件中(如圖62003-02-13 09:23:12Caused an exception in thread
at00601099 000100000099 H\Log\MAPEXE\Debug\MAPDLL.dll[Exception Code0xC0000005][Exception Flags0x00000000])。
之后,以只讀的方式打開EXE文件,首先讀取EXE文件末尾的原始EXE文件長度,然后定位到MAP文件區(qū)域的起始位置,然后通過搜索圖中分割符來分析每一個MAP文件起始和終止位置,逐個取出MAP文件并放入CMAPInfo結(jié)構(gòu)中,每一個CManInfo中包含多個代碼信息塊CFileNameAndLine,最終構(gòu)成一個CMAPInfolst列表。
再后,逐個分析發(fā)生異常時EXCEPTION_POINTERS結(jié)構(gòu)中函數(shù)調(diào)用棧中每一個函數(shù)的代碼行信息,包括模塊名、異常在內(nèi)存中的地址,在模塊中的偏移地址、發(fā)生異常的函數(shù)名、發(fā)生異常的文件名和發(fā)生異常的代碼行號,棧頂?shù)暮瘮?shù)就是發(fā)生異常的函數(shù),發(fā)生異常的內(nèi)存地址就在這個函數(shù)中。
分析代碼行信息的具體算法如下首先判斷函數(shù)調(diào)用的內(nèi)存地址落在哪一個MAP文件信息塊中,通過遍歷CMAPInfolst中的CMAPInfo,計算函數(shù)調(diào)用的內(nèi)存地址是否大于CMAPInfo.m_dLoadStartAddr而小于CMAPInfo.m_dLoadEndAddr。如果是,就說明落在這個MAP文件信息塊中;然后通過CMAPInfo.m_decorateNameMAP對應(yīng)表和函數(shù)調(diào)用的內(nèi)存地址計算出函數(shù)名稱;然后用函數(shù)調(diào)用的內(nèi)存地址減去CMAPInfo.m_dLoadStartAddr再減去0x1000,就得到了代碼行偏移地址。然后通過CMAPInfo.m_FileNameAndLineMAP對應(yīng)表和代碼行偏移地址計算出是哪一個CFileNameAndLine結(jié)構(gòu),其中包括代碼行號和文件名。這樣,就將發(fā)生異常的文件名和代碼行號找到。
之后,將上述文件名和代碼行號等信息添加到異常代碼定位文件中,并關(guān)閉所述異常代碼定位文件。
最后,判斷第二可執(zhí)行文件是退出還是繼續(xù)執(zhí)行。
請參閱圖6,是由WinCordDump工具根據(jù)以上方法生成的異常代碼定位文件。
所述WinCoreDump工具包括文件格式規(guī)范中加載單元AppendMAP和刪除單元DeleteMAP,以及異常捕獲與定位動態(tài)庫中異常捕獲鉤子和異常代碼定位單元。
所述異常代碼定位文件包括發(fā)生異常時間、發(fā)生異常的線程ID、發(fā)生異常的內(nèi)存地址、發(fā)生異常的模塊名稱及所在模塊中的偏移地址、異常ID、異常標(biāo)志(用以標(biāo)志所述異常是否可以讓程序繼續(xù)執(zhí)行,如果收到一個不可繼續(xù)執(zhí)行(noncontinuable)的異常后還要繼續(xù)執(zhí)行文件,會產(chǎn)生一個異常終止程序)、發(fā)生異常時的函數(shù)調(diào)用棧信息、函數(shù)從屬的文件名、函數(shù)從屬的執(zhí)行體和代碼行號。
圖6中第一行“2003-02-13 09:23:12Caused an exception in thread
at00601099 000100000099H\Log\MAPEXE\Debug\MAPDLL.dll”固定是發(fā)生異常的時間、線程ID、發(fā)生異常的內(nèi)存地址、在模塊中的偏移地址和模塊名稱;第二行“[Exception Code0xC0000005]”是異常ID;第三行“[Exception Flags0x00000000]”是異常標(biāo)志;從第四行起是發(fā)生異常時線程的函數(shù)調(diào)用棧信息(所述函數(shù)調(diào)用棧信息的行數(shù)多少視具體調(diào)用情況而定調(diào)用的函數(shù)多,則函數(shù)調(diào)用棧中的行數(shù)較多;調(diào)用的函數(shù)少,則函數(shù)調(diào)用棧中的行述較少)。比如圖6中的信息如下第四行用于說明從第5行起是線程ID 00000CEC的函數(shù)調(diào)用棧信息,第5行之后的[1]和[4]分別是棧頂和棧底,可以看出,系統(tǒng)首先調(diào)用的是kernel32.dll的某個函數(shù)(由于kernel32.dll不是自行開發(fā)的,沒有相應(yīng)的MAP文件,因而只能夠知道內(nèi)存地址和偏移量,卻無法確切地定位到相應(yīng)的代碼行),然后調(diào)用的是MAPEXE1.EXE的某個函數(shù),然后又是MAPEXE1.EXE的另一個函數(shù),最后調(diào)用的是MAPDLL.dll,由于所述MAPDLL.dll是自行開發(fā)的,有對應(yīng)的MAP文件,并且MAP文件已通過AppendMAP命令追加到了MAPEXE1.EXE中,因此可以確切地將異常代碼信息定位到代碼行,即圖6中第5行中所示的信息異常發(fā)生在MAPDLL.cpp文件的38行,位于MAPDLLHappyFunc函數(shù)中。由于MAPDLLHappyFunc函數(shù)在棧頂,那么說明異常是發(fā)生在該MAPDLLHappyFunc函數(shù)中以及MAPDLL.cpp文件的第38行。圖6所示的異常代碼定位文件就清楚表明了發(fā)生異常的函數(shù)名、文件名以及在所述文件中的具體代碼行號。
以上所述僅是本發(fā)明的優(yōu)選實施方式,應(yīng)當(dāng)指出,對于本技術(shù)領(lǐng)域的普通技術(shù)人員來說,在不脫離本發(fā)明原理的前提下,還可以作出若干改進(jìn)和潤飾,這些改進(jìn)和潤飾也應(yīng)視為本發(fā)明的保護(hù)范圍。
權(quán)利要求
1.一種定位程序異常的方法,其特征在于包括步驟1)生成第一可執(zhí)行文件以及代碼信息文件;2)在所述第一可執(zhí)行文件中加載所述代碼信息文件,生成含有代碼信息文件信息塊的第二可執(zhí)行文件;3)啟動所述第二可執(zhí)行文件,加載異常捕獲與定位動態(tài)庫;4)當(dāng)?shù)诙蓤?zhí)行文件發(fā)生異常時,產(chǎn)生系統(tǒng)核心數(shù)據(jù),所述系統(tǒng)核心數(shù)據(jù)包含函數(shù)調(diào)用的內(nèi)存地址;5)從所述第二可執(zhí)行文件讀取代碼信息文件,比較前述系統(tǒng)核心數(shù)據(jù)和代碼信息文件,確定程序異常的位置。
2.根據(jù)權(quán)利要求1所述的定位程序異常的方法,其特征在于,所述第二可執(zhí)行文件包括第一可執(zhí)行文件、代碼信息文件、分割符和第一可執(zhí)行文件長度數(shù)據(jù);所述分割符位于所述第一可執(zhí)行文件和所述代碼信息文件之間、相鄰的兩個代碼信息文件之間,以及代碼信息文件和第一可執(zhí)行文件長度之間,用于分割第一可執(zhí)行文件、各個代碼信息文件以及第一可執(zhí)行文件長度。
3.根據(jù)權(quán)利要求1所述的定位程序異常的方法,其特征在于,所述步驟1)具體包括在程序編譯單元中設(shè)置加載單元,并通過程序編譯單元生成第一可執(zhí)行文件以及代碼信息文件;在所述步驟2)中,通過加載單元將代碼信息文件添加至第一可執(zhí)行文件。
4.根據(jù)權(quán)利要求1所述的定位程序異常的方法,其特征在于,所述步驟3)中加載異常捕獲與定位動態(tài)庫的過程包括將異常捕獲鉤子添加到第二可執(zhí)行文件中,用于捕獲異常;將異常代碼定位單元嵌入第二可執(zhí)行文件中,用于生成異常代碼定位文件。
5.根據(jù)權(quán)利要求4所述的定位程序異常的方法,其特征在于,所述步驟3)中異常捕獲鉤子添加到第二可執(zhí)行文件中的異常捕獲鏈表中。
6.根據(jù)權(quán)利要求1至5中任一項所述的定位程序異常的方法,其特征在于,所述步驟5)具體包括步驟41)生成異常代碼定位文件的文件名;42)將系統(tǒng)核心數(shù)據(jù)寫入異常代碼定位文件;43)讀取第二可執(zhí)行文件中的代碼信息文件信息塊;44)比較系統(tǒng)核心數(shù)據(jù)和代碼信息文件,確定程序異常對應(yīng)的文件名、函數(shù)名和代碼行。
7.根據(jù)權(quán)利要求6所述的定位程序異常的方法,其特征在于,所述步驟43)具體包括從第二可執(zhí)行文件中分離出各個代碼信息文件,并依據(jù)內(nèi)存映射格式將代碼信息文件讀入內(nèi)存。
8.根據(jù)權(quán)利要求7所述的定位程序異常的方法,其特征在于,所述代碼信息文件是MAP文件。
9.根據(jù)權(quán)利要求8所述的定位程序異常的方法,其特征在于,所述MAP文件在內(nèi)存中以MAP文件信息列表類格式存放。
10.根據(jù)權(quán)利要求9所述的定位程序異常的方法,其特征在于,所述MAP文件信息列表類對應(yīng)MAP文件信息類以及文件名和代碼行類;所述文件名和代碼行類存放文件名和代碼行;所述MAP文件信息類存放MAP文件模塊名、模塊代碼加載起始地址、模塊代碼加載終止地址、文件名和代碼行對應(yīng)表以及函數(shù)名對應(yīng)表;所述文件名和代碼行對應(yīng)表存放讀取MAP文件時生成的文件名和代碼行的哈希值;所述函數(shù)名對應(yīng)表存放讀取MAP文件時生成的函數(shù)名的哈希值,鍵值為函數(shù)地址。
11.根據(jù)權(quán)利要求10所述的定位程序異常的方法,其特征在于,所述步驟44)中進(jìn)一步包括通過比較函數(shù)調(diào)用的內(nèi)存地址與所述模塊代碼加載起始地址和模塊代碼加載終止地址,來分析、查找對應(yīng)于函數(shù)調(diào)用的內(nèi)存地址的MAP文件信息塊;通過函數(shù)調(diào)用的內(nèi)存地址和函數(shù)名對應(yīng)表確定函數(shù)名。
12.根據(jù)權(quán)利要求9所述的定位程序異常的方法,其特征在于,所述步驟44)進(jìn)一步包括通過比較函數(shù)調(diào)用的內(nèi)存地址與所述模塊代碼加載起始地址和模塊代碼加載終止地址,來分析、查找對應(yīng)于函數(shù)調(diào)用的內(nèi)存地址的MAP文件信息塊;將函數(shù)調(diào)用的內(nèi)存地址減去模塊代碼加載起始地址以及偏移地址,得到代碼行偏移地址;通過代碼行偏移地址與文件名和代碼行對應(yīng)表查找發(fā)生異常的文件名和代碼行類結(jié)構(gòu),依據(jù)所述文件名和代碼行類結(jié)構(gòu)確定發(fā)生異常的代碼行號和文件名。
全文摘要
本發(fā)明公開了一種定位程序異常的方法,包括下述步驟1)生成第一可執(zhí)行文件以及代碼信息文件;2)在所述第一可執(zhí)行文件中加載所述代碼信息文件,生成含有代碼信息文件信息塊的第二可執(zhí)行文件;3)啟動所述第二可執(zhí)行文件,加載異常捕獲與定位動態(tài)庫;4)當(dāng)?shù)诙蓤?zhí)行文件發(fā)生異常時,產(chǎn)生系統(tǒng)核心數(shù)據(jù),所述系統(tǒng)核心數(shù)據(jù)包含函數(shù)調(diào)用的內(nèi)存地址;5)從所述第二可執(zhí)行文件讀取代碼信息文件,比較前述系統(tǒng)核心數(shù)據(jù)和代碼信息文件,確定程序異常的位置。應(yīng)用本發(fā)明提供的定位程序異常的方法可以高效、便捷、準(zhǔn)確地對發(fā)生異常的程序進(jìn)行自動定位,并提供充足信息供程序維護(hù)人員查找異常原因。
文檔編號G06F11/36GK1704908SQ20041004282
公開日2005年12月7日 申請日期2004年5月26日 優(yōu)先權(quán)日2004年5月26日
發(fā)明者陳遠(yuǎn)翔 申請人:華為技術(shù)有限公司