1. 程式人生 > >Unity3D Android使用Bugly定位崩潰問題總結

Unity3D Android使用Bugly定位崩潰問題總結

看著bugly幹了1個多月的crash問題處理,可以說是心力憔悴,整天對著一堆莫名其妙的崩潰堆疊和一大把日誌發愁,背鍋的滋味可是真不好受,得空寫一篇總結與各位背鍋俠共勉。

一般來說遊戲的Crash引起原因分為這麼幾類:1.記憶體不足、2.邏輯程式碼導致、3.Unity引擎自身問題以及4.第三方庫的自身問題。記憶體不必說了,邏輯程式碼因為是C#指令碼,又隔了一層引擎出現崩潰的可能性也是極低,大部分需要處理的crash其實都是3和4。這樣說來的話其實負責解決crash問題的同志們也就可以洗洗睡了,引擎和第三方庫的問題又不是咱的問題,咱也改不了,出了問題這鍋想接也沒法接啊。其實不然,對於3、4問題的處理,一是可能因為我們用了錯誤的呼叫方式,二是可能有一些workaround去繞過崩潰的地方,而這兩點都基於我們能夠準確定位crash產生的原因或者至少準確定位crash產生位置的基礎之上。這篇部落格也就是總結一下bugly使用的一些經驗,通過bugly去來定位或者分析這些crash。

*

首先第一步要做的就是上傳libunity.so的符號表!,這一步是最簡單也是最重要的,雖然bugly官網上是這樣寫的

2) Unity專案的Android工程中預設載入的幾個Native庫(libmono.so,libunity.so等)沒有的debug版本,所以開發者也無法獲得對應的符號資訊進行配置

但是其實Unity從5.3開始就提供的libunity的符號表https://support.unity3d.com/hc/en-us/articles/115000292166-Symbolicate-Android-crash
位置在【Unity安裝目錄】\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Release\Symbols,進行完這一步操作後基本上所有Unity引擎的crash問題都可以直接通過崩潰堆疊看出產生原因了,再結合具體的遊戲場景進一步做分析最終找到避免該crash發生的方法。
再多說一嘴,一般來說Unity的crash基本上有這麼幾類,一個是shader編譯的平臺適配問題,二是跟資源載入相關的問題,三是跟動畫、粒子這種多執行緒處理模組相關的問題。shader問題的話基本上調整調整shader就能搞定,剩下那些問題直接在google上搜索一下相關的堆疊資訊或者日誌提示資訊基本在stackoverflow或者unity官方論壇上有相關的主題討論,一般會有人提出一些workaround。另外就是瀏覽一下後面版本unity patch release釋出說明裡面的fixed項,看看有沒有相關crash的修復。總之一旦定位到了具體崩潰的模組或者方法,後面的處理基本上也就算是有眉目了,無論最後解決還是不解決基本上都能有一個靠譜的答覆。

*

Unity引擎的崩潰處理基本上就說完了,下面說說最頭痛的第三方庫的崩潰問題,通常第三方庫可是沒有符號表的,而且基本肯定是原生程式碼寫的,所以你看到的堆疊資訊基本上來說應該是這樣的:這裡寫圖片描述
這是什麼鬼!這種情況下崩潰堆疊裡是沒什麼可用資訊了。那日誌是什麼情況呢?情況絕對好不到哪裡去,bugly上截得日誌是在太短了,經常是連崩潰的點都沒有擷取到,也不知道他們是什麼演算法。不過沒有關係,一個裝置崩潰的日誌不好使還有其他的,點選檢視更多,總會是有些裝置的日誌裡能看到點有用資訊。比如下面這兩段日誌,就是來源於同一個crash下面,那資訊量可以算的上是天差級別了。
WTF!
這個Log是來搞我的麼!!!還是下面這個靠點譜
這裡寫圖片描述

需要注意的是看Log定位崩潰原因時不要僅僅被紅字所吸引,bugly應該是會把Error標成紅色,也就是第四項為E的log,但是Error不等同於崩潰,還要具體看Error後面的資訊,如果資訊裡包含什麼SIG啊Abort啊crash這類的的才能說這一條Error是crash的點。但是如果你看到的Log型別是F,也就是Fatal,那肯定就屬於系統崩潰日誌了,當然也沒準是垃圾,就比如上面展示的第一條Log。
一般來說光看崩潰資訊是沒有什麼用的,因為那都是標準的系統崩潰說明,比如SIGSEGV、SIGABRT這些,如果你只拿著這種資訊去網上搜肯定是搜不出來有用東西的,這個時候除了定位系統崩潰點之外,通常還需要做第二件事,那就是找!相!同!
有啥相同呢一般來說有執行時間(是否都是剛啟動或者長時間執行)、裝置機型(是否是某一特定機型的相容性問題)、系統版本(是否是特定系統的相容性問題),以及最重要的崩潰點之前的日誌資訊。前面幾點如果找到了一些相同特效頂多是為你描述這個crash或者瞭解crash影響的範圍有所幫助,但是真正如果要解決一個crash的話還是要從日誌資訊中入手,一般說來如果你發現了相同的日誌資訊總是出現在crash發生節點之前,那麼接下來就應該著手去分析或者測試打出日誌的點與crash的關係了。這塊說得實在有點幹,還是講一個具體的例子吧。

