天天看點

如何診斷Windows CE的應用程式崩潰

無論你是一個單純的電腦使用者還是一名進階軟體工程師,都一定對程式崩潰不陌生。做為一名Windows CE應用程式開發者,你也一定遇到過下圖這種場景:

如何診斷Windows CE的應用程式崩潰

這個對話框告訴你,有一個叫installer.exe的程式在位址00019320處崩潰了。如果這個程式歸你負責,那麼你的問題就來了:怎麼找出這個BUG?這篇文章我想談談我在這方面的一些經驗。

Windows CE的崩潰界面給出的資訊十分的少,其中最有用的無疑是崩潰位址,如果你能從崩潰位址定位到源代碼去,這個問題可以說就解決了一半。

從位址定位到源代碼的方法有幾種。一種是利用MAP檔案:你可以在BUILD程式時生成MAP檔案。MAP檔案是一個文本檔案,其中主要記錄了各個函數入口對應的位址資訊。比如這個例子中,崩潰位址對應的入口是:

0001:000082f4 [email protected]@@@ATL@@[email protected] 000192f4 f i ImageViewer.obj      

MAP檔案的好處是它是文本檔案,可以人工閱讀,缺點是資訊不夠多,隻能定位到函數級别,而且要看懂MAP檔案你需要有足夠的經驗,比如其中那串長長的貌似亂碼的字元串是C++函數經過name mangling處理後的結果,沒有一定的經驗你根本沒法還原出實際的函數。

另一種方法是利用PDB檔案,PDB檔案收集了應用程式的調試符号。PDB檔案提供的資訊很全,不過你得需要一定的工具才能解讀它。如果你是一個經驗豐富的Windows桌面平台的應用程式開發者,你可能聽說過MSJ(Microsoft System Journal)這本雜志。如果你曾經看過這本雜志,應該對Bugslayer不陌生。在 這篇文章中,Bugslayer介紹了他做的一個工具:CrashFinder。CrashFinder可以從崩潰位址通過查詢相應的PDB檔案直接定位到導緻崩潰的源代碼行。幸運的是,由于Windows CE可執行程式及其PDB檔案的格式和Windows桌面系統上的是一樣的,是以CrashFinder也可以用來定位Windows CE程式的崩潰位址。下面是CrashFinder顯示的結果:

如何診斷Windows CE的應用程式崩潰

CrashFinder提供的資訊十分有用,但是不夠直覺。為此我在Remote Process Explorer提供了一個更友善的界面,它可以直接顯示源代碼,并把導緻崩潰的那一行highlight出來:

如何診斷Windows CE的應用程式崩潰

PDB檔案裡包含着大量的調試資訊可以幫助你診斷應用程式錯誤,是以一般來說即使是正式release的版本,你也應該生成并維護好這些PDB檔案。使用PDB檔案的關鍵是崩潰的應用程式和PDB檔案一定要比對,否則它不但不能幫到你,反而會誤導你。下圖是VS2005中設定生成PDB檔案和MAP檔案的地方:

如何診斷Windows CE的應用程式崩潰

前面說過,Windows CE的崩潰界面給出的資訊十分少,很多時候我們還需要更多的資訊才能定位問題,另外有些Windows CE裝置可能根本沒有顯示器。為解決這一問題,Windows CE在應用程式崩潰時還同時往外(一般是序列槽)輸出相關的崩潰資訊。比如這個例子中,如果你正好接着調試序列槽,開着HyperTerminal,那麼在程式崩潰的時候你會看到這樣的資訊:

Data Abort: Thread=8d661000 Proc=81a477c0 'installer.exe'
AKY=00000401 PC=00019320(installer.exe+0x00009320) RA=00019094(installer.exe+0x0
0009094) BVA=16080100 FSR=00000007      

我相信Windows CE開發者一定也對這幾行資訊很熟悉。怎麼利用這些資訊診斷程式的問題?這裡面最關鍵的資訊是PC和RA給出的位址資訊。PC就是上面提到的崩潰位址,根據這個位址用CrashFinder或者我的Remote Process Explorer裡的Crack Address界面可以定位到導緻崩潰的源代碼行;RA是PC的傳回位址(Return Address),根據這個位址可以找到導緻崩潰的上一級函數,這個資訊也很重要,因為很多時候崩潰的原因往往是上層函數往底層函數傳遞了非法參數導緻的,比如你的應用程式用一個非法的視窗類調用MFC函數,崩潰位址在MFC函數裡面,但是出問題的地方在你的調用代碼裡。下圖是RA位址對應的源代碼:

如何診斷Windows CE的應用程式崩潰

除了PC和RA,其他資訊也可以提供一些參考作用:BVA是ARM中的Fault Address Register(FAR),是引起Data Abort的虛拟位址,比如說你的程式試圖通路一個非法位址裡的内容,那麼Data Abort時BVA就是這個非法位址;FSR是Fault Status Register,指明導緻異常的原因,FSR的解釋可以看 這裡。要注意的是Thread和Proc給出的不是Thread Id/Thread Handle或者Process Id/Process Handle,它們給出的是該Thread或Proc對應的核心對象的指針,類似于Window NT平台的TEB和PEB的概念。由于你看到崩潰資訊時線程已經退出了,是以根據這個資訊在事後你無法知道是哪個線程出的錯。以後我将介紹一種系統級的logging機制,這種機制可以把每條log的Thread Id、TEB等資訊同時記錄下來,這樣在崩潰時就可以根據Data Abort的TEB資訊和先前log中出現的TEB,找到出錯的線程。這樣,你不但可以定位到錯誤的源代碼,還能找到運作錯誤代碼的線程,将大大提高解決問題的效率。