iPhoneでアプリがクラッシュすると、クラッシュログが生成されます。
クラッシュログは、iPhoneの設定アプリ -> 「プライバシー」 -> 「解析」→「解析データ」から取得できます。
中身はこんな感じですね
Date/Time: 2017-04-30 19:31:49.0245 -0500
Launch Time: 2017-04-30 19:31:28.9773 -0500
OS Version: iPhone OS 10.3.1 (14E304)
Report Version: 104
Exception Type: EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d
Triggered by Thread: 0
Filtered syslog:
None found
Thread 0 name: Dispatch queue: NSPersistentStoreCoordinator 0x174270780
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001881fb278 0x1881fa000 + 4728
1 libdispatch.dylib 0x00000001880e96b4 0x1880d5000 + 83636
2 libdispatch.dylib 0x00000001880e8c5c 0x1880d5000 + 80988
3 CoreData 0x000000018b54ed00 0x18b4da000 + 478464
4 CoreData 0x000000018b53af14 0x18b4da000 + 397076
:
クラッシュした時のスタックトレースが出力されていますが、モジュール名とアドレスが表示されているだけで、このままでは何が起きたのかわかりません。
symbolicatecrashを使う
そこで、symbolicatecrashというコマンドを使って、ログをシンボリケートしてもう少しわかりやすくします。
symbolicatecrashは、Xcodeについてくるツールなのですが、Xcodeのバージョンによって、置いてある場所が違ったりします。Xcode8の場合、以下にあります。
/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash
symbolicateを実行するには、クラッシュログの他に、dSYMファイルと呼ばれる、デバッグ用のシンボルを保持するファイルが必要になります。
dSYMファイルは、アプリケーションバッケージの中にあるので、オーガナイザーから該当のアプリのバイナリを右クリックして、「Show in Finder」を選びファインダーに表示し、
xcarchiveファイルを右クリックして、「パッケージの内容を表示」を選び、表示されたdSYMsディレクトリをそのまま、クラッシュログを置いたディレクトリへコピーしましよう。
この時注意しないといけないのは、クラッシュログを生成したアプリと同じバージョンのバイナリからdSYMSファイルを抽出する必要があるということです。
symbolicatecrashに実行パスを通すかフルパスで指定してもいいですが、クラッシュログのあるディレクトリーへコビーしてきたあと、ターミナルで以下のように指定して実行できます。
$ ./symbolicatecrash crash.log dSYMs/ > symbolicated.crash
引数で渡しているcrash.logがクラッシュログ、dSYMs/が先程抽出したdSYMsディレクトクリです。
もし、
Error: "DEVELOPER_DIR" is not defined at ./symbolicatecrash line 69.
といったエラーが出力される場合は、
export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer/
と、シェル変数に値を設定してから再実行します。
実行が終わると、シンボリケートされたファイルが生成されます。(上の場合は、symbolicated.crash)
中身を見ると次のようになっています。
Thread 0 name: Dispatch queue: NSPersistentStoreCoordinator 0x174270780
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001881fb278 semaphore_timedwait_trap + 8
1 libdispatch.dylib 0x00000001880e96b4 _os_semaphore_timedwait + 92
2 libdispatch.dylib 0x00000001880e8c5c _dispatch_semaphore_wait_slow + 72
3 CoreData 0x000000018b54ed00 -[NSXPCStoreConnection sendMessage:fromContext:store:error:] + 272
4 CoreData 0x000000018b53af14 -[NSXPCStore sendMessage:fromContext:interrupts:error:] + 236
: :
31 Photos 0x000000019501b5c8 -[PHQuery executeQuery] + 52
32 Photos 0x0000000194fe93d4 +[PHAsset fetchAssetsInAssetCollection:options:] + 692
33 BatchResizer2 0x000000010011deac PhotoAlbumViewController.collectAlbums() -> () (PhotoAlbumViewController.swift:248)
34 BatchResizer2 0x000000010011e4f0 PhotoAlbumViewController.applicationDidBecomeActive(Notification) -> () (PhotoAlbumViewController.swift:358)
35 BatchResizer2 0x000000010011f50c @objc PhotoAlbumViewController.applicationDidEnterBackground(Notification) -> () + 68
36 CoreFoundation 0x00000001891b95ec __CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 20
37 CoreFoundation 0x00000001891b8d00 _CFXRegistrationPost + 400
38 CoreFoundation 0x00000001891b8a7c ___CFXNotificationPost_block_invoke + 60
: :
スタックトレースのアドレスが、該当メソッド名に置き換わり、アプリのソースファイル名と行番号も出力されています。これで原因調査がやりやすくなりました。
もっと簡単なやり方
実は、symbolicatecrashを使わなくても、クラッシュログを簡単にシンボリケートする方法があります。
Xcodeにデバイスを繋いだ状態で、「Window」->「Devices」を開き、デバイスを選択して、「View Device Logs」をクリックします。
表示されたデバイスログ画面にクラッシュログをドラッグ&ドロップするとログが追加されます。追加した直後はまだシンボリケートされていませんが、暫くすると自動的にシンボリケートされます。
暫くすると...
シンボリケートされたログが表示されました。
こちらのやり方の方が簡単ですね。