案例

這裡寫圖片描述
這是我們線上報出的一個crash,佔比挺高的一直都沒定位解決,首先看堆疊資訊:
這裡寫圖片描述
就一條libmono.so,什麼鬼,難道還是C#程式碼導致的崩潰?開什麼玩笑,而且怎麼會一句話就搞崩了呢。看一下執行緒資訊‘#unknown(1523)’不是UnityMain,所以肯定是排除了邏輯程式碼【直接】導致崩潰的情況。那麼接下來先看看裝置資訊:
這裡寫圖片描述
咦?怎麼系統版本全是一樣的,看來是系統版本相容性問題嘍?哼,難道要甩鍋給系統版本不管了?想得太美,版本一樣不是個有用資訊,還得接著看。裝置怎麼全是小米5?我靠小米這破手機坑老子!還有個virtual machine 2應該是個模擬器,先來看看小米5的問題吧,隨便選擇一個看下裝置詳細資訊:
這裡寫圖片描述
這xxx的ROM,這x86的CPU架構,原來剛才錯怪小米了,這裝置不是小米5啊,其實是個PC上的模擬器啊!ok,現在確認所有崩潰都發生在模擬器上了,老大,要不咱把模擬器遮蔽了唄?屏你個頭啊,咱跟人家可是有合作的!這個問題必須解決!好吧,那隻能看Log了,先隨便點開一個看一下:
這裡寫圖片描述
VM Aborting!肯定就是崩潰在這兒了,看一下崩潰資訊,native thread exited without detaching,再往上都是些不知所云的log。先來查一下這個錯誤是什麼意思,呵,CSDN上就有相關內容,隨便給個面善的兄弟打個廣告吧,http://blog.csdn.net/wangchenggggdn/article/details/7819708,大概情況就是有這麼一個執行緒結束後沒有呼叫detach,導致了這個崩潰。
嗯嗯,這好說,那找到那個執行緒的程式碼把detach加上不就解決了。。。。。個頭!這明顯就是第三方庫裡的程式碼導致的,在Unity邏輯程式碼裡沒有用執行緒(其實有網路執行緒,當時忽略了),怎麼可能改嘛!可是這個問題必須要解決啊,沒辦法,雖然現在定位了問題的原因,但是還沒有定位問題產生的位置,至少定位是哪個第三方元件的問題不行找人家解決唄,也算能有個交代。
那麼繼續看,注意到log中第二和第三欄位,92是程序ID,386是執行緒ID,如果能知道386這個執行緒是誰啟動的就好了。這個log裡是看不出來了,沒關係,看看其他的log,在大概看了100多個上報裝置的log以後,我在其中幾個log中發現了相同點!
這裡寫圖片描述
這裡寫圖片描述
Nailed It! MSDK!這口鍋給我背了一個月!那麼現在的情況是已知MSDK有個一個GetLoginRecord的執行緒,沒有呼叫Thread.Detach,導致遊戲崩潰了。怎麼辦呢?讓人家大廠改肯定是不現實了,不過既然定位了問題產生的點,那麼就進一步繼續定位邏輯程式碼中的呼叫位置,看看是不是呼叫的方式不對或者是有什麼辦法可以繞過去。那麼下一步,在呼叫GetLoginRecord的地方新增上UnityLog並輸出呼叫堆疊,出包重新測試,發現原來這些呼叫都是在TCP Socket Receive回撥執行緒中呼叫的,查看了一下SVN,這一塊的新增日誌與crash首次上報日期吻合(在兩次版本釋出時間的中間)!
唉,到頭來這口鍋還是得我們程式來背了(哭),可是那也不是我寫的啊(大哭!)!

總結一下這個crash,native thread exited without detaching,原因是在Socket執行緒中呼叫了一個第三方介面,生成了一個執行緒attach了socket執行緒但是卻沒有做detach,所以當這個socket資源被回收時attach的第三方執行緒本應呼叫detach卻沒有呼叫,導致了程式的崩潰。解決方法就是隻在主執行緒中呼叫這個介面,這樣也就不存在需要detach的情況了。

最後再總結一下crash定位,一句話,不要怨天尤人,這口鍋咱得背瓷實了!