首頁 遊戲資訊 譯介丨我遇到過的最硬的 B...

譯介丨我遇到過的最硬的 Bug

前言:這是一篇發布在 10 年前的文章(原文發布於 2013年10月31日),作者講述了它為 PS1 開發《古惑狼》時遇到的一個奇怪的 Bug。原文是問答網站 Quora 的一個回答,它回答的問題是:你調試過最硬的 Bug 是什麼?這個答案最終也獲得了 6.7K 的投票。

原文連結丨作者:Dave Baggett

說起這事我現在都還有點痛苦。

作為一個程式設計師,你會有一個 bug 原因的追查列表。首先你會把 bug 歸咎於你的代碼,然後是第二、第三、……,在數到第一萬的時候你可能會怪編譯器。直到最後,你只能去責問硬體。

這就是我遇到的硬體 bug 的故事。

我當時在為《古惑狼》寫一些操作(加載/存儲)記憶卡的代碼。對一個遊戲開發者老兵來說這就是稀鬆平常的事,我估計我幾天就可以搞定。結果我花了六個禮拜來調試它。這段時間我當然還在做別的事,但我時不時就會回來調試一下這個 bug,大概每天會花幾個小時。讓人崩潰。

問題是這樣的。你要存儲你的遊戲進度,所以你會訪問記憶卡。大部分時間它都挺正常的……但是每隔一段時間,你就會遇到一次讀寫超時的錯誤,毫無緣由。一次快速寫入(short write)常常會把記憶卡搞崩:玩家存檔的時候我們不僅沒辦法正確保存,我們還會把玩家的記憶卡整個清除。D’OH!

過了一陣,我們在SONY的製作人, Connie Booth [譯注],開始慌了。很顯然我們不能帶著這個 bug 把遊戲推出去,而且六個禮拜過去了,我還是一點頭緒都沒有。通過 Connie 我們和 PS 1 的開發者搭上了話 —— 「你們誰見過類似這樣的事情嗎?」結果是沒有。沒有任何人在記憶卡的存儲系統上遇到過任何問題。

在你束手無策的時候,你唯一能做的就是分解代碼然後逐個擊破:一點一點移除可疑的代碼,直到你只剩下一段相對短小但是依然會導致 bug 的代碼。排除所有無關的部分,剩下的就是 bug 所在。 在電子遊戲的場景下這事就有點棘手:你很難移除代碼片段。比如說,如果你移除了重力模擬的代碼,或者移除了渲染角色的代碼,你還怎麼運行遊戲?你能做的只能是移除一些模塊,然後用一些接口代碼(stubs)作為代替。這些接口代碼會假裝工作,但實際上它們只會做一些絕對不會引發 bug 的事情。你必須重新寫一些框架(scaffolding code) 來讓遊戲足以運行。這是一個漫長而痛苦的過程。

長話短說:我這麼幹了。我移除了大塊大塊的代碼,直到最後,我基本上除了啟動代碼什麼都沒剩下了。這些代碼就只是啟動系統,運行遊戲,初始化渲染硬體,諸如此類。當然,這時候我無法調出加載/存儲的菜單,因為我把圖像相關的代碼都移除了。但是我可以模擬用戶使用了那個(不可見)的加載/存儲界面,選擇保存,然後寫入記憶卡。

終於,我把會引發問題的代碼縮小到了一個非常小的范圍——但是依然會隨機觸發!大部分時間它都能正常工作,但是每過一段時間它就會失敗。幾乎所有真正和《古惑狼》相關的代碼都被移除了,但是錯誤還在。事情變得令人費解:剩下的代碼其實什麼都沒干。

在某個時刻——可能是凌晨三點——我冒出了一個想法。讀寫操作(I/O)涉及到精確的時序。當你和硬體打交道的時候——一塊集成快閃記憶體,或者一個藍牙發信器,隨便什麼東西——那些讀寫相關的底層代碼需要依照時鍾來執行。時鍾可以讓沒有和 CPU 直接連接的硬體設備與 CPU 保持同步。時鍾決定波特率——數據從一邊發送到另一邊的速率。如果時序被弄亂了,硬體或者軟體,或者二者皆有,會被迷惑。這非常非常糟糕,通常會導致數據丟失。

