TIPS-如何偵錯被try catch包住的例外

同事詢問,有一段程式碼類似以下結構,用try...catch包住函數呼叫,當函數出錯,程式優雅地顯示錯誤訊息,卻漏失了錯在哪一列程式碼等細節,造成偵錯困難。

protected void Page_Load(object sender, EventArgs e)
{
    try
    {
        raisException();
    }
    catch (Exception ex)
    {
        Response.Write("Error:" + ex.Message);
        Response.End();
    }
}
private void raisException()
{
    throw new ApplicationException("Something Wrong!");
}

有幾種方法可以解決這個問題:

    1. 將try ... catch先拆掉。(但測完又要改回去,有點笨)
    2. Response.Write(ex.StackTrace)取得錯誤所在程式列數資訊。(無法中斷在錯誤列上,進行檢查變數值等進一步偵察)
    3. 攔截First Chance Exception,讓偵錯中斷點可以停在throw new Application那一列上。(看來是較好的做法)

對Debugger來說,當程式有例外發生,會接到兩次通知。第一次通知為First Chance Exception,接著如果出錯的程式有被包在try catch中,就會由指定的例外處理(Exception Handler)接手;若程式碼沒有try catch或發生的例外不在Exception Handler要catch的對象清單中,就會再觸發Second Chance Exception,這就是第二次通知。

我們通常將try catch裡發生的例外視為可容忍、預料中的狀況,可能在程式執行中會發生多次(但這會嚴重傷害效能,還是要儘可能降低進行例外處理的頻率,例如: 事先檢查參數是否合理,不合理就不要繼續,以免執行出錯觸發catch裡的邏輯,對效能很傷)。基於這樣的假設,Debugger預設會忽略First Chance Exception的通知,只有Code處理不了時,Debugger才認定程式出錯,進入偵錯模式,讓開發者可以進行Line-By-Line偵錯。

回到上述的例子,由於raiseException()被包在try catch中,故出錯時只會有First Chance Exception通知,接著被catch裡的程式接手處理,並不會觸發偵錯中斷。因此,我們只需小做調整,就可以要求Debugger在First Chance Exception(throw new Application那一列)時就把控制權交給我們。方法是按Ctrl-Alt-E叫出Exceptions設定對話框,勾選CLR Throw選項,就大功告成囉!

開啟後,你可能會發現不少原本被包在try catch裡的Exception都會揭竿而起,讓你的偵錯過程一再被打斷,因此也可以選擇針對某些Exception才抓First Chance(如下圖)。但是,若程式中真有這麼多First Chance Exception,也可視為一種警訊,應該檢討一下是否太依賴try catch,或把Exception Handler當成正常邏輯使用,這不是良好的設計。

[Update: 針對已知可疑位置的問題,可以使用System.Diagnostics.Break()強制進偵錯模式(跟Javascript裡寫debugger;有異曲同工之妙),感謝網友ChrisTorng補充!]

歡迎推文分享:
Published 14 December 2008 08:49 PM 由 Jeffrey
Filed under: , ,
Views: 12,888



意見

# ChrisTorng said on 14 December, 2008 09:21 PM

我都用 ex.ToString() 會有完整的錯誤訊息加執行堆疊。另外也可以在 catch 中加上 System.Diagnostics.Break(); 就會自動中斷進入偵錯模式。

但以良好的程式觀點來說,不應該 catch 最通用的 Exception,而應該針對特定的 Exception 類別各別寫訊息輸出,也不應該把實際的錯誤訊息傳給使用者。

可以考慮用 #if DEBUG 來區別偵錯版與正式版的錯誤處理。

另外也有看過文章說可以把 MDAs (Managed Debugging Assistants) 給打勾,它可以幫忙抓到一些隱藏的小問題。msdn.microsoft.com/.../d21c150d.aspx

# 邪惡油頭 said on 16 December, 2008 09:29 AM

看來Java跟.NET還是有些差異啊! 基本上可捕捉的exception問題應該要能在錯誤訊息的堆疊當中看出來才是.不能捕捉的自然就會是error而不是Exception啦!

而且try catch的確是保障你在runtime時期的系統穩定的重要機制嗎?

# 菜逼巴工程師 said on 19 January, 2018 05:11 AM

想請問一下,如果我使用三層式架構,在Data層中把例外拋出去了,那我前端網頁有「必要」再用try catch接例外嗎?

# Jeffrey said on 19 January, 2018 07:20 PM

to 菜逼巴工程師, 依一般設計準則,網頁應該要捕捉例外,將例外細節寫入Log,然後傳回「抱歉系統無法處理您的需求,請洽客服」之類較友善的訊息,避免直接噴出IIS或ASP.NET原始錯誤訊息(Yellow Screen of Death,YSOD)。但這樣有一個問題是,客戶報案時除了時間點沒有其他線索,針對這個我有一個改良做法,將例外細節寫入Log時包含錯誤訊息、Callstack(出錯的程式碼位置)、URL參數、Cookie、使用者身分、IP等資料(愈能還原現場愈好),並為每次錯誤取流水號,在顯示友善錯誤訊息時附上錯誤流水號,接獲使用者回報時方便由流水號調閱事故記錄展開調查。

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<December 2008>
SunMonTueWedThuFriSat
30123456
78910111213
14151617181920
21222324252627
28293031123
45678910
 
RSS
創用 CC 授權條款
【廣告】
twMVC

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication