Crash 的路上有你有我

Journey on programming
5 min readJun 15, 2019

--

寫 iOS 也快兩年了,在開發過程中遇到的 crash 其實並不可怕,從 QA 那邊測到回報的也還好,最可怕的是有出現過,但是卻不知道怎麼重現的 bug,通常這種問題在產品上線之後,都會莫名的大量出現XD

現在可以協助我們分析 Crash 的工具其實很多,包含我們在上傳 ipa 的時候,AppStoreConnect 上面也會有相關的報告,甚至我們自己使用的 Fabric, Crashlytics 等等,都可以協助我們來做分析,但是那是有 debug symbol 的情況下。

如果我們今天是從用戶的手機拿到 Crash Report,沒有 debug symbol 的支援,我們該如何繼續往下,讓我們繼續看下去…

首先,讓我們先了解整份的 crash report 主要分成哪幾個部分

1. APP 資訊
當中會提到這份報告所產生的時間,與機型版本等等。
需要注意的是有一個欄位是 Version ,其組成為 build+(version),我們需要用這個欄位來比較出該 build 是在哪一個 commit sha 與 版本的產物,才能順利的拿到 debug symbol 來做解譯。

這邊可以提一下,我們在包 APP 的過程會有一個產出物是 debug symbol (.dSYM),其會在我們 Archive 的時候自動產生,這個 bundle 當中包含該 app 與包進去的 Libarary 所需要用到的 dSYM 的檔案。

這邊還需要注意一件事情,如果是拿了別人的 Crash Report 要來進行分析,但是該作業系統並非本機 Xcode 所能支援的版本,這時候可以將該機器連接傳輸線到電腦,讓 Xcode 去進行 processing debug symbol 的動作。

2. Exception 資訊
這邊的資訊中可以看到問題發生的型別,錯誤代碼,發生原因等等,最重要的是可以看到有一個欄位是 triggered by Thread ,這邊即表示 crash 發生在哪條 thread 上,如果為 0 就代表是 main thread.

3. Thread call stack
這邊會清楚了列出在問題發生當下,每一條 thread 的 call stack,stack 本身有堆棧的概念,所以記得這邊閱讀的順序會是從下到上,即你在 A 裡面呼叫了 B 你會看到的結果會是,B 在下 A 在上的 call stack。

4. Binary Images
最下方的 Binary 即列出該 APP 載入了哪些 Library 與其所佔用的記憶體位置。這邊需要注意的就是,通常問題都會發生在我們自己專案當中,所以我們要先找到專案的名字那一行,然後可以看到前面的第一個記憶體位置,即為該 Library 被載入時候的記憶體位置,我們稱做 load address 我們稍等需要這個記憶體位置來使用。

此外我們還需要注意到,我們在載入 App 的 Library 的時候,是哪個平台,ARM64/ARMV7,通常會跟在我們的 Library 後面。

Crash Report 本來的內容僅會有給機器讀的語言,一大堆的記憶體位置,這時候我們需要 debug symbol 來做解譯的動作,我們稱為 Symbolicate ,而 Crash report 又依被解譯的程度分作
— Not Symbolicated 尚未解譯
— Partially Symbolicated 部分解譯

(通常是僅看到的 UIKit, Foundation 解譯的結果,因為在 MAC 上開啟 Crash Report 的時候會自動幫你載入)
— Fully Symbolicated 完全解譯

那我們該如何進行解譯呢?
其實解譯的方法有很多種,例如透過 SymbolicateCrash (Xcode 內建),atos (address to symbols),或者你使用 Xcode 直接開啟等等都是不錯的方法,因此我這邊僅針對 atos 來做說明,因為工作上剛好有碰到,其他的工具等到我之後有碰到再來寫文章了QQ

註: 如果是使用 Xcode 直接接上裝置,可以從 Window -> Organizer 裡面,選到該支手機,並且點擊 View device logs 然後找到發生問題的那份 crash report,點擊右鍵就會有一個 re-symbolicate 的選項可以選擇了,從這邊會比較快速的找到問題點,但如果你的電腦上沒有特定 app 的 debug symbol 就不能用這招了。

打開 terminal,並且輸入下列指令

atos -arch {platform} -o {appname.dSYM} -l {load_address}

這邊我們先了解到幾個參數的用法


-arch : 指定平台,就是剛剛提到的 ARM64/ARMV7
-o : 這邊需指定 app 的 Library
-l : 則是 binary 的 load address

輸入完之後,會發現很像進入了什麼 terminal 的畫面,這時我們就可以找到發生問題的 call stack 與其的記憶體位置,並且將其貼上去,他就會幫我們轉譯成我們看得懂的程式碼囉。

好的,其實我很嘗試的去理解並且說明到讓我自己都覺得沒有問題的境界,但終究還是未夠班,如果有錯誤的地方歡迎指正。

--

--

Journey on programming

Software Developer at 91APP. If you like my articles, please clap and follow me on Medium. Never stay still, never plateau!