C# Release和debug模式下調試跟蹤程序的原則和方法

取相對路徑:AppDomain.CurrentDomain.BaseDirectory

入門簡介
VS.Net 提供了兩種機制來幫助開發人員診斷和糾正程序中的錯誤.一個是debug類, 另一個是trace類.這兩個類都有了一個assert函數. Assert在很多情況下可以用來檢驗變量,比如檢驗一個指針在調用了某一個系統的API之後是不是爲空。即使是使用try-catch塊,我們也要面對下面兩種情況,其一是我們要抓取系統的API拋出的異常,但是我們無法簡單的得到API失敗時的返回值,我們只好來處理“異常”,,這會使編程變得複雜.其二,我們往往要生成兩個版本-測試版和發佈版,這也使開發變得複雜。

如果你是一個從來沒有使用過assert和trace的程序員,你大可以讀到這兒爲止了,因爲下面我要講的東西可能沒有一點會減輕你編程工作的強度,也不會減少開發費用,也不能給你正在測試的程序一些好的建議.
這是我在實際開發中親身經歷的一個例子
 
好幾年以前,我還是一個多線路視頻監視系統的開發組長,我們的視頻系統可以通過RS-232線接收多達64個攝像機.這些攝像機直接或者通過專線或者通過撥號連接到視頻系統上來.這是一個複雜的系統,我在這兒就不多說了。 
 
實現跟蹤的手段之一: 跟蹤日誌(Trace logs)
在每一個項目的開始,我們通常要記錄下面的事情:
1. 所有與GUI有關的事件: 選擇菜單,選擇按鈕等等.
2. 所有內部的消息(我們使用消息體系來調用沒有個功能的實現)
3. 所有狀態的變化.




對於上面的實現方法,我們輸出一個調試日誌來報告與消息相關的事件,消息和當前的狀態. 
  
 
實現跟蹤的手段之二: 斷言(assert) 
 
我們也纔有用assert來檢驗所有函數的參數和他們的返回值.當觸發一個assert的時候,我們會把與這個相關的信息寫的調試日誌中.這時候,就會關閉調試日誌,顯示一個錯誤信息,並且中止程序的運行.
 
 
不配合的質量評估人員
 
在開發過程中,質量評估部門會測試不同的模塊.最初,我們與質量評估部門的關係是敵對的,我們使用了先前的調試手段,而我們卻發現, 質量評估部門正好測試到我們不讓他們那麼實現的情況(他們只想引起管理層的注意).當我們糾正了各種不同的觀念之後, 質量評估部門的工作開始作的非常好.有一個問題,對於任何測試人員,當你問他具體作了些什麼,他們會非常安心的對你說,我點擊了X,然後是Y,接下去是Z. 而事實上,他們點擊的是A,B,C. 有了我們上面對於GUI的實現手段,我們沒有必要去問測試人員他們作了些什麼,我們自己完全可以從日誌文件中看的出來.
 
 
實地測試(Field Testing)
 
因爲內部測試與實際使用有很大的出入,我們會選擇一些用戶,提供一個beta版本給他們測試,這些版本中我們打開了我們前面所講的調試手段.這可以顯示一些問題,多數是因爲硬件的不同,而這個我們在內部測試中並沒有發現.
 
 
迴歸測試(Regression Testing)
 
最後,用於我們前面說的調試手段已經做到GUI中,所有我們可以記錄和回放我們的測試教本.所以我們可以實現迴歸測試,這個測試是用來測試我們的新代碼對以前的東西有沒有影響.
 
 
開發測試(Development Testing) [Page]
 
不必多說,上面的調試手段對我們自己的開發測試也是非常有用的(特別是有了版本控制以後,它可以允許我們退回到舊版本上,測試相同的事件序列.)
 
 
提前發佈產品
 
管理層因爲上一個項目的原因非常惱火,因爲他們要應付客戶的發火,所以這次分配了三個月的事件用來測試(以前從來沒有聽說這麼長的時間,我們的編碼僅用了六個月,而測試在編碼過程中就已經開始了!),由於我們的調試手段,質量評估部門僅用一個月的時間就沒有辦法再發現新的問題了,所以我們的產品提前發佈. 
 
發佈版本 VS 測試版本 
 
由於我們得調試手段非常深入,出於性能的原因,我們在發佈版本中關閉了跟蹤的語句,但是我們在發佈版本中保留了assert. 這是因爲在實地工作中,我們的程序會連續不斷的運行,會使日誌文件非常大.此外,assert語句會報告一個非常有價值的消息,爲什麼會處罰這個assert,因爲在質量評估部門必然會有東西會出錯,特別是由於世界各地不同的硬件產品.

