12
9

More than 5 years have passed since last update.

[UE4] エラーデバッグとプログラムエラー

Last updated at Posted at 2018-12-24

1. はじめに

 UE4にはプログラム上の様々なエラーを意図的に引き起こすためのDebugコマンドが用意されています。
このコマンドを利用することで意図的なエラーチェックをすることも可能ですが、プログラミングのミスやEngine内部での不具合がどういったことが要因で発生しているか、というプログラムエラーのケースを学ぶこともできます。
 本記事では4.21.1にて確認しています。

2. エラーと用語

 この項以降で記載している内容や用語について説明しておきます。

check/fatal/ensure など
UE4が提供するプログラムの妥当性をチェックするためのマクロを利用したエラーを示します。殆どの内容は、アサーションにて詳細に説明があります。checkなど一部はShippingにおいても動作を継続しますが、fatalなど一部ではクラッシュが発生してアプリケーションは終了します。

GPF
一般保護違反。プログラム上でCPUの仕様に反する例外にないエラー(つまり例外)が発生することを意味します。この場合クラッシュが発生してアプリケーションは終了します。

ヒッチ
UE4ではアプリケーションが瞬間的または、継続的に停止する際にこの用語が使用されることが多いです。
例えば膨大なレベルをロードする際などにはスレッドが断定的に停止して画面が停止しているように見る状態となります。

ハングアップ
ヒッチと似ていますが、応答が帰らずに画面がフリーズしたままのようなケースで使用されることが多いです。
この場合はアプリケーションを強制的に終了することはありません。

無限再起/無限ループ
プログラム上で特定の処理から抜けられずに繰り返し実行するようなケースです。
この場合はアプリケーションを強制的に終了することはありません。

OOM
Out Of Memoryの略で使用できるメモリが不足した状態です。
この場合クラッシュが発生してアプリケーションは終了します。

3. Console Command

 Shipping以外のビルド構成(Debug/Development/Test)で利用可能です。エラー種別によっては類似のコードもあるため、いくつかのケースにおいてのみ、そのコードを記載しています。詳しい情報を見たい方は、UnrealEngine.cpp の UEngine::PerformErrorをご参考下さい。コードを見て「なぜエラーとなるのか」を考えてみることもエラー回避のために役に立ちます。

Debug RenderCrash

RenderThreadでFatalエラーを発生させるためのRenderCommandを発行します
クラッシュが発生してアプリケーションを終了します

ENQUEUE_UNIQUE_RENDER_COMMAND(CauseRenderThreadCrash, { UE_LOG(LogEngine, Warning, TEXT("Printed warning to log.")); SetCrashType(ECrashType::Debug); UE_LOG(LogEngine, Fatal, TEXT("Crashing the renderthread at your request")); });

Debug RenderCheck

RenderThreadでcheckエラーを発生させるためのRenderCommandを発行します
クラッシュが発生してアプリケーションを終了します

Debug RenderGPF

RenderThreadでGPFエラーを発生させるためのRenderCommandを発行します
クラッシュが発生してアプリケーションを終了します

ENQUEUE_UNIQUE_RENDER_COMMAND(CauseRenderThreadCrash, { UE_LOG(LogEngine, Warning, TEXT("Printed warning to log.")); SetCrashType(ECrashType::Debug); *(int32 *)3 = 123; });

Debug RenderFatal

RenderThreadでLowLevelFatalErrorを発生させるためのRenderCommandを発行します
クラッシュが発生してアプリケーションを終了します

Debug RenderEnsure

RenderThreadでensureエラーを発生させるためのRenderCommandを発行します
クラッシュが発生してアプリケーションを終了します

Debug ThreadCrash

Debug用のThreadでFatalエラーを発生させます
クラッシュが発生してアプリケーションを終了します

Debug TheradCheck

Debug用のThreadでcheckエラーを発生させます
クラッシュが発生してアプリケーションを終了します

Debug TheradGPF

Debug用のThreadでGPFエラーを発生させます
クラッシュが発生してアプリケーションを終了します

Debug TheradFatal

Debug用のThreadでFatalエラーを発生させます
クラッシュが発生してアプリケーションを終了します

Debug TheradEnsure

Debug用のThreadでensureエラーを発生させます
クラッシュが発生してアプリケーションを終了します

