日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

那些年,我在游戏开发中改过的bug:靠不住的OS和SDK

發布時間:2024/8/26 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 那些年,我在游戏开发中改过的bug:靠不住的OS和SDK 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

記憶中有很多次了,幾個程序員朋友聊天,聊著聊著,就聊到自己遇到過的bug。然后大家開始口沫橫飛交流那些或詭異或神奇的bug,談論自己當年是如何搞定bug或是被bug搞定。

正好看見Gamesutra上也登了篇Dirty Coding Tricks ,發現老外也有這個癖好,原來天下程序員本一家。一路走來,程序員的成長,便是一路刀光劍影,與bug斗個你死我活。了解別人的Debug過程,或是回憶一下自己Debug的時候思路,也是很有意思的事情,值得定期總結。

下面分享一下自己遇到過印象深刻的bug:

靠不住的c:Memcpy的傳說

做一個PC項目的時候,兇猛的測試兄弟把Winxp 64單獨列出來,作為一個測試平臺,然后我們的噩夢就開始了。游戲在Winxp 64上面頻繁Crash,經常在更新Octree的時候訪問到空指針,但邏輯上來看那個指針不可能是空的。Crash位置很隨機,到處都有,通常都是玩了一個小時在一個莫名其妙的地方Crash。

第一反應就是那些地址被非法訪問,可能是某個錯誤的指針指向那里,往里面寫了不該寫的值。于是我根據最常Crash的地址設下數據斷點,試了好幾天,從來沒有斷下過,Crash還是依舊。然后同事試圖加上大量的保護代碼,判斷一個指針是不是空指針后才使用,很好的降低了Crash機率,但偶爾還是會有。想想問題根源沒有找到,降低Crash概率只是讓自己更難修bug,而且訪問Octree也比較多,亂加保護會影響性能,我一狠心又把保護代碼全去掉了。
?

Takeaway: 不到萬不得已,不要用保護代碼掩蓋Bug,它知會讓你的日子更難過。


來回幾輪搞下來,根據某次比較靠譜的Crash Callstack,懷疑到了memcpy。memcpy是個老同志了,兢兢業業地忙碌在各個程序里,負責搬運數據很多年,工作績效有口皆碑。它有什么問題呢?它還能有什么問題呢?

為了保證多線程能同步并行執行,我們程序中有個很大的memcpy,把一塊Octree從后臺用memcpy復制到前臺的工作buffer。當然這個做法的設計優劣不在此討論,存在即合理,2007年,多線程引擎我們還不是那么擅長搞。

既然有懷疑,就要捉奸。我做了試驗,在memcpy后面直接加一個循環,逐字節比較源數據和目標數據,有時候居然會不相等... 這個可顛覆了我的世界觀。我試圖寫了一個函數,里面就一個循環,逐字節復制數據,然后把所有的memcpy全替換成這個函數,果然不Crash了。但顯然這是不行的,速度太慢了。

既然有了點線索,就可以試圖簡化bug重現條件了。我不能每次都花一個小時去運行游戲,尋找那一次crash。我在游戲load起來,開始走主循環后,加了一個死循環,不停用memcpy復制一塊內存,然后比較源數據和目標數據。源數據里面沒有0,都是1-255的值,可是運行幾十秒以后目標數據居然有0。這樣,我們成功地把重現一次bug的平均時間從一個小時降低到一分鐘。

我們的懷疑從3d代碼轉移到多線程,在進入那個死循環之前,我們設下斷點,把其他無關的線程全部都Freeze,只有那個線程會運行。這樣,任何多線程的干擾全部排除,memcpy在一個理想的環境中歡快的運行,但memcpy還是會出錯。

繼續簡化,我單獨寫一個小程序,里面只做死循環和memcpy,游戲賬號交易平臺來判斷是不是OS的問題(實在是走投無路了)。試驗結果是,Winxp 64沒有問題,memcpy始終如一地正常工作著(本該如此^_^)。 可是某一次,當我們的游戲在后臺運行的時候,再啟動這個小程序,居然memcpy又出問題了...無語了,原來我們的游戲還能萬里追殺,跨進程搞垮OS下面的其他進程。

