歡迎光臨
每天分享高質量文章

一次因量子力學而 Debug 的痛苦經歷

(點擊上方公眾號,可快速關註)


翻譯:伯樂在線 – 熊鐸 ,英文Dave Baggett

好文投稿, 請點擊 → 這裡瞭解詳情


伯樂在線導讀:除錯 Bug不每個程式員工作中必須品。在 Quora 上有一個和 Bug 相關的熱門問答帖:《What’s the hardest bug you’ve debugged? | 你除錯過的最難 Bug 是?》。在眾多回覆中,Dave Baggett 的經歷最讓人驚嘆,得到了 5500 多個頂。

回想起這個bug,仍然讓我有些痛苦。作為一個程式員,在發現bug時,你學會了首先在自己代碼中找問題,或許在測試一萬次之後,你會把問題歸咎於編譯器。

只有在這所有的都不起作用之後,你才會把問題歸咎於硬體。

這是我遭遇一個硬體bug的故事。

拋開別的不說,我曾為《Crash Bandicoot》寫儲存卡(讀寫)代碼。對於一個自大的游戲程式員,這就像是在公園裡散步一樣輕鬆愉快,我認為只要幾天就寫完了,最終除錯用了六個禮拜。

在此期間我做一些其他的事情,但我一直回來處理這個bug——幾天內每天幾個小時。這個bug實在煩人。

這個bug的癥狀是,當你需要儲存你的進度時,代碼會訪問儲存卡,而大部分情況下沒有什麼問題…但是偶爾讀寫會超時…沒有任何明顯的原因。

一個短小的寫入經常毀掉儲存卡。玩家要儲存進度,我們不僅不儲存,還擦除他們儲存卡上的全部東西。天哪。

過了一段時間,我們在Sony的製作人Connie Booth慌了。我們顯然不能帶著這個bug發佈游戲,而六個星期之後我對於問題出在哪一點線索都沒有。通過Connie我們向其他 PS1 開發者求助:有沒有人出現過像我們這樣的情況?沒有。絕對沒有任何人在儲存卡系統上出現任何問題。

在你絞盡腦汁之後,你能做的唯一一個除錯方法就是分而治之:一點點去除程式中的代碼,直到留下的代碼很少但你仍然出問題。像木雕一樣去除沒有問題的代碼,留下的就是你的bug所在。

在這樣的背景下挑戰在於,視頻游戲是很難去除某一部分的。在你刪除模擬重力或者顯示字符的代碼後,如何運行游戲?

你必須做的是用一個假裝做真正的事情,但實際上只是做很簡單的不會出現bug事情的東西來替換掉整個模塊。你必須寫新的支撐代碼來讓這些玩意正常工作。這是一個緩慢而痛苦的過程。

長話短說:我做完了。我移除了大片大片的代碼,相當多,只留下了初始化代碼——就是準備游戲運行系統,初始化底層硬體等等。當然,我不能顯示加載/儲存選單,因為我截除了所有的圖像代碼。但是我能夠假裝用戶使用(不可見的)加載/儲存屏幕並且請求儲存,然後寫入卡中。

我最終以一個帶有這個bug的很少量的代碼結束——但問題仍然隨機出現!在大多數情況下沒啥問題,但是偶爾會失效。基本上所有的Crash的實際代碼都被移除了,但還是這樣。這實在是莫名其妙:留下來的代碼基本上都沒做什麼事。

在那時——估計是凌晨3點——一個想法蹦了出來。讀寫(I/O)涉及精確定時。無論是硬碟、儲存卡、藍牙發送器——隨便啥——做讀寫的底層代碼都是根據時鐘來的。

時鐘讓不直接連接到CPU的硬體設備和cpu運行的代碼同步。時鐘決定了波特率——資料從一頭傳到另一頭的速率。如果計時有什麼問題,硬體或者軟體或者兩者都會亂七八糟的。這真的,真的很糟糕,並且通常導致資料損壞。

如果我們的初始化代碼以某種方式弄亂了計時會怎麼樣?我又看了一遍測試程式中和計時有關的代碼,並註意到我們將PS1上的可編程計時器設置到了1kHz(1000跳每秒)。這是比較快了,當PS1啟動的時候,預設狀態大概是100Hz。因此,大多數游戲將他們的計時器設置為100Hz。

這個游戲的帶頭(和除我外的唯一)開發者Andy,將計時器設置為1kHz,使得Crash的動作計算更加準確。Andy喜歡矯枉過正,如果我們要模擬重力,我們應該盡可能的提高精度!

然而如果提高計時器頻率莫名其妙的干擾了整個程式的計時,故而將這個計時器設置到儲存卡的波特率上會怎樣呢?

我將計時器代碼註釋掉。然後我就無法複原這個bug了。但是這並不表示bug被修複了,這個問題是隨機發生的。萬一我只是運氣好呢?

幾天過去了,我還是在玩我的測試程式。Bug沒有再出現。我回到全部的Crash代碼中,修改了加載/儲存代碼,在訪問儲存卡之前將可編程計時器重置為預設設置(100Hz),之後設置回1kHz。從此之後沒有發現問題再次出現。

但是…為什麼?

我重新回到測試程式上,試著檢測當計時器設置為1kHz時出現的那些錯誤的樣式。終於,我註意到這些錯誤出現在使用PS1手柄的人身上。因為我自己很少這樣做,所以我沒有註意到(為啥我要在測試加載/儲存代碼的時候用手柄)。但是有一天我們的美工等我去完成測試(我確定那時候我在爆粗口),而他緊張的擺弄著手柄。卡損壞了。“等下,怎麼回事?喂,再來一次!”

一旦我發現了這兩件事是聯繫著的,就很容易重現bug:開始寫入儲存卡,動一下手柄,儲存卡損壞。在我看來完全是硬體bug。

我去找Connie告訴他我的發現。她轉述給設計過PS1的硬體工程師。她被告知:“不可能,這不可能是硬體問題。”我跟她說問一下我能不能直接和他說。

那個工程師給我打電話了,他用著他的爛英語,我用著我更爛的日語,我們爭論一會。我最後說:“我給你一個30行的測試程式,讓你在動手柄的時候能夠出現這問題。”他答應了。他向我保證,這是浪費時間,而他正在一個新專案上很忙,但因為我們是Sony很重要的開發者,他會試的。

第二天晚上(我們在洛杉磯,而他在東京,所以對於我來說是晚上而他是到了第二天),他給我打電話,不好意思的向我道歉。這是個硬體問題。

我還是沒有完全搞清楚問題到底在哪,但是我的印象中,從Sony總部的反饋聽到的是,如果將可編程計時器設置到足夠高的時鐘頻率,會影響到主板上時鐘晶振附近的一些東西。這些東西之一就是儲存卡的波特率控制器,同時也設置手柄的波特率。我不是搞硬體的,所以對於細節我相當模糊。

但是主旨是主板上兩個獨立部分的串擾,以及手柄接口和儲存卡接口資料發送的結合在 1kHz 的時鐘頻率下會導致丟位,從而資料丟失,以致卡損壞。

這是我全部編程生涯中,唯一一次因為量子力學而debug的問題。

覺得本文有幫助?請分享給更多人

關註「演算法愛好者」,修煉編程內功

淘口令複製以下紅色內容,再打開手淘即可購買

範品社,使用¥極客T恤¥搶先預覽(長按複製整段文案,打開手機淘寶即可進入活動內容)

近期,北京地區正常發貨,但派件時間有所延長

赞(0)

分享創造快樂