除錯與工具

把除錯當成一套有紀律的流程(debugging)

想像你家廚房的水龍頭在漏水。你可以隨手把每顆看得到的螺絲都鎖緊、然後祈禱——或者你可以先看清楚水到底從哪裡來,一個一個關掉以找出哪個能止住滴水,對磨損的墊圈提出一個猜想,把它換掉,再確認滴水是不是真的停了。第二種做法就是把除錯當成一門紀律:不是亂揮一通,而是一個小小的循環——觀察、縮小範圍、猜測、修正、確認。所謂臭蟲(bug),不過就是程式所做的事與你想要的事之間的差距;除錯就是你刻意去縮小這個差距,而不是靠運氣。

這個循環有明確的步驟,而跳過它們正是讓除錯感覺像黑魔法的原因。重現:找出一個可靠、最好很小的配方,能每次都讓臭蟲出現,因為一個你無法隨手觸發的臭蟲就是一個你無法研究的臭蟲。隔離:縮小情境——刪減輸入、關閉功能、把程式碼註解掉——直到只剩下必要的觸發條件,這樣你才大致知道該往哪裡看。提出假設:對成因說出一個具體、可測試的猜想(「檔案不存在時那個指標是 NULL」),而不是一種模糊的感覺。測試那一個猜想——用除錯器、用印出來的訊息、用斷言——讓結果去證實或推翻它。修正真正的成因,然後再次重現、看著臭蟲不再出現,藉此確認。每一步都很便宜;按順序做才是省下好幾小時的關鍵。

為何重要:系統程式設計裡的臭蟲常常默默地破壞記憶體,過很久才當掉,而且當掉的地方離真正的錯誤很遠,所以在這裡用猜的代價特別高。有紀律的循環讓你保持誠實——它逼你確認臭蟲是真的修好了,而不只是被藏起來,也讓你不會去「修」那些本來就沒壞的東西。最大的陷阱是一次改好幾樣東西:每次實驗只改一個變因,否則你永遠不會知道是哪個改動起了作用。這個欄位裡的工具——除錯器、消毒器、追蹤器——不過是讓你在同一個循環裡取得更銳利觀察的手段。

一個程式「有時候」會當掉。有紀律的做法:先把重現條件釘死——只有在輸入檔是空的時候才當。把範圍隔離到讀取迴圈。提出假設:讀到零個位元組時,緩衝區是在未初始化的情況下被使用了。用一個印出訊息來確認,修好讀取的檢查,然後再餵一個空檔案進去,看著它乾淨地跑完。

重現 -> 隔離 -> 提出假設 -> 修正 -> 確認:每一步只改一個變因。

在一個你並不理解的改動之後「看起來能動」的修正,並不算被確認過——臭蟲可能只是搬了家或安靜下來。只有當你能解釋為什麼原本的重現步驟現在會正確運作時,你才算真的修好了它。

又稱
systematic debuggingthe scientific method applied to bugs系統化除錯