山窮水盡疑無路,我無奈下單步跟蹤了一下memcpy的匯編代碼,上來有兩句

  • cmp DWORD PTR __sse2__available,0
  • je Dword_align
  • 復制代碼


    也就是說,memcpy上來看有沒有設置__sse2_available,這個值估計是CRT庫里面設的,如果有SSE2就執行sse優化的memcpy,沒有就跳到Dword_align那里執行普通版本的流程。我開始懷疑是不是我們的游戲里面對系統做了點什么手腳,導致在__sse2_available允許的情況下,優化的代碼會執行出錯。游戲的代碼規模實在太大,又用了n個中間件,我無力一一查看,且我也看不懂SSE代碼(哎呀好羞射),就隨手做了個試驗,在那句判斷的地方,通過Debugger把__sse2_available的值改成了0。從此memcpy再也不出錯了。

    所以最終的解決方案是,Win64下,我在游戲一開始初始化的地方,加上謎一樣的初始化代碼:
    ?

  • extern "C" extern DWORD __sse2_available;
  • __sse2_available = 0;
  • 復制代碼


    這樣memcpy就永遠使用不做sse2優化的代碼了。

    memcpy不使用sse2后會不會有性能問題?經過測試,發現問題不大,對于頻繁調用的少量數據復制,memcpy不太能從sse2里面得到多少好處。對于大量數據的復制,我們用得也不多,profile了一下,沒有發現明顯的瓶頸,無視了。這事情也可以從反向理解,由于游戲規模太大,各種多線程和GPU/CPU同步,導致任何一點的效率損失,可能不會擴散到整個游戲,被其他同步和等待吸收掉了…我真是一個好程序員,能想到這么好的理由說服自己。

    對游戲跨進程影響其他程序的memcpy,實在沒能力解決了,Winxp 64是一個太小眾的環境,用戶要么用Winxp 32, 要么用Vista 32/64,市場占有率很低,我們也算仁至義盡了。

    可得結論:Winxp 64靠不住(其實問題還是應該出在我們內部,不過其他OS都沒問題,就賴在它身上了)
    ?

    Takeaway: 首先,你要有一個有耐心的老板,才能給你時間去查這么奇怪的Bug


    為了達成目的,我們要不擇手段。

    靠不住的SDK:OutputDebugString

    話說當年開發Splinter Cell 4,使用的還是XBOX360的Alpha kit。微軟早期的360 Kit,全不像后期的Kit,后期kit長得和主機差不多。而當時的KIt,是用一個很大的Power Mac G5,換上一塊ATI顯卡,再刷上MS的固件,連馬甲都不穿一件就出來見人了。

    Xbox 360開發SDK,早期bug一大堆,比如預編譯頭文件太大了,編譯器抱怨說預編譯頭文件預留內存不夠,這個好辦,加上/Zm512編譯選項即可。加上,編譯,沒用?!只好寫信去MS問,他們說,哦,原來如此啊,今天天氣真好,哈哈哈哈,請等待下一次更新,謝謝您匯報云云...雖是MS的bug,可是我也不能等著他修復才工作,只好手動拆分預編譯頭文件,把很多內容放在預編譯頭文件外面,預編譯頭文件就會變小,編譯就可以順利通過了。

    扯遠了,回頭來說這個OutputDebugString的問題。

    360有3個cores,每個core有2個Hyperthreads,總計6個線程,我們的游戲在邏輯線程、聲音線程和渲染線程外,還開了3個輔助線程,用自己寫的Thread Pool管理系統來管理這些線程,初始化的時候就是一個循環把每個線程創建出來。

    這個bug的表現現象就是加載失敗,程序僵死。團隊當時有幾十個測試人員,每天打游戲八小時,從沒碰到過游戲加載失敗情況。但是開發人員這里就有很低概率會加載失敗,表現情況為用VC啟動游戲,然后過一會加載屏幕就僵死不動了。開發人員往往過了5分鐘還沒在電視上看見游戲畫面才知道游戲又掛了,重現概率是每個程序員每一到兩天碰到一次。我們擔心這是線程管理系統內部的問題,就讓每個開發人員碰到這種情況不要急著重啟動,把現場給我看一下。每次僵死的時候都是在系統內核死鎖,Callstack也沒有有價值的信息,基本都是在創建每個線程的時候打印一句語句的時候就內核就死了。在接下來一周里,我試圖在線程管理系統內部加一些日志輸出,每次重現bug的時候查看日志,也沒有找到更好的線索。

    重現概率實在太低,不好調試,于是我試圖用簡單的程序片段來重現這個bug。因為都是創建新線程時候死鎖,第一個想到的就是寫一個小程序,直接一個死循環,創建線程,打印日志,然后殺掉線程,重復再做。果然能夠重現bug,程序運行1分鐘以后,就死鎖了。同樣的現場,還是在OutputDebugString內部死鎖了。難道bug不是在線程庫,而是在OutputDebugString內?
    ?

    Takeaway: 不要輕易相信你看見的表層問題,問題可能在別處


    正好那些天有個微軟360開發組的人員在我們組Onsite支持,于是他帶著大量的360 Sdk的符號庫(Symbol)來幫助調試,因為他不是做這一個模塊的,最后也沒有什么結果。最后他把我的小程序發回微軟,找到內部開發人員處理,這比我們直接走正式support流程快很多。
    ?

    Takeaway: 隔離問題,編寫更小的測試案例,便于和別人溝通交流,尋求幫助


    一天后,微軟發回Email,說這是內部的Bug,請無視,不會影響Release版本,是Debug協議上的問題。

    可得結論:微軟靠不住。

    總結

    以上是生活随笔為你收集整理的那些年,我在游戏开发中改过的bug:靠不住的OS和SDK的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。