iOS アプリのクラッシュレポートを取得する : Objective-C プログラミング

PROGRAM


iOS アプリのクラッシュレポートを取得する

iPhone や iPad などで起動中のアプリが落ちたとき、その状況を記した「クラッシュレポート」が記録されています。

制作者にとってはアプリの不具合の手がかりを知ることのできる貴重な手がかりなので、これを添えて不具合をお知らせすると大変喜ばれたりします。

原因を特定してもらえる可能性もぐっと高まるので、これを送ってあげることは、自分にとってもメリットです。

 

そんなクラッシュレポートですけど、iTunes で同期を取れば、パソコンにそれが自動で転送されます。

転送場所は次の通りです。

 

Windows 8 の場合

"%HOMEDRIVE%\Users\%USERNAME%\AppData\Roaming\Apple Computer\Logs\CrashReporter\MobileDevice"

 

Mac OS X 10.7 の場合

"~/Library/Logs/CrashReporter/MobileDevice/"

 

このフォルダに、同期されたデバイスのクラッシュレポートが、デバイス名前でフォルダ分けして保存されています。

この中の拡張子が ".crash" のファイルに、テキスト形式でクラッシュレポートが記録されています。

どのファイルがどのアプリのレポートかはファイル名から類推するしかないのですけど、たとえば AppBank Games の ダンジョンズ & ゴルフ の場合は "dandg_2013-05-10-000939_iPhone-5.crash" というように、なんとなく判るファイル名になっている場合が多いです。

なお、環境によってはログファイルが "*.ips" という名前で保存されていることがあるようです。

 

プログラムからはクラッシュレポートファイルを取得できない様子

iOS アプリ内からプログラムを使って、デバイス内に記録されているクラッシュレポートファイルを取得することはできなそうでした。

デバイス内にも "/Library/Logs" ディレクトリとか、"/var/mobile/Library" とか、"/var/mobile/Applications/{SSID}/Library" とか、"/var/logs" とか、"/var/log" とか、気になるフォルダはあったのですけど、検索しても ".crash" ファイルは見つかりませんでした。

 

アプリ内からクラッシュレポートを取得する代わりになる方法としては、次のように AppDelegate で Objective-C 例外をキャッチして、その例外のコールスタックを取得する方法が、プログラムの仕方によってはもしかすると役に立つかもしれません。

// Objective-C 例外をキャッチしなかった場合に呼び出す関数です。

void EzUncaughtExceptionHandler(NSException* exception)

{

NSLog(@"%@", exception.callStackSymbols);

}

 

int main(int argc, char *argv[])

{

@autoreleasepool

{

// Objective-C 例外をキャッチしなかった場合に関数が呼び出されるようにします。

NSSetUncaughtExceptionHandler(EzUncaughtExceptionHandler);

 

return UIApplicationMain(argc, argv, nil, NSStringFromClass([EzLBAppDelegate class]));

}

}

例外が持つコールスタックで取得できる情報はクラッシュレポートと比べて限られるので、どれほど有益な情報が取得できるかは判りません。

とりあえず NSException の callStackSymbols メソッドでは、次のような情報を NSArray 型で取得できました。

(

0 CoreFoundation 0x339293ff <redacted> + 186

1 libobjc.A.dylib 0x3b7b3963 objc_exception_throw + 30

2 EzLibraryBrowser 0x0001bd7b -[EzLBViewController EzCrash] + 102

3 EzLibraryBrowser 0x0001bca7 -[EzLBViewController clash:] + 134

4 UIKit 0x35823087 <redacted> + 70

5 UIKit 0x35823111 <redacted> + 120

6 UIKit 0x35823087 <redacted> + 70

7 UIKit 0x3582303b <redacted> + 30

8 UIKit 0x35823015 <redacted> + 44

9 UIKit 0x358228cb <redacted> + 502

10 UIKit 0x35822db9 <redacted> + 488

11 UIKit 0x3574b5f9 <redacted> + 524

12 UIKit 0x357388e1 <redacted> + 380

13 UIKit 0x357381ef <redacted> + 6198

14 GraphicsServices 0x3742c5f7 <redacted> + 590

15 GraphicsServices 0x3742c227 <redacted> + 34

16 CoreFoundation 0x338fe3e7 <redacted> + 34

17 CoreFoundation 0x338fe38b <redacted> + 138

18 CoreFoundation 0x338fd20f <redacted> + 1382

19 CoreFoundation 0x3387023d CFRunLoopRunSpecific + 356

20 CoreFoundation 0x338700c9 CFRunLoopRunInMode + 104

21 GraphicsServices 0x3742b33b GSEventRunModal + 74

22 UIKit 0x3578c2b9 UIApplicationMain + 1120

23 EzLibraryBrowser 0x0001a3f9 main + 188

24 libdyld.dylib 0x3bbe0b20 <redacted> + 0

)

NSThread にも +callStackSymbols メソッドが用意されているので、例外発生時でなくても、どのような経緯でメソッドが呼ばれたかを知ることもできます。

NSThread の callStackSymbols で取得できるのは、例外発生時点ではなく、現時点でのスタックなので、例外がキャッチされずに最終的に呼び出された関数内で実行しても、あまり有用な情報は得られないかもしれません。

 

ちなみに NSSetUncaughtExceptionHandler 関数を使って例外をキャッチする場合、どのスレッドで例外が発生してもキャッチできるので便利ですけど、Objective-C++ で std::set_terminate 関数を使って C++ 例外をキャッチしていると呼び出されないので注意が必要です。

メインスレッドで発生した Objective-C 例外だけのキャッチでよければ、main.m の UIApplicationMain 関数の呼び出しの前後を @try - @catch で括ってあげる方法もあります。

[ もどる ]