我認爲這是一個非常好的體系,發佈一個不帶調試手段的版本給我們的客戶. 如果客戶那兒持續有問題,並且想和你一起來解決這個問題,你可以給他一個帶有全部調試手段的版本. 除此之外,使用正確的assert控制,你可以顯示一個非常漂亮的出錯信息給你的客戶.我比較喜歡IE6的出錯信息,就象這樣”對此使用引起的不方便表示道歉,我們發現一個問題等等”. 你可以用這個機會自動重新啓動你的程序.從一個用戶的觀點來看,這好像是被髮生事故是被安全氣囊劃傷了一下子,卻不是直接撞在擋風玻璃上,這就是進步.如果客戶可以接入互聯網,那麼你的程序可以把錯誤信息發送給你,這就是爲什麼麼在發佈版本中保留assert的原因,你可以得到除了錯誤發生地址,棧的信息,寄存器和內存映象以外更多的信息.
 
 
調試(Debug)和跟蹤(Trace)
 
在C#中, debug 和 trace 類都提供了這些功能,但是它們都可以被關閉.缺省情況下,在調試(Debug)模式下它們是可用的.在發佈(release)模式下,dubg的功能是關閉的, trace仍然可以使用.它們兩個都支持 assert 和 trace. 通常的使用方法這樣. Debug.Assert 來使用assert功能, Trace.Write來使用trace功能.

如果你研究了我前面說得內容,就會覺得這樣作不對. 出於對程序性能和生存週期的考慮,純粹的trace應該在發佈版本中拿去, 而Debug.Assert應該保留.

因此, 程序中應該用Debug.Write來保存跟蹤文件, Trace.Assert來實現assert. 當在生成發佈版時, Debug.Write就會失去作用,而assert仍然保留.
 
 
在Try-Catch 塊中處理異常
 
通常開發人員對於異常只有一個願望:它能不能看起來更溫和友好一些。不如,是不是可以讓用戶不必停止程序就可以結束對一個文件的度操作?如果答案是是的話,那麼我們的代碼就是正確的,如果答案是否,我們就要產生一個斷言(assert).我們收集類似像這種情況的信息,他們在處理異常是就會成爲非常有用的東西,我們就可以在類似“程序運行失敗“消息中,顯示更多的信息給用戶。

Behind The Scenes
我們用assert的WriteLine來測試一下在編譯過的中間代碼中,測試版和發佈版中的debug類實現起來有什麼不同

使用如下的C#代碼
Debug.WriteLine(/"Debug.WriteLine/");
System.Diagnostics.Trace.WriteLine(/"Test.WriteLine/");
                          
Debug.Assert(false, /"Debug.Assert/");
System.Diagnostics.Trace.Assert(true, /"Trace.Assert/");
 
在測試模式下,編譯器產生如下的代碼
                          Debug.WriteLine(/"Debug.WriteLine/");
0000000f mov         ecx,dword ptr ds:[01BA03CCh] 
00000015 call        dword ptr ds:[02EF870Ch] 
                          System.Diagnostics.Trace.WriteLine(/"Test.WriteLine/");
0000001b mov         ecx,dword ptr ds:[01BA03D0h] 
00000021 call        dword ptr ds:[02EF89F4h] 
                          Debug.Assert(false, /"Debug.Assert/");
00000027 mov         edx,dword ptr ds:[01BA03D4h] 
0000002d xor         ecx,ecx 
0000002f call        dword ptr ds:[02EF86ECh] 
                          System.Diagnostics.Trace.Assert(true, /"Trace.Assert/");
00000035 mov         edx,dword ptr ds:[01BA03D8h] 
0000003b mov         ecx,1
00000040 call        dword ptr ds:[02EF89D4h] 
在發佈模式下,編譯器產生如下代碼
0000000f mov         ecx,dword ptr ds:[01BA03CCh]  [Page]
00000015 call        dword ptr ds:[02EF870Ch] 
0000001b mov         edx,dword ptr ds:[01BA03D0h] 
00000021 mov         ecx,1
00000026 call        dword ptr ds:[02EF86ECh] 
 
你可以注意到如下有趣的幾點:
1. Deubg語句被完全拿掉了。
2. Trace.WriteLine實際上是調用了Debug.WriteLine.
3. Trace.Asser是調用了Debug.Assert
 
總結:
所有的debug函數都被拿掉了,所有的Trace函數都轉化爲調用debug的函數。這是因爲c#中條件屬性控制的原因,我們會用一個相同的機制來建立自己的一個更聰明的debug類。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章