Debug,中文時常譯成「除錯」,這是程式員們絕對都相當熟悉的活動。根據估計,除錯所花費的時間,往往占去撰寫程式的一至兩倍時間,由此可見,身為程式員的我們,某種程度上來說,更是不折不扣的除錯員。
不過,雖然除錯占了開發生活中很大的一個比例,但是談論如何撰寫程式碼的文章或書籍,其數量及份量恐怕都遠勝於談論除錯相關的主題。事實上,好的除錯技巧絕對有助於提升開發速度。
試想,倘若除錯技巧不佳,一個臭蟲就足以耗掉3天的時間。換作懂得訣竅的人,也許1小時內就能解決了。所以,好的除錯技巧帶來的生產力改善比是十分可觀的。
除錯也有人譯為「偵錯」,從字面上來看,「debug」的字首「de」代表除去的意思,所以譯為「除錯」是比較符合原文的意思。
但是,無可諱言的,在除錯的工作中,首要之務必須先偵測出錯誤的原因,之後才能加以解決。倘若知道原因,問題多半容易解決,因此,除錯工作的核心,其實還是必須回歸到「偵錯」。
程式不對勁、出了問題,多半不是程式員自己發現的。在程式員的機器上,臭蟲知道應該要「收歛行止」,所以程式員在測試人員回報錯誤時,最常回答的一句話就是:「在我的機器上明明會動啊!(It works on my machine!)」從這件事,我們就可以明白,程式員往往是最後一個發現自己的程式有臭蟲的人。
除錯的第一步:試著重現(Reproduce)問題
在問題回報時,多半會伴隨著如何產生(Produce)這個問題的操作情境(Scenario)。而程式員除錯的第一個步驟,便是試著依照問題提出者所寫下的操作情境,試著重現(Reproduce)這個問題。
此時很可能遇到兩種情況,第一種是問題提出者所言屬實,沒錯,事實就擺在眼前,再怎麼搪塞都得面對。而第二種情況就是除錯界第一名言:「It works on my machine!」,程式員認為程式在自己的電腦上沒有錯誤,但測試者就是可以測出問題。
許多因素可能造成「It works on my machine!」的情況。例如環境的不同(作業系統版本、語系、瀏覽器版本、網路設定、額外加裝的軟體等),或者程式員對回報者寫下的操作情境有認知差異,就會演變成羅生門的局面,使得程式員及問題回報者各執一詞。
比較健康的做法,還是得回到問題回報者的環境,試著在出現問題的實際環境中,重現當時的情境,觀察問題是否仍舊出現。如果不會,對程式員來說,便是個短期的好消息,因為這種時候,只能先Highlight這是個可能出現的問題,但因為無法重現,也只能先擱置。
如果在回報者的環境能夠重現問題,那麼幾乎就可以確定這是個貨真價實的問題,而得加以處理。對程式員而言,必須想辦法界定問題的根源。
除錯的第二步:觀察程式的流程及狀態
有些程式員收到問題之後,往往不知所措,不知要從何著手解決問題,所以開始胡亂猜想可能的原因,接著東改改、西改改。其實,解決問題的方式應該要具系統性。
在確定問題的重現操作情境後,進一步便要觀察程式的行為為何會出現差錯。如何觀察呢?可以觀察兩件事情。第一是程式的流程是否如你所預期,第二是程式的狀態是否如你所預期。
程式的流程,指的是程式中進行流程控制的地方,例如if-else之類的分支判斷,或者各種迴圈的迭代。而程式的狀態,泛指程式中各種資料值的變化,例如程式中變數值的變化。
觀察程式的流程,要觀察程式在給定的操作情境及輸入值下,是否進入到正確的分支判斷或迴圈。而觀察程式的狀態,是要觀察各個變數,是否被正確地評估與計算。
決定要觀察的標的物之後,就要決定觀察的方法。觀察的方法,從最簡單的將訊息印到管理介面(Console)、除錯訊息寫到檔案、或用Message Box把訊息丟出來,到用專門的除錯器,做中斷點的設定、變數值的觀察,甚至利用一些日誌程式庫(Logging Library,例如Java領域有名的Log4j),將程式中的除錯訊息傾印到日誌檔或日誌資料庫中,都是常見的觀察方法。
除錯器無疑是很方便的一種工具,但是對於那種不容易被重現的問題,例如系統上線後,有時使用者的帳號資料會錯亂,由於問題本身是機率性的出現,所以不容易搭配除錯器使用。只能使用列印訊息或利用日誌記錄(Logging)的方式觀察系統。
運氣好的話,你以從記錄或印出的訊息當中,找出程式在那個範圍內,發生了不是你所預期的行為。那麼就可以進一步地在該範圍中鋪設更多的觀察點,提高觀察的密度,以縮小觀察的範圍。倘若運氣不夠好,找不到出錯的地方,就得試著提高觀察的密度,期望能夠找到出錯的範圍。
整個除錯的過程,大概就是這樣子不斷的縮小可疑的範圍,最終找到問題之所在。我自己常用的方法有點像是數值方法裡的十分逼近法。我們先將搜尋問題的範圍,畫分為若干個等分,如圖一所示:
|
圖一:運用數值方法裡的十分逼近法。先將搜尋問題的範圍,劃分若干個等分。 |
接著透過觀察點的鋪設,找出了問題落在某個範圍內,如圖二:
|
圖二:透過觀察點,可以發現問題落在某個範圍。 |
也許是因為在這個區段,我們觀察到某變數A的值是100,不是我們所預期的200,此刻,我們大概可以將範圍縮小。接著針對可能出問題的範圍,再鋪上更多的觀察點,如圖三:
|
圖三:接著針對可能出問題的範圍,再舖上更多的觀察點。 |
如此持續的縮小範圍,最終找出實際的問題所在,如圖四:
|
圖四:最終找出實際的問題所在。 |
上述的逼近法,比較類似臨時起意的觀察。這種臨時起意的觀察,通常都是為了要解決突發性的臭蟲,事實上,在撰寫程式的同時,更好的習慣是建立起除錯的基礎建設,而在下一回中,就讓我們探討如何在撰寫程式的同時,建立除錯的基礎建設。
《作者簡介》王建興
清華大學資訊工程系的博士研究生,研究興趣包括電腦網路、點對點網路、分散式網路管理、以及行動式代理人,專長則是Internet應用系統的開發。曾參與過的開發專案性質十分廣泛而且不同,從ERP、PC GAME到P2P網路電話都在他的涉獵範圍之內。
熱門新聞
2025-01-13
2025-01-15
2025-01-14
2025-01-14
2025-01-13