【UE5】Android環境でハングアップしたときの調査方法

今回はUnreal Engineでビルドした APK ファイルがAndroid環境でクラッシュしたときの調査方法について書きます。

ログの回収方法

クラッシュの原因調査方法として、まずは Unreal Engineが出力しているログを回収します。

基本的には以下のコマンドを使って ADB 経由でログをコピーします。

adb pull /sdcard/Android/data/<pkg>/files/UnrealGame/<Project>/Saved/Logs .

Information

ADB経由で取り出すため、PCとAndroid端末がUSBケーブルなどで接続されている必要があります。

ログのパスを調べる方法

ログのパスである <pkg> (パッケージ) と、<Project> (プロジェクト名) は adb shell コマンドで調べると良さそうです。

例えば「adb shell ls sdcard/Android/data」でインストールされているパッケージ名が取得できます

プロジェクト設定に記載されているものと同じなので、わざわざコマンドで調べる必要はないかもしれませんが、この一覧から探すこともできます。

次にプロジェクト名を確認する必要があるので「adb shell ls sdcard/Android/[パッケージ名]/files/UnrealGame」を実行。

これで目的のログファイルにたどり着ければそのパスを使ってログを取り出せます。(場合によって [プロジェクト名]/[プロジェクト名] )というパスが作られることもあるので、直接ストレージを確認すると確実です)

バグレポートの取得

次にバグレポートの取得方法についてです。Androidでクラッシュが発生すると「bugreport.zip」というファイルでバグ情報が取り出せます。

こちらはログ取得よりも簡単で、以下のコマンドを実行するだけです。

adb bugreport bugreport.zip

これを実行すると、現在のフォルダに "bugreport.zip" がコピーされます。

クラッシュの原因とコールスタックを確認する

クラッシュ情報は「bugreport/FS/data/tombstones」というパスにファイルがいくつか配置されています。

ここには、これまでに発生したクラッシュ情報すべてを保持しています。そのため基本的には最新のファイルを見ることになると思います。

最新の「tombstone_##」の拡張子がないファイルをテキストエディタで開きます。

直接的に重要なのは「Cause」という部分です。

Cause: null pointer dereference
    x0  000000741e6abe30  x1  0000000000000000  x2  0000000000000001  x3  0000000000000004
    x4  0000007409ad6c00  x5  0000000000000002  x6  0000000000000000  x7  000000741e4c55c0
    x8  0000000000000001  x9  0000000040040008  x10 b4000075660245c8  x11 0000000000000000
    x12 000000002f46b850  x13 000000000000007f  x14 0000000000000000  x15 00000000000083d5
    x16 00000074ec899bc0  x17 000000761b32dd00  x18 00000000fffffe80  x19 000000741e6abe30
    x20 00000074f2285d00  x21 00000074f2285d00  x22 00000074f2285d00  x23 0000000000000030
    x24 0000000000000050  x25 00000074ecc89bd8  x26 0000000000000100  x27 000000742f499160
    x28 0000000000001400  x29 00000074f227f500
    lr  00000074e89a6c40  sp  00000074f227f470  pc  00000074e89a72c0  pst 0000000000001000

例えば上記ログであれば、"null pointer dereference" となっているので、NULL参照がエラーの原因となります。

もう1つ重要なのがコールスタックです。

