回想起這個BUG,仍然讓我有些痛苦。做爲一個程序員,在發現BUG時,你學會了首先在本身代碼中找問題,或許在測試一萬次以後,你會把問題歸咎於編譯器。只有在這全部的都不起做用以後,你纔會把問題歸咎於硬件。程序員
這是我遭遇一個硬件bug的故事。web
拋開別的不說,我曾爲《Crash Bandicoot》寫存儲卡(讀寫)代碼。對於一個自大的遊戲程序員,這就像是在公園裏散步同樣輕鬆愉快,我認爲只要幾天就寫完了,最終調試用了六個禮拜。在此期間我想盡了各類方法,但有個BUG始終沒有被修復——每隔幾個小時就會發生一次。這實在是個煩人的BUG。編程
這個BUG的症狀是,當你須要保存你的進度時,代碼會訪問存儲卡,而大部分狀況下沒有什麼問題…可是偶爾讀寫會超時…沒有任何明顯的緣由。一個短小的寫入常常毀掉存儲卡。玩家要保存進度,程序不只不保存,還擦除他們存儲卡上的所有東西。天哪。網絡
過了一段時間,咱們在Sony的製做人Connie Booth慌了。咱們顯然不能帶着這個BUG發佈遊戲,而六個星期以後我對於問題出在哪一點線索都沒有。經過Connie咱們向其餘 PS1 開發者求助:有沒有人出現過像咱們這樣的狀況?沒有。絕對沒有任何人在存儲卡系統上出現任何問題。學習
在你絞盡腦汁以後,你能作的惟一一個調試方法就是分而治之:一點點去除程序中的代碼,直到留下的代碼不多但你仍然出問題。像木雕同樣去除沒有問題的代碼,留下的就是你的BUG所在。測試
在這樣的背景下挑戰在於,視頻遊戲是很難去除某一部分的。在你刪除模擬重力或者顯示字符的代碼後,如何運行遊戲?網站
你必須作的是用一個僞裝作真正的事情,但實際上只是作很簡單的不會出現BUG事情的東西來替換掉整個模塊。你必須寫新的支撐代碼來讓這些玩意正常工做。這是一個緩慢而痛苦的過程。翻譯
長話短說:我作完了。我移除了大片大片的代碼,至關多,只留下了初始化代碼——就是準備遊戲運行系統,初始化底層硬件等等。固然,我不能顯示加載/保存菜單,由於我截除了全部的圖像代碼。可是我可以僞裝用戶使用(不可見的)加載/保存屏幕而且請求保存,而後寫入卡中。debug
我最終以一個帶有這個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的問題。
鳥語:PS1時代的索尼在遊戲開發者心目中同今天的蘋果無異,故事的主人,本文做者Dave Baggett以及Sony的硬件工程師在對待問題時的嚴謹與專業精神應該值得全部開發者們學習與借鑑。只有認真對待每個BUG,才能讓遊戲獲得玩家的喜好,才能讓本身獲得同行的尊重。爲此,TestBird和咱們的6000多個合做夥伴們一直以來都在不斷努力着。