iOSアプリのクラッシュログから解析する


概要

リリースビルドのiOSアプリのクラッシュログから、クラッシュ理由を探るべく方法を調査したのでそのメモです。

なお、今回の記事を書くにあたって以下の記事を参考にさせていただきました。

準備するもの

解析するにあたっていくつか準備するものがあります。

  • クラッシュログ(.crash.ipsファイル)
  • Xcode
  • dSYMファイル

大まかなフロー

大まかなフローをまず先に示します。

  1. iPhone/iPadなど端末からクラッシュログを取り出す(.crash or .ipsファイル)
  2. 対象アプリの.appファイルと.dSYMファイルを取り出す
  3. 取り出したログをsymbolicateする(実行ファイルのsymbolicatecrash

クラッシュログを解析するためには、抽出したログからだけでは困難です。
というのも、クラッシュログは

Thread 32 name:  com.apple.NSURLConnectionLoader
Thread 32:
0   libsystem_kernel.dylib          0x00000001855ecbc4 0x1855ec000 + 3012
1   libsystem_kernel.dylib          0x00000001855eca3c 0x1855ec000 + 2620
2   CoreFoundation                  0x0000000185a9bce4 0x1859b2000 + 957668
3   CoreFoundation                  0x0000000185a998b0 0x1859b2000 + 948400
4   CoreFoundation                  0x00000001859ba2d8 0x1859b2000 + 33496
5   CFNetwork                       0x0000000186123b40 0x186075000 + 715584
6   Foundation                      0x00000001864e3860 0x1863d6000 + 1103968
7   libsystem_pthread.dylib         0x000000018572032c 0x18571e000 + 9004
8   libsystem_pthread.dylib         0x00000001857201f8 0x18571e000 + 8696
9   libsystem_pthread.dylib         0x000000018571ec38 0x18571e000 + 3128

こんな感じで、いったいどのクラスのどのメソッドでクラッシュしたのか、というのがまったく分かりません。(フレームワークくらいなら書いてありますが・・・)
そこで、解析しやすいように、実行した対象のメモリ番地がなにを指しているのかをsymbolicate(シンボル化)する必要があります。

実際にシンボル化すると以下のような形に復元されます。

Thread 32 name:  com.apple.NSURLConnectionLoader
Thread 32:
0   libsystem_kernel.dylib          0x00000001855ecbc4 mach_msg_trap + 8
1   libsystem_kernel.dylib          0x00000001855eca3c mach_msg + 72
2   CoreFoundation                  0x0000000185a9bce4 __CFRunLoopServiceMachPort + 196
3   CoreFoundation                  0x0000000185a998b0 __CFRunLoopRun + 1424
4   CoreFoundation                  0x00000001859ba2d8 CFRunLoopRunSpecific + 436
5   CFNetwork                       0x0000000186123b40 +[NSURLConnection+ 715584 (Loader) _resourceLoadLoop:] + 404
6   Foundation                      0x00000001864e3860 __NSThread__start__ + 996
7   libsystem_pthread.dylib         0x000000018572032c _pthread_body + 308
8   libsystem_pthread.dylib         0x00000001857201f8 _pthread_body + 0
9   libsystem_pthread.dylib         0x000000018571ec38 thread_start + 4

見比べてみるとだいぶ見やすくなっているのが分かるかと思います。
自分がコードを書いた部分でのクラッシュであればメソッド名などからあたりを付けることができると思います。

iOSからクラッシュログを取り出す

さあ、では実際に解析作業に入りましょう。
まずは対象アプリのクラッシュログを取り出します。
クラッシュログは以下の場所に保存されています。
該当アプリのログを探して取り出しましょう。

設定アプリ > プライバシー > 解析 > 解析データ

こんな感じで、ログを出力したアプリ名が記載されているのでそれを探します。
該当ファイルがあったらそれを出力してMacに送ります。(自分の手元に端末がある場合はAirDropでも受け取れるようです)

.appと.dSYMを取り出す

次に対象アプリの.app.dSYMを取り出します。

対象ファイルは、Xcodeの「Xcode > Window > Organizer」でウィンドウを開くとビルドしたアーカイブ一覧が表示されます。

対象ビルドを選択、右クリック > Show in Finder」とすると、対象のファイル(.xcarchive)がFinderに表示されます。
そして、「対象ファイル、右クリック > パッケージの内容を表示」を選択すると以下のように.appファイルと.dSYMファイルを見つけることができます。

シンボル化する

前述までに取り出したファイルを使って実際にシンボル化を行います。
シンボル化を行うにはXcodeに付属しているsymbolicatecrashを利用します。
実行ファイルはXcodeのアプリ内にあります。以下のパスです。(バージョンによっては異なる位置にあるかもしれません)

symbolicatecrashの場所
$ /Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash

コマンドは以下のように指定します。

※ 適当なフォルダ(下の例ではworks_folder)などを作って、そこに必要なファイル(クラッシュログなど)をすべて集めておくと作業しやすいでしょう。

シンボル化
$ /path/to/xcode-thing/symbolicatecrash -v ./works_folder/hoge.ips ./works_folder/hoge.app.dSYM

また、シンボル化した情報は標準出力に出力されるので、ファイルなどで保存したい場合は以下のようにすることでファイルに保存することができます。

ファイルへの保存
$ /path/to/xcode-thing/symbolicatecrash -v ./works_folder/hoge.ips ./works_folder/hoge.app.dSYM > anyName.txt

ただし、初期状態では以下のエラーが出ます。

Error: “DEVELOPER_DIR” is not defined...

これは、symbolicatecrashで使用されているDEVELOPER_DIRが定義されていないために起こるエラーです。
なので、.bashrcなどに以下の設定を追加しておきます。

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

これで再度実行すると無事にシンボル化されたものが出力されると思います。

おまけ

Xcode -> Run時のdSYM生成

Instrumentsを利用してのメモリリークチェックなどで、Xcodeから直にビルド→インストールで実機確認するケースがあると思います。
その際にもdSYMを使ってシンボル化したいケースがあります。

しかし、通常はdSYMが生成されません。
そこで、Build SettingsのDEBUG_INFORMATION_FORMATdwarf-with-dsymにしてビルドを行うと、Productsフォルダの中にdSYMが生成されるのでシンボル化に使うことができるようになります。

具体的には以下のパスに生成されます。

dSYMの場所
/Users/[USER_NAME]/Library/Developer/Xcode/DerivedData/[PROJECT_NAME]-[RANDOM_STRING]/Build/Products/Debug-iphoneos/[APP_NAME].app.dSYM