22 total frames
backtrace:
      #00 pc 00000000140752c0  ==/lib/arm64/libUnreal.so (ACPP_GrabActorBase::UpdateRotation(UMeshComponent*, float)+40) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #01 pc 0000000014074c3c  ==/lib/arm64/libUnreal.so (ACPP_GrabActorBase::UpdateTimerTick()+104) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #02 pc 0000000014078ac4  ==/lib/arm64/libUnreal.so (TBaseUObjectMethodDelegateInstance<false, ACPP_GrabActorBase, void (), FDefaultDelegateUserPolicy>::ExecuteIfSafe() const+120) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #03 pc 0000000014046c58  ==/lib/arm64/libUnreal.so (ACPP_GameTimer::_ExecuteTimerEvent(FTimerEvent&)+220) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #04 pc 000000001404591c  ==/lib/arm64/libUnreal.so (ACPP_GameTimer::_ProcessTimerEvents(float)+288) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #05 pc 0000000010bafa7c  ==/lib/arm64/libUnreal.so (FActorTickFunction::ExecuteTick(float, ELevelTick, ENamedThreads::Type, TRefCountPtr<FBaseGraphTask> const&)+148) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #06 pc 0000000012225cb4  ==/lib/arm64/libUnreal.so (FTickFunctionTask::DoTask(ENamedThreads::Type, TRefCountPtr<FBaseGraphTask> const&)+288) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #07 pc 0000000012225b40 ==/lib/arm64/libUnreal.so (TGraphTask<FTickFunctionTask>::ExecuteTask()+60) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #08 pc 000000000b0f1e18  ==/lib/arm64/libUnreal.so (UE::Tasks::Private::FTaskBase::TryExecuteTask()+348) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #09 pc 000000000b0fc1a4  ==/lib/arm64/libUnreal.so (FNamedTaskThread::ProcessTasksNamedThread(int, bool)+1988) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #10 pc 000000000b0fb078  ==/lib/arm64/libUnreal.so (FNamedTaskThread::ProcessTasksUntilQuit(int)+244) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #11 pc 000000000b0fa8ec  ==/lib/arm64/libUnreal.so (FTaskGraphCompatibilityImplementation::WaitUntilTasksComplete(TArray<TRefCountPtr<FBaseGraphTask>, TSizedInlineAllocator<4u, 32, TSizedDefaultAllocator<32> > > const&, ENamedThreads::Type)+1868) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #12 pc 0000000012221330 ==/lib/arm64/libUnreal.so (FTickTaskSequencer::ReleaseTickGroup(ETickingGroup, bool)+2064) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #13 pc 000000001221ce98  ==/lib/arm64/libUnreal.so (FTickTaskManager::RunTickGroup(ETickingGroup, bool)+176) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #14 pc 000000001171dee8 ==/lib/arm64/libUnreal.so (UWorld::Tick(ELevelTick, float)+4784) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #15 pc 0000000011424a7c  ==/lib/arm64/libUnreal.so (UGameEngine::Tick(float, bool)+1692) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #16 pc 000000001293994c  ==/lib/arm64/libUnreal.so (FEngineLoop::Tick()+12468) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #17 pc 00000000129351b0 ==/lib/arm64/libUnreal.so (AndroidMain(android_app*)+4272) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #18 pc 000000001293d994 ==/lib/arm64/libUnreal.so (android_main+200) (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #19 pc 000000001295e38c  ==/lib/arm64/libUnreal.so (BuildId: 47ad9d8b85d0a9586137f91ee040952f1e6277d1)
      #20 pc 00000000000fd0ec  /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+204) (BuildId: dc4c39cee8280a6712ff2d9e4968d431)
      #21 pc 0000000000094fb0  /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: dc4c39cee8280a6712ff2d9e4968d431)

上記ログであれば「ACPP_GrabActorBase::UpdateRotation」で何らかの NULL参照が発生したこととなります。

ただ、このバグレポートにはシンボル情報がないためわかるのは呼び出し関数名のみです。

シンボルと tombstone を連携して場所を特定する方法

Androidビルド時には、通常 "Binaries/Android/<Project>_Symbols_v1/<Project>arm64" に "libUnreal.so" というシンボルファイルが作成されます。

"libUnreal.so" ファイルが見つかれば以下のコマンドでクラッシュダンプを書き出せます。

:: NDK内の ndk-stack.cmd
set TOOL=%NDK_ROOT%\ndk-stack.cmd
:: libUnreal.soがあるフォルダ
set SYM_DIR=[プロジェクトのパス]\Binaries\Android\<Project>_Symbols_v1\<Project>arm64
:: bugreport内のtombstoneファイル。拡張子がない tombstone_## を指定.
set TOMBSTONE=bugreport\FS\data\tombstones\tombstone_##

:: dump.txtにクラッシュダンプを書き出し.
%TOOL% -sym %SYM_DIR% -dump %TOMBSTONE% >> dump.txt

Information

ただ得られるのは「CPP_GrabActorBase.cpp:247:38」といった行番号までで、ビルドしたときのリビジョンのソースコードを参照する必要があります。

おしまい

Androidでのクラッシュが発生したときの調査方法でした。

今回はログ取得と例外の種類、コールスタックを調べる方法のみですが、何かもっと良い方法が見つかったら別途記事を書こうと思っています。

以上、Unreal EngineでのAndroidのデバッグに役立てれば幸いです。

\ 最新情報をチェック /