萬一由於某種原因我們的設定代碼里面有什麼東西把時序給搞亂了呢?我又看了一遍測試程序中和時序有關的代碼,我注意到我們把 PlayStation 1 的可編程定時器設置在了 1 kHz。這是一個相對比較高的頻率,PlayStation 1 啟動的時候默認頻率是 100 Hz。Andy,這遊戲的主程(也是除我之外唯一開發者),把它設置成了 1 kHz,以使《古惑狼》的運動計算更加精確。Andy 喜歡這種追求極致的風格,既然我們要模擬重力,那我們就要盡可能地准確。

但是,如果增加這個定時器的頻率會莫名其妙地影響到整個程序的時序,進而影響記憶卡的波特率呢?

我把設置定時器的注釋掉,然後我就沒辦法復現那個錯誤了。但是這並不能說明我修好了它,因為這個問題是會隨機出現的。萬一我只是運氣好呢?

接下來的幾天,我一直擺弄我的測試程序,那個 bug 再也沒出現過。我回到《古惑狼》完整代碼,修改了加載/存儲的代碼,在訪問記憶卡之前把可編程定時器設置成默認的 100 Hz,在訪問過後再把它調回 1 kHz。於是我們再也沒見到過那個問題。

但是為什麼?

我反復地調試測試程序,嘗試在定時器被設置在 1 kHz 時找到一些錯誤的共有模式。最終我注意到,當有人在用 PlayStation 1 的手把的時候就會出錯。由於我很少這麼干——我在測試加載/存儲的代碼,怎麼會去碰手把呢——所以我一直沒發現這事。但是有一天,一位美術在等我完成測試——我當時肯定在罵罵咧咧的——於是他就在小心翼翼地擺弄手把。然後就出錯了。「等會,怎麼回事?嘿,你再做一次!」

一旦我觀察到了二者的關聯性,復現就變得很容易了:開始寫入記憶卡,搖動手把,記憶卡崩潰。我覺得這應該是一個硬體 bug。

我去找了 Connie,告訴她我的發現,她又把這些轉述給了開發了 PlayStation 1 的一位硬體工程師。「不可能,」對方跟她說,「這不可能是一個硬體的問題。」我問她能不能讓我直接和對方講。

於是他打電話過來,我們用他蹩腳的英文和我(極其)蹩腳的日語爭論起來。最終我告訴他,「讓我發給你一段 30 行的測試程序,你搖搖杆的時候就會觸發這個問題。」他接受了。這只會是浪費時間,他向我保證,而且他當時正在忙於另一個項目,但他還是勉為其難地接受了,因為對SONY來說我們是非常重要的開發者。我整理了一下我的測試小程序,發給了他。

第二天晚上(我們當時在洛杉磯,而他在東京,所以他的白天剛好是我的晚上)他打電話給我,非常難為情地向我致歉。這確實是一個硬體問題。

直到最後我也不是很清楚具體的原因是什麼,但我印象中從SONY總部聽到的說法是,當那個可編程定時器被設定在一個比較高的時鍾頻率,它會影響主板上諧振器附近的一些東西。其中一樣就是記憶卡的波特率控制器,它同時也會設置手把的波特率。硬體不是我的專長,所以我不是很清楚那些細節。重點是主板上的通訊干擾,還有在定時器被設置在 1 kHz 時同時發往手把埠和記憶卡埠的數據傳輸,會導致一些比特的數據丟失,於是數據失效,於是記憶卡崩潰。

[譯注]: Connie Booth 參與了很多知名遊戲的製作和發行,包括《小龍史派羅》、《古惑狼》、《瑞奇與叮當》、《蜘蛛俠》、《秘境探險》、《最後生還者》、《往日不再》等。2020 年,她以SONY互娛的產品開發部門副總裁入選了 2020 年電子遊戲名人堂。

來源:機核