Debug TwoThreadsCrash

Debug用の2つのThreadでFatalエラーを発生させます
クラッシュが発生してアプリケーションを終了します

Debug TwoThreadGPF

Debug用の2つのThreadでGPFエラーを発生させます
クラッシュが発生してアプリケーションを終了します

Debug Crash

Fatalエラーによりクラッシュが発生してアプリケーションを終了します

        UE_LOG(LogEngine, Fatal, TEXT("%s"), TEXT("Crashing the gamethread at your request"));

Debug Check

checkエラーによりクラッシュが発生してアプリケーションを終了します

        check(!"Crashing the game thread via check(0) at your request");

Debug GPF

GPFエラーによりクラッシュが発生してアプリケーションを終了します

        *(int32 *)3 = 123;

Debug Ensure

ensureエラーによりクラッシュが発生してアプリケーションを終了します

        if (!ensure(0))
        {
            return true;
        }

Debug EnsureAlways

ensureAlwaysエラーによりクラッシュが発生してアプリケーションを終了します

        if (!ensureAlways(0))
        {
            return true;
        }

Debug Fatal

LowLevelFatalErrorエラーによりクラッシュが発生してアプリケーションを終了します

        LowLevelFatalError(TEXT("FError::LowLevelFatal test"));

Debug BufferOverRun

BufferOverRunによりクラッシュが発生してアプリケーションを終了します

        ANSICHAR SrcBuffer[] = "12345678901234567890123456789012345678901234567890";
        BufferOverflowFunction(ARRAY_COUNT(SrcBuffer), SrcBuffer);

Debug CRTInvalid

CRT(C-Runtime Library)エラーを発生します
現在機能しません

Debug Hitch [TIME]

意図的なHitch(Sleep)を指定時間(ms)発生します
時間指定が無い場合は1000ms(1s)が選択されます

        float Seconds = FCString::Atof(Cmd) / 1000.0f;
        if (Seconds == 0.0f)
        {
            Seconds = 1.0f;
        }
        FPlatformProcess::Sleep(Seconds);

Debug Spin [TIME]

ThreadにSpinlock(whileループロック)を指定時間(ms)発生します
時間指定が無い場合は1000ms(1s)が選択されます

Debug RenderSpin [TIME]

RenderThreadでSpinlock(whileループロック)を指定時間(ms)発生させるためのRenderCommandを発行します
時間指定が無い場合は1000ms(1s)が選択されます

Debug LongLog

2048Byteを超えるログを出力します

Debug Recurse

再帰呼び出しを実行しアプリケーションをクラッシュします

Debug ThreadRecurse

Threadで再帰呼び出しを実行しアプリケーションをクラッシュします

Debug EatMem

強制的にメモリを確保し続けることで、利用可能なメモリを全て食い潰してクラッシュを発生します

        while (1)
        {
            void* Eat = FMemory::Malloc(65536);
            FMemory::Memset(Eat, 0, 65536);
        }

Debug OOM

OOMを発生させるまで強制的にメモリを確保し続けます
現在機能しません

Debug StackOverflow

再帰的関数の中でMemsetを実行しStackOverflowによるクラッシュを発生します

Debug ThreadStackOverflow

Threadの中で再帰的関数の中でMemsetを実行しStackOverflowによるクラッシュを発生します

Debug SoftLock

Threadのwhile無限ループ内でsleep処理を実行しアプリケーションをハングアップします

Debug InfiniteLoop

InfiniteLoop(for無限ループ処理)を実行しアプリケーションをハングアップします

        for(;;)
        {
        }

Debug Sleep

1hour(3600sec)のsleep処理を実行しアプリケーションをハングアップします

        FPlatformProcess::Sleep(3600);

Debug AudioGPF

AudioThreadにてGPFエラーを実行しクラッシュを発生します

4. まとめ

 エラーテストは大抵の場合、開発やデバッグの終盤で実施されるということがしばしばありますが、このように多くのコンソールコマンドが最初から用意されているため先んじて実施することも容易です。特にエラーテストは自身でエラーケースとなるコードを書いて、リリース時には削除して、といった結局書いたコードを無駄にしてしまうのであれば、こうした既存の機能を利用しない手はありません。できるだけ早期から検証することで出来るだけ手戻りが不要な状態を維持することは大切です。

12
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9