【iOS】Appleから送られてきたクラッシュログを解析し、その結果を読み解く

# はじめに
クラッシュが原因でAppleからリジェクトを食らうと、ご丁寧にクラッシュログを添付してくれることがあります。
しかしそれを見ただけでは実際どこでクラッシュしたのか分かりません。そんなときに必要となるのが「ログの解析」です。
今回は備忘録として、解析の手順から結果の読み解き方までを記しておきたいと思います。

# 手順
## 下準備
まずは**Appleから送られてきたクラッシュログ**を`clash_log.txt`とかでデスクトップに保存します。
あと**審査に提出したArchiveの.dSYMファイル**も必要なので、下記の手順で同様にデスクトップ保存します。

```
- Xcodeを起動する
- Window > Organizerを選択
- アプリ申請時に指定したArchiveを右クリック > Show in Finderを選択
- 対象の.xcarchiveファイルを右クリック > パッケージの内容を表示を選択
- dSYMsディレクトリの中にアプリ名.app.dSYMファイルがあるのでデスクトップにコピーする
```
引用元：[iOSアプリのクラッシュログを解析する - 備忘録](http://eeggs.hatenablog.com/entry/2016/02/27/095357)

ここまで来たらあとはターミナルでの作業です。
<img width="278" alt="スクリーンショット 2019-04-03 18.20.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/112818/846e682d-3356-f6b3-76a7-161146c13462.png">


## 解析する
ファイルを保存したデスクトップに移動します。

```
cd ~/Desktop 
```

このあとのコマンドでエラーが出ないようにするおまじないをかけます。

```
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer/
```

最後に解析結果を書き出します。

```
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash -v ~/Desktop/clash_log.txt ~/Desktop/アプリ名.app.dSYM > crash.txt
```

うまくいくとターミナルに結果がぶわっと吐き出されて、ファイル（crash.txt）が書き出されます。
ファイルの中身をエディタで見てみると、解析されたっぽいログが並んでいるのが確認できると思います。（空のファイルができてしまった場合、残念ながら失敗です）

<img width="399" alt="スクリーンショット 2019-04-03 18.49.13.png" src="https://qiita-image-store.s3.amazonaws.com/0/112818/d13c227e-e710-f1ee-4fcc-658d866e21d5.png">

## 解析結果を読み解く
結果が出たのはいいものの、ざっと1000行近くのログが並んでおり、「これどうやって見たらいいんだ…」と途方に暮れたくなるかもしれません（わかります、私もそうでした）。
<img src="https://qiita-image-store.s3.amazonaws.com/0/112818/89aa4d2f-e888-7f4a-8695-4e9ae29504fe.png" width="500"/>

とりあえず上の方から見ていきましょう。

> app_name:
> Date/Time:
> OS Version:

よく見知った単語が並んでおり少し安心できます。はじめの20行ほどは**ログの基本情報**だと見て取れました。
その先に出てくるのが**クラッシュした原因**です。

>Exception Type:  EXC_BREAKPOINT (SIGTRAP)

今回の`EXC_BREAKPOINT`というのは、[Appleのドキュメント](https://developer.apple.com/library/archive/technotes/tn2151/_index.html#//apple_ref/doc/uid/DTS40008184-CH1-SYMBOLICATION)によると
・使用しようとしたオプショナル型がnilだった
・型変換に失敗した
といった原因が考えられるようです。

じゃあそれが具体的にどこで起きているのか。それがその先にずらーっと並んでいる**スタックトレース**に記されています。

>Thread 0 name:  Dispatch queue: com.apple.main-thread
>Thread 0 Crashed:
>0  HogeApp                     	0x0000000104844dd4 specialized Fuga.init(json:) + 904660 (Fuga.swift:355)
>1  HogeApp                     	0x0000000104844a84 specializedFuga.init(json:) + 903812 (Fuga.swift:355)
>2  HogeApp                     	0x000000010483dee4 Fuga.init(json:) + 876260 (<compiler-generated>:0)
・・・

「え、このトレース全部読まないといけないの…？」と絶望感に苛まれる方もいるかもしれません（わかります、私もそうでした）。

スタックトレースは例外の発生状況と発生個所を示すもので、プログラムを実行した情報を「下から上に」積み上げていきます。
つまり**一番上（とその周辺）に書かれた行のクラス・行数を見ることで、実際にどこでクラッシュしたのかを読み取ることができる**のです。全ての行に目を通す必要はありません。

`Thread 0`というのはメインスレッドのことなので、
上の例だとメインスレッドの、Fugaクラス355行目で、オプショナル型がnilだったり型変換に失敗したりしたというような理由でクラッシュしたのだ！と突き止めることができると思います。

これで無事にバグを修正し、再申請に持っていくことができそうですね:thumbsup:

# さいごに
一度手順を覚えてしまえばどうってことのないクラッシュログ解析ですが、初めて自分でやろうとすると結構骨が折れました。「解析」という難しそうな言葉に尻込みしてしまう方もいるかもしれません。そんなときこの記事が少しでもお役に立てば嬉しく思います。

以下に参考にさせていただいた記事を載せておくので、さらに詳しく知りたいという方は合わせてどうぞ。

[iOSのクラッシュログをSymbolicate（復元）して解析する - Qiita](https://qiita.com/ruwatana/items/cc470eb229d267d693b0)
[iOSアプリのクラッシュログを解析する - 備忘録](http://eeggs.hatenablog.com/entry/2016/02/27/095357)
[スタックトレースからデバッグのヒントを読み取る：デバッグのヒント教えます（2） - ＠IT](https://www.atmarkit.co.jp/ait/articles/0605/20/news012.html)
[Javaのエラー解決の王道～スタックトレースの読み方～ | ITのおもちゃ箱](https://ittoybox.com/archives/588)
[iOS - appleからのクラッシュ報告でリジェクトをくらい、クラッシュログの解決が出来ない。｜teratail](https://teratail.com/questions/114807)