概要
リリースビルドのiOSアプリのクラッシュログから、クラッシュ理由を探るべく方法を調査したのでそのメモです。
なお、今回の記事を書くにあたって以下の記事を参考にさせていただきました。
準備するもの
解析するにあたっていくつか準備するものがあります。
- クラッシュログ(
.crash
や.ips
ファイル) - Xcode
- dSYMファイル
大まかなフロー
大まかなフローをまず先に示します。
- iPhone/iPadなど端末からクラッシュログを取り出す(
.crash
or.ips
ファイル) - 対象アプリの
.app
ファイルと.dSYM
ファイルを取り出す - 取り出したログを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のアプリ内にあります。以下のパスです。(バージョンによっては異なる位置にあるかもしれません)
$ /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_FORMAT
をdwarf-with-dsym
にしてビルドを行うと、Products
フォルダの中にdSYMが生成されるのでシンボル化に使うことができるようになります。
具体的には以下のパスに生成されます。
/Users/[USER_NAME]/Library/Developer/Xcode/DerivedData/[PROJECT_NAME]-[RANDOM_STRING]/Build/Products/Debug-iphoneos/[APP_NAME].app.dSYM