Unityには現状iOSプラットフォームのみ利用可能な「UnityEngine.CrashReport」と言うAPIが備わってます。
こちらの機能は、恐らくはiOS標準で備わっている?クラッシュレポート機能を利用して実装されているものと思われ1、それを踏まえつつ実際に有効活用する上で必要となってくるであろうレポートの読み方や注意点について調べみたのでメモ序に纏めて行ければと思います。
もし間違いなどあればコメントにて教えて頂けると幸いです! ![]()
(間違いなどあったら随時修正予定...)
記事中で利用するUnity/Xcodeのバージョンについて
- Unityバージョン:
2019.4.01f - Xcodeバージョン:
11.3.1
TL;DR
本題に入っていく前に、幾つかの要点だけ先に纏めておきます。
UnityEngine.CrashReportについて
-
UnityEngine.CrashReportではメモリ不足によるクラッシュは取得できない- iOSでのメモリ不足によるクラッシュは
JetsamEventとして記録されるため、これが影響していると推測(アプリ本体のクラッシュレポートとは違うところに記録される) - この事もあってか、
Cloud Diagnosticsに備わっているクラッシュレポートも同様に記録されない (ドキュメントにも記載有り)
- iOSでのメモリ不足によるクラッシュは
-
レポート内容からは「発生端末, OSバージョン, 日時, etc..」や「例外情報」などは分かる一方、スタックトレースはシンボル化されていないので情報を復元する必要がありそう
- 参考: iOSアプリのクラッシュログから解析する
-
Cloud Diagnosticsのクラッシュレポートはシンボル化がプロセスとして組み込まれているっぽいので、復元された結果が表示される
iOSのクラッシュレポートについて
- 大凡の情報は以下のドキュメントに纏まっている
-
クラッシュレポートは端末に保持されており、
[プライバシー -> 解析および改善 -> 解析データ]から参照可能- 以下のフォーマットで記録されている
- アプリ:
<AppBinaryName>-<DateTime>.ips - JetsamEvent:
JetsamEvent-<DateTime>.ips
- アプリ:
- AirDrop経由でMacに転送することでシンボル化と言った復元・解析が可能
- JetsamEventも同様に記録されているので、こちらを活用することでメモリクラッシュの原因特定に活かせる
- 以下のフォーマットで記録されている
-
「クラッシュした際の例外情報は何を示しているのか?」については以下のドキュメントに纏まっている
- Understanding the Exception Types in a Crash Report
- Xcodeでデバッグ実行中にクラッシュした際の原因調査にも役立つかと思われる
UnityEngine.CrashReportの利用方法
使い方については「Scripting API - CrashReport」に載ってますが、簡単に纏めます。
- Unity iOSのPlayer Settingsで以下の設定を適用
-
Other Settings -> Script Call Optimizationを**「Fast but no Exceptions」**に設定 -
Debugging and crash reporting -> Enable CrashReport APIにチェックを入れる- ※こちらの設定は
Cloud DiagnosticsにあるCrashes and exceptionsにチェックを入れていると強制的に有効になる模様
- ※こちらの設定は
-
設定が適用された状態でビルドを行い、生成された.xcodeprojを開くとClasses/CrashReporter.hにある**ENABLE_CUSTOM_CRASH_REPORTERに1が設定されているかと思われます。**
この状態であれば設定は完了です。
// Enable custom crash reporter to capture crashes. Crash logs will be available to scripts via
// CrashReport API.
# define ENABLE_CUSTOM_CRASH_REPORTER 1
クラッシュさせてレポートを確認
実際にアプリをクラッシュさせてレポートの方を確認していきます。
一先ずは検証用に以下の2パターンのクラッシュを用意しました。
-
Utils.ForceCrashを叩いて強制的にクラッシュ
- 今回はForcedCrashCategory. AccessViolationを渡して無効なメモリアクセス想定でクラッシュさせる
-
メモリ不足を引き起こしてクラッシュ
- 実装としてはクソデカテクスチャを大量に生成して不足させている
こちらをアプリから呼び出し、クラッシュさせた後にUnityEngine.CrashReportのAPIを呼び出して内容を確認していきます。
参考までに今回使用したサンプルコードを載せておきます。
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Diagnostics;
namespace Samples
{
sealed class CrashTest : MonoBehaviour
{
[SerializeField] Button _fatalErrorButton = default;
[SerializeField] Button _outOfMemoryButton = default;
[SerializeField] Button _showLatestReportButton = default;
[SerializeField] Button _showAllReportsButton = default;
[SerializeField] Button _removeAllReportsButton = default;
void Start()
{
_fatalErrorButton.onClick.AddListener(() => { FatalError(); });
_outOfMemoryButton.onClick.AddListener(() => { OutOfMemory(); });
_showLatestReportButton.onClick.AddListener(() => { ShowLatestReportButton(); });
_showAllReportsButton.onClick.AddListener(() => { ShowAllReportsButton(); });
_removeAllReportsButton.onClick.AddListener(() => { RemoveAllReportsButton(); });
}
/// <summary>
/// 強制クラッシュテスト
/// </summary>
void FatalError()
{
// 無効なメモリアクセス想定でクラッシュ
Utils.ForceCrash(ForcedCrashCategory.AccessViolation);
}
/// <summary>
/// メモリ不足テスト
/// </summary>
void OutOfMemory()
{
// クソデカテクスチャを生成しまくってメモリ不足を起こす
for (int i = 0; i < 100000; ++i)
{
var texture = new Texture2D(4096, 4096, TextureFormat.RGBA32, true);
}
}
/// <summary>
/// 最新のクラッシュレポートを表示
/// </summary>
void ShowLatestReportButton()
{
var report = UnityEngine.CrashReport.lastReport;
if (report == null)
{
Debug.Log("not lastReport");
return;
}
// 参照しやすいようにテキストにも書き込み
File.WriteAllText(Application.persistentDataPath + "/latest_report.txt", report.text);
Debug.Log($"[time]:{report.time}\n[text]:{report.text}");
}
/// <summary>
/// 取得可能な全てのレポートを表示
/// </summary>
void ShowAllReportsButton()
{
var reports = CrashReport.reports;
if (reports == null || reports.Length <= 0)
{
Debug.Log("not reports");
return;
}
Debug.Log($">>> [reports.length]:{reports.Length}");
var count = 1;
foreach (var report in reports)
{
File.WriteAllText(Application.persistentDataPath + "/all_reports_" + count + ".txt", report.text);
count++;
Debug.Log($"[time]:{report.time}\n[text]:{report.text}");
Debug.Log("-----------------------------------------------------");
}
}
/// <summary>
/// 取得可能なレポートリストから全てのレポートを削除
/// </summary>
void RemoveAllReportsButton()
{
CrashReport.RemoveAll();
Debug.Log("remove all.");
}
}
}
レポート内容を確認
始めにUtils.ForceCrashを叩くパターンでアプリをクラッシュさせ、その次にメモリ不足パターンで再度アプリをクラッシュさせます。
2回クラッシュさせたらアプリを再起動し、UnityEngine.CrashReport.reportsを実行して結果を出力します。
(上記サンプルコードのCrashTest.ShowAllReportsButton()を実行)
この時に期待する挙動としては「Utils.ForceCrash」と「メモリ不足」による2件分のクラッシュレポートが表示されることですが、実際に取得出来ているレポートはUtils.ForceCrashでクラッシュさせた時の1件だけであり、メモリ不足によるクラッシュレポートは取得できてません。
何故メモリ不足によるレポートを取得できていないのか?
iOSはアプリ側でメモリが圧迫し続けるとシステム(OS)の方から強制終了される仕組みが備わってます。
この際に行われるイベントをJetsam Event2と呼び、アプリ本体側のクラッシュレポートとは違うカテゴリーのJetsamEventと言うJSON形式のレポートに情報が書き込まれます。
参考: Identifying High-Memory Use with Jetsam Event Reports
話を戻してUnityEngine.CrashReport.reportsでメモリ不足によるクラッシュレポートが読み込まれなかった理由についてですが、恐らくはUnityEngine.CrashReportが内部的に参照しているレポートはアプリ本体側のクラッシュレポートのみであり、上記のJetsamEventによるレポートはカテゴリー違いによって参照されなかった為に読み込まれなかったものと推測できます。
ここらの話を理解するにあたっては、iOSが持つクラッシュレポート機能そのものについての予備知識が必要になってくるので、後半にて順を追って整理していきます。
Utils.ForceCrashのレポート内容について
先に今回取得に成功したUtils.ForceCrashのクラッシュレポートについて簡単に触れておきます。
レポートを全て載せると結構な長さとなるので、一先ずは説明に必要な箇所を引用します。3
`Utils.ForceCrash`でのクラッシュレポート(クリックで展開)
Incident Identifier: 6E86B3BE-3333-45F6-BAF1-D15B58C07112
CrashReporter Key: TODO
Hardware Model: iPhone12,5
Process: CrashReportTest [2623]
Path: /private/var/containers/Bundle/Application/*****/CrashReportTest.app/CrashReportTest
Identifier: ********
Version: 0
Code Type: ARM-64
Parent Process: ??? [1]
Date/Time: 2020-06-14 11:53:24 +0000
OS Version: iPhone OS 13.3.1 (17D50)
Report Version: 104
Exception Type: SIGSEGV
Exception Codes: SEGV_ACCERR at 0x117e64000
Crashed Thread: 0
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001b678eefc 0x1b6769000 + 155388
1 ??? 0xf62aeb81b65d84dc 0x0 + 0
2 ??? 0x2340ab810516ac7c 0x0 + 0
3 UnityFramework 0x00000001056ecccc 0x104738000 + 16469196
4 UnityFramework 0x00000001056f4384 0x104738000 + 16499588
5 UnityFramework 0x00000001056ae748 0x104738000 + 16213832
6 UnityFramework 0x00000001055dc810 0x104738000 + 15353872
7 UnityFramework 0x000000010572b240 0x104738000 + 16724544
8 UnityFramework 0x000000010572aa04 0x104738000 + 16722436
9 UnityFramework 0x000000010572a78c 0x104738000 + 16721804
10 UnityFramework 0x000000010478a588 0x104738000 + 337288
11 UnityFramework 0x00000001055bb480 0x104738000 + 15217792
12 UnityFramework 0x00000001050d4f70 0x104738000 + 10080112
13 UnityFramework 0x00000001050e2fc4 0x104738000 + 10137540
14 UnityFramework 0x00000001050f033c 0x104738000 + 10191676
15 UnityFramework 0x0000000104cc3608 0x104738000 + 5813768
16 UnityFramework 0x0000000104cc34d8 0x104738000 + 5813464
17 UnityFramework 0x0000000104ea07b8 0x104738000 + 7767992
18 UnityFramework 0x0000000104e9752c 0x104738000 + 7730476
19 UnityFramework 0x0000000104e97588 0x104738000 + 7730568
20 UnityFramework 0x0000000104e97838 0x104738000 + 7731256
21 UnityFramework 0x00000001053b9898 0x104738000 + 13113496
22 UnityFramework 0x0000000104751984 0x104738000 + 104836
23 UnityFramework 0x0000000104751860 0x104738000 + 104544
24 QuartzCore 0x00000001bd429f24 0x1bd418000 + 73508
25 ??? 0xcb69e301bd4f8608 0x0 + 0
26 ??? 0x72166981b68f5dac 0x0 + 0
27 ??? 0x99715701b69207c4 0x0 + 0
28 ??? 0xf6195101b691fe90 0x0 + 0
29 ??? 0xc0245f01b691aac8 0x0 + 0
30 ??? 0x4040bd01b6919f40 0x0 + 0
31 ??? 0x69526c01c0baa534 0x0 + 0
32 ??? 0x7a61c901baaa5580 0x0 + 0
33 ??? 0xeb6e50810475031c 0x0 + 0
34 CrashReportTest 0x000000010426be1c 0x104264000 + 32284
35 libdyld.dylib 0x00000001b6798e18 0x1b6798000 + 3608
Thread 1:
0 libsystem_kernel.dylib 0x00000001b678fab4 0x1b6769000 + 158388
1 ??? 0x814efc81b66b59e8 0x0 + 0
Thread 2:
0 libsystem_kernel.dylib 0x00000001b678fab4 0x1b6769000 + 158388
1 ??? 0x7c476701b66b59e8 0x0 + 0
Thread 3
// ※ここから上記の様な感じのスタックトレースが[Thread 39]分まで載っている。長いので省略。
Thread 0 crashed with ARM-64 Thread State:
pc: 0x00000001b678eefc fp: 0x000000016bb98e90 sp: 0x000000016bb98e70 x0: 0x0000000000000000
x1: 0x0000000000000000 x2: 0x00000000000120a8 x3: 0x000000013b004e3c x4: 0x000000016bb989f0
x5: 0x000000016bb98e40 x6: 0x000000000000000a x7: 0x000000000000004e x8: 0x00000000000005b9
x9: 0x979d972d475ea69c x10: 0x00000001ffe10990 x11: 0x0000000000000002 x12: 0x00000000fffffffd
x13: 0x0000010000000000 x14: 0x0000000000000000 x15: 0x0000000000000000 x16: 0x0000000000000148
x17: 0x0000000000000001 x18: 0x0000000000000000 x19: 0x000000000000000b x20: 0x0000000000000407
x21: 0x00000001046c5920 x22: 0x00000001055d3590 x23: 0x0000000000000001 x24: 0x000000016bb98ef8
x25: 0x0000000000000000 x26: 0x00000001057251c8 x27: 0x0000000000000001 x28: 0x000000016bb98fb8
lr: 0x00000001b66ae8b8 cpsr: 0x0000000040000000
// プロセスにロードされた各バイナリイメージのビルドUUID。こちらも全部乗せると長いので省略。
Binary Images:
0x104264000 - 0x10426bfff +CrashReportTest arm64 <d18f18a360ba30be848a2d978859bca9> /private/var/containers/Bundle/Application/75C3EDB6-2DCE-4839-B56A-D7CD34B855FA/CrashReportTest.app/CrashReportTest
0x104738000 - 0x105e5bfff UnityFramework arm64 <24b073e8659a305cb641b1601462faf6> /private/var/containers/Bundle/Application/75C3EDB6-2DCE-4839-B56A-D7CD34B855FA/CrashReportTest.app/Frameworks/UnityFramework.framework/UnityFramework
0x107ad8000 - 0x107ae3fff libobjc-trampolines.dylib arm64-unknown <a782ecde318c3379b33e7d204926c9c9> /usr/lib/libobjc-trampolines.dylib
0x1b657c000 - 0x1b6592fff libsystem_trace.dylib arm64-unknown <1177e8a367aa3c8cb5605bcc40419d54> /usr/lib/system/libsystem_trace.dylib
...
得られる情報とシンボル化について
レポートを見ると、「どの例外が投げられたのか?」と言った情報は分かる一方、スレッド別のスタックトレースにはアドレスと思われる情報しか載っておらず、「どのクラス・メソッドで実行されたのか?」と言った情報が追えません。
これについてはシンボル化と呼ばれる作業を行い、情報を復元する必要があります。(詳細については後述)
但しシンボル化自体はXcodeが必要となる作業という認識のため、もしランタイム上から取得して何かを行うとしたら「発生日時・端末/OS情報」や「例外情報」と言った読める範囲の情報をアプリ上のDebuggerに表示したり、Slackにメッセージを投げたりすると言ったことは出来るかもしれません。
Exception Type: SIGSEGV
Exception Codes: SEGV_ACCERR at 0x117e64000
Crashed Thread: 0
Thread 0 Crashed:
0 libsystem_kernel.dylib 0x00000001b678eefc 0x1b6769000 + 155388
1 ??? 0xf62aeb81b65d84dc 0x0 + 0
2 ??? 0x2340ab810516ac7c 0x0 + 0
3 UnityFramework 0x00000001056ecccc 0x104738000 + 16469196
4 UnityFramework 0x00000001056f4384 0x104738000 + 16499588
5 UnityFramework 0x00000001056ae748 0x104738000 + 16213832
6 UnityFramework 0x00000001055dc810 0x104738000 + 15353872
7 UnityFramework 0x000000010572b240 0x104738000 + 16724544
8 UnityFramework 0x000000010572aa04 0x104738000 + 16722436
9 UnityFramework 0x000000010572a78c 0x104738000 + 16721804
10 UnityFramework 0x000000010478a588 0x104738000 + 337288
11 UnityFramework 0x00000001055bb480 0x104738000 + 15217792
12 UnityFramework 0x00000001050d4f70 0x104738000 + 10080112
13 UnityFramework 0x00000001050e2fc4 0x104738000 + 10137540
14 UnityFramework 0x00000001050f033c 0x104738000 + 10191676
...
補足: Cloud Diagnosticsのクラッシュレポートについて
Cloud Diagnosticsと言うサービスを利用すると、簡単にクラッシュレポートを集計し、ダッシュボード上から確認することが出来るようになります。(保持件数や保持期間などで違いがあるが、Unity Personalでも利用可能)
この記事中では導入に関しての解説は省くので、詳細については以下のドキュメントを御覧ください。
シンボル化されたスタックトレースを確認可能
ポイントとしては、UnityEngine.CrashReportでは対応されていないスタックトレースのシンボル化もプロセスに含まれているみたいなので、以下の様に自動で復元されたスタックトレースを確認することが出来るみたいです。
※ただ、デバッグシンボル(.dSYM)のアップロードプロセス等でミスが起きた際にシンボル化に失敗する時もあるらしく?手動でシンボルをアップロードする仕組みも用意されているので、覚えてくと問題が起きた際に幸せになれるかも。
参考: Crashes and Exceptions -> Symbols
メモリ不足によるクラッシュ(JetsamEvent)は集計されないので注意
メモリ不足によるクラッシュ(JetsamEvent)は集計されないみたいなので、その点は注意する必要がありそうです。
(恐らくはUnityEngine.CrashReportと同様にJetsamEvent自体が別なクラッシュレポートとして保持されるので取得されない説がある)
ドキュメントにも以下のように記載されてます。
Note that iOS crash reporting cannot capture crashes caused by the application running out of memory and then being closed by the operating system.
メタデータについて
ダッシュボードからは「最後に確認した発生時刻」「Occurrences」「Stack Trace」「端末情報」と言った様々な情報を確認することが出来ますが、場合によっては参照したいのに表示されていないデータなどがあるかもしれません。
手元で確認した感じだとiOSのクラッシュレポートにある例外情報がどこにも載っていないように見受けられました。
(※プランによって表示内容を変更したり参照できる項目が増えたりするので、Plus/Proならひょっとしたら参照できるかもしれないが...(未調査))
こういったデータについては、ダッシュボード上からJSON形式でレポートを取得することで参照できるかもしれません。
例としてiOSの例外情報は以下のような形でJSONに載っていました。
※JSON自体はフォーマットされていないので、見やすいように手動でフォーマット掛けてます。
iOSのクラッシュレポートについて
ここからは機能を活用していく上での予備知識として、iOSのクラッシュレポートについて簡単に纏めていきます。
とは言えども、解説する内容の大体は以下の公式ドキュメントに纏まっています。
記事中では幾つかの要点を絞って解説していきますが、公式ドキュメントの方も一読してみることをオススメします。
- Diagnosing Issues Using Crash Reports and Device Logs
- Understanding and Analyzing Application Crash Reports
クラッシュレポートの取得
クラッシュレポートはiOS端末上に保持されており、端末上からAirDrop経由などでMacに転送することが出来ます。
(AppSotreやTestFlight経由で集計することも可能な模様)
参考: Acquiring Crash Reports and Diagnostic Logs
iOS端末上のクラッシュレポートの参照と取得
クラッシュレポートはiOSの設定にある以下のメニューから参照/シェアなどが可能です。
日: [プライバシー -> 解析および改善 -> 解析データ]
英: [Privacy -> Analytics & Improvements -> Analytics Data]
メニューを開くと<AppBinaryName>-<DateTime>のフォーマットでクラッシュレポートがアルファベット順に並んでいるかと思います。
今回サンプルで用意したCrashReportTestは以下のように並んでおり、この.ipsファイルがクラッシュレポートとなります。
ちなみにメモリの圧迫によるクラッシュ(JetsamEvent)は以下のようにJetsamEvent-<DateTime>のフォーマットで記録されます。JetsamEventについてはこちらの章で解説していきます。
内容の確認
リストから確認したいレポートをタップすることで開けます。
試しにCrashreportTest-<最新の日付>.ipsを開くと以下のようになっており、内容自体はUnityEngine.CrashReportから読み込める内容と大凡一致していることが確認出来ます。4
後述のシンボル化を行うにあたってはこちらが必要となるので、解析するのであれば右上のシェアボタンからAirDrop等でMacに転送しておくと良いかもしれません。
JetsamEventについて
参考: Identifying High-Memory Use with Jetsam Event Reports
iOSはアプリ側でメモリが圧迫し続けるとシステム(OS)の方から強制終了される仕組みが備わっており、この際に行われるイベントをJetsamEventと呼ぶみたいです。
この時に書き込まれるレポートとしてはアプリ本体側のレポート(<AppBinaryName>-<DateTime>のフォーマットの方)ではなく、JetsamEvent-<DateTime>と言うフォーマットのレポートに詳細が記録されます。
JetsamEventのレポート自体はJSON形式且つシンボル化が不要な形で記録されており、reasonと言うKeyに記録されているValueの内容から原因を特定することが出来ます。
「メモリ不足」特定の例
reasonの一例を挙げると、Valueに**per-process-limitと言う文字列が入っている場合には、ドキュメントには「システムがすべてのアプリに課した常駐メモリの制限を超えました。この制限を超えると、プロセスは終了の対象となります」とあり、大凡メモリ不足でクラッシュしたと言うことの特定に繋がります。**
実際にCrashReportTestにてメモリ不足でクラッシュさせた後に、最新のJetsamEventのレポートを開いてper-process-limitで検索をかけると以下のようなブロックが引っかかります。
name(プロセス名)には想定通り、クラッシュしたCrashReportTestの文字列が入っています。
{
"uuid" : "d18f18a3-60ba-30be-848a-2d978859bca9",
"states" : [
"frontmost"
],
"killDelta" : 8259,
"genCount" : 0,
"age" : 106579063,
"purgeable" : 0,
"fds" : 50,
"coalition" : 1800,
"rpages" : 134272,
"reason" : "per-process-limit",
"pid" : 2922,
"cpuTime" : 1.271835,
"name" : "CrashReportTest",
"lifetimeMax" : 134272
},
これ以外にも得られる情報はあるので、詳細はドキュメントを御覧ください。
余談: Jetsamの名前の由来?について
調べていたらKeijiro Takahashiさんの過去ツイを見つけました。なるほど。
"Jetsam"は船舶の難破を防ぐために投棄された積荷/備品の事。iOSでメモリ不足の時にプロセスを殺す仕組みも"jetsam"なんですが、ここからきてるのね。 http://t.co/TaD8rv2oYx
— Keijiro Takahashi (@_kzr) April 10, 2013
シンボル化について
参考: Adding Identifiable Symbol Names to a Crash Report
UnityEngine.CrashReportから読み込んだレポートや、端末に記録されているクラッシュレポートのスタックトレースはそのままの形だと特定が困難です。
こちらを元の「クラス名・メソッド名」に戻すにはシンボル化を行う必要があります。
シンボル化については以下の記事がわかりやすく纏まっており、この記事中で特筆して書き足す要素は無かったので、内容については以下の記事を御覧ください。
クラッシュレポートの読み方について
全部丁寧に纏め上げると長くなりそう(それだけで1記事書けそう)なので、ここではドキュメントの紹介レベルで解説を済ませます。
(ただ、内容自体は大切なところなので、どこかで纏め直したい。。)
クラッシュレポートの構造
クラッシュレポートの構造(どういったセクションが存在して何が記載されているのか?)については以下のドキュメントを御覧下さい。
例えば「ヘッダ情報とそのフィールドについて」や「例外情報とそのフィールドについて」などが記載されます。
手元にあるクラッシュレポートと合わせて確認していくと理解しやすいかもしれません。
一般的なクラッシュ原因の特定
表題の件に関しては、ほぼ名の通りですが以下のドキュメントを御覧ください。
内容としてはレポート中にある例外情報とトレース情報を元に、**「こう言うパターンの情報が載っていたら、この原因が疑える」**と言った情報が載ってます。
レポートの分析
以下のドキュメントは「クラッシュレポートの読み方」と言うよりは「それを活用した分析手法(問題に対する考え方)」が纏まってます。
例外情報の理解
クラッシュレポートの例外情報セクションには以下のような例外の情報が記載されてます。
Exception Type: SIGSEGV
Exception Codes: SEGV_ACCERR at 0x117e64000
Crashed Thread: 0
以下のドキュメントではこれらが主に何を意味するのか?について記載されています。
こちらの情報はクラッシュレポートの理解以外にも、Xcodeでデバッグ実行中にクラッシュした際の原因調査にも役立つと思われるので、一読しておくことを個人的にはオススメします。
Understanding the Exception Types in a Crash Report
参考/関連リンク
Unity
iOS
- Understanding and Analyzing Application Crash Reports
-
Diagnosing Issues Using Crash Reports and Device Logs
- Essentials
- Crash Reports
- Device Logs
- iOSアプリのクラッシュログから解析する
- iOS における malloc の挙動について


