[UE4] Objコマンドによるオブジェクト解析


#1.はじめに
Objコマンドは[「UE4におけるLoadingとGCのProfilingと最適化手法」](https://www.slideshare.net/EpicGamesJapan/ue4loadinggcprofiling/41)で説明されているようにシーン中のUObjectを知るのに便利というのは一般的に知られていますが、他にも様々な機能やオプションが用意されているのでここで紹介します。
4.20.0で確認しています。
#2. Obj コマンド
Objコマンドは基本的にShipping以外で使用することが許可されたコマンドです。
Objコマンドを使用することで、ロードが完了しているオブジェクトや、オブジェクト同士の参照関係をランタイムで確認することができます。また、不用意なメモリ確保などによるメモリリークを特定することは難しいですが、オブジェクトが削除されずに残り続けるケースやリソースのボトルネックを確認することもできます。

---
以降は、UnrealEngine.cppのUEngine::HandleObjCommand()に定義されたものです。

##2.1. Obj LIST
Objectリストをサイズで降順出力します。クラス単位で確保されているオブジェクトのサイズを出力しているため、全ての合算値がプログラムで確保されているサイズではなく重複していることがあります。
![obj list.png](https://qiita-image-store.s3.amazonaws.com/0/101019/026b9a3c-7a58-6981-7a15-2aea87265d45.png)
**Class**
クラス名を示します。
**Count**
存在するオブジェクト数を示します。
**NumKB**
使用中のオブジェクトのサイズを示します。
この値はシリアライズされたオブジェクトのサイズ、つまりプログラム上で確保されているサイズを示します。
この値はそのクラスで確保している合算値です。この値が大きい場合はオブジェクト自体のサイズが大きいかオブジェクト数が大きいため削減の余地があります。
**MaxKB**
使用中のオブジェクトの最大サイズを示します。
TArrayを使用している場合は予め確保されたArrayMax*ElementSizeで計算されます。
![ArrayHelper.png](https://qiita-image-store.s3.amazonaws.com/0/101019/89f5be6b-1dcf-181c-cc62-854bc4742650.png)
**ResExcKB**
任意のメモリから割り当てられた合計バイト数を示します。これは以下の
ResExcDedSysKB、ResExcShrSysKB、ResExcDedVidKB、ResExcShrVidKB、ResExcUnkKBを合算したサイズになります。
**ResExcDedSysKB**
専用システムメモリで割り当てられたバイト数を示します。
各オブジェクトのGetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize)で計算され、Material、SoundWave、Font、StaticMeshなどのリソースサイズを示します。以下はSoundWaveを出力した際の例ですが、Editorの場合はEngineコンテンツに含まれるサウンドファイル、ComileSuccess(コンパイル成功SE)、ComileFailed(コンパイル失敗SE)などが含まれます。
以下は”obj list Class=SoundWave”でSoundWaveリソース一覧を指定した際の出力です。
![Sound.png](https://qiita-image-store.s3.amazonaws.com/0/101019/d4a17f82-92ab-7adb-2c0a-a9ab78013a76.png)
**ResExcShrSysKB**
共有システムメモリで割り当てられたバイト数を示します。
現在この数値は使用されていません。
**ResExcDedVidKB**
専用ビデオメモリ(VRAM)で割り当てられたバイト数を示します。
各オブジェクトのGetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize)で計算され、基本的にはTextureのリソースサイズを示します。以下はTexture「Ai_Spawnpoint」を出力した際の例でResourceSize=ResExcDedVidKBで合致します。
以下は”obj list Name=Ai_Spawnpoint”でリソース名を指定した際の出力です。
![2018-09-15_15h19_59.png](https://qiita-image-store.s3.amazonaws.com/0/101019/c9832728-8df9-d893-ffba-9bf0ed402d87.png)
**ResExcShrVidKB**
共有ビデオメモリ(VRAM)で割り当てられたバイト数を示します。
現在この数値は使用されていません。
**ResExcUnkKB**
未知のメモリで割り当てられたバイト数を示します。
各オブジェクトのGetResourceSizeEx(FResourceSizeEx& CumulativeResourceSize)で計算され、基本的にはSkeletalMesh、StaticMesh、ParticleEmitterで割り当てられます。例えばStaticMeshの場合は以下のようにLODのリソースサイズがカウントされます。
![ResExcUnkKB.png](https://qiita-image-store.s3.amazonaws.com/0/101019/410169ce-525c-aa00-381a-5e51c3fb3e55.png)

オプション 
---
###Class=[ClassName]###
クラス名を指定でオブジェクトを一覧で表示します。**つまり現在ロードされているクラスのオブジェクトを一覧で表示することができます。**ここからどのオブジェクトにどれだけのリソースを割いているかを推定することができます。
以下はThirdPersonCharacterを出力したものです。
*【入力例】obj list Class=ThirdPersonCharacter_C*
![Class.png](https://qiita-image-store.s3.amazonaws.com/0/101019/32c19773-41fc-8135-33a6-579913a49a0b.png)

###Outer=[ObjectName/ClassName]###
オブジェクト自身が持つ情報を出力します。
オブジェクトまたはクラスを指定すると所有する関数やプロパティで確保されたサイズが一覧として出力されます。
*【入力例】obj list Outer=WidgetAnimation*
![Outer.png](https://qiita-image-store.s3.amazonaws.com/0/101019/36ebc398-ffcd-ea64-b03a-7d35e89a8016.png)

UWidgetAnimationを見ると以下のように関数とプロパティが定義されていることから分かるように、ここで出力された内容と一致することが分かります。ただしvirtual関数など親クラスで定義された内容は含まないため、あくまで指定したオブジェクト・クラスが保有するメンバーのみが対象となります。
![WidgetAnim.png](https://qiita-image-store.s3.amazonaws.com/0/101019/3ab7db01-8d5c-e239-a8cf-5c789e24cc60.png)

###Package=[InsidePackage]###
パッケージ名指定でオブジェクト情報を出力します。Inside/InsideClassオプションとの併用で指定はできません。
obj list class=packageでInsidePackageを一覧で表示することができます。
![package.png](https://qiita-image-store.s3.amazonaws.com/0/101019/87506dcc-c63e-12d0-bf56-f4d12ac67a4c.png)
Package(UPackage)はコンテンツのパスが基本的なオブジェクト名です。
![UPackage.png](https://qiita-image-store.s3.amazonaws.com/0/101019/bf142faa-b9c0-1161-c6cd-f271c53a7136.png)
*【入力例】obj list Package=/Script/[ProjectName]*
以下の場合プロジェクト内で定義されたC++クラスのオブジェクト情報を表示します。
![PackageProject.png](https://qiita-image-store.s3.amazonaws.com/0/101019/5fc0e0ee-8344-43e4-d234-0ffeef392585.png)

###Depth=[DepthNum]###
オブジェクトの情報を表示する際のクラス階層の下限を指定します。デフォルトでは-1が指定されているためクラスの最下層(現在のクラス)まで表示することができますが、任意に指定することで特定のクラス階層までしか表示されなくなります。
*【入力例】obj list InsideClass=NavTestGameMode depth=1*
![Depth1.png](https://qiita-image-store.s3.amazonaws.com/0/101019/cd646619-0b50-7318-e620-adced978ca0c.png)
*【入力例】obj list InsideClass=NavTestGameMode depth=2*
![Depth2.png](https://qiita-image-store.s3.amazonaws.com/0/101019/20e2adc4-a877-ddb3-33c7-a3aff2178293.png)
*【入力例】obj list InsideClass=NavTestGameMode depth=-1*
![Depth-1.png](https://qiita-image-store.s3.amazonaws.com/0/101019/bee41115-287d-749b-fdc9-780d7d7a4758.png)

###Inside=[InsideObject]###
オブジェクト名指定でオブジェクト情報を表示しますが基本的にはPackageと同じです。異なる点はUObject名を直接指定することで内部の情報のみを抽出して表示できます。Package/InsideClassとの併用で指定はできません。
*【入力例】obj list Inside=/Script/NavTest.NavTestCharacter*
(NavTest.uprojectのC++クラスにあるNavTestCharacterのオブジェクト情報を表示します)
![Inside.png](https://qiita-image-store.s3.amazonaws.com/0/101019/f937cd46-c489-6eb4-a836-43b361ae4a6a.png)

###InsideClass=[InsideClass]###
指定したクラスが参照するオブジェクトの情報を表示します。Package/Insideオプションとの併用で指定はできません。
以下の場合プロジェクト内で定義されたC++クラスのGameModeが、継承したBPクラスの参照を表示します。
*【入力例】obj list InsideClass=NavTestGameMode*
![InsideClass.png](https://qiita-image-store.s3.amazonaws.com/0/101019/3d8ab049-0774-47bb-dcc6-ad48b215ebf5.png)

###Name=[ObjectName]###
オブジェクト名を指定で存在しているオブジェクトを一覧で表示します。
*【入力例】obj list Name=DocumentationActor*
![Name.png](https://qiita-image-store.s3.amazonaws.com/0/101019/bd5ffb79-11b3-390e-dafa-207057cbbf09.png)

###-All###
クラス指定での表示時にそのクラスの全てのオブジェクト情報を表示するようになります。以下のコマンドによるオブジェクトのマスクを無効化します。

###-GCONLY###
DisregardForGC対象のObjectをフィルタリングして出力に含まないコマンドです。

###-GCNOCLUSTERS###
DisregardForGC対象のObjectをフィルタリングして、かつGCクラスタリング対象のObjectを出力に含まないコマンドです。

###-ROOTONLY###
RootObjectのみ出力するようにフィルタリングします。

###-INCLUDEDEFAULTS###
CreateDefaultObjectを出力に含むようにします。

###-DEFAULTSONLY###
このオプションを有効にした場合、CreateDefaultObjectでないオブジェクトの出力をフィルタリングします。(おそらくリストは出力が無くなります)

###-NODETAILEDINFO###
クラス指定での表示時にそのクラスの全てのオブジェクト情報を表示に含まず、統計情報のみを表示するようになります。
###-ALPHASORT###
Objectリストをアルファベット順で降順出力します。
*【入力例】obj list -alphasort*
![Alphasort.png](https://qiita-image-store.s3.amazonaws.com/0/101019/bd01bbc5-42fa-9730-bbe2-63c40899174b.png)
###-COUNTSORT###
Objectリストをカウント数で降順出力します。
*【入力例】obj list -countsort*
![Countsort.png](https://qiita-image-store.s3.amazonaws.com/0/101019/3ec3b024-d785-960d-29fc-df0360a33af8.png)
###-CSV###
.csv形式のカンマ区切りで結果を出力します(.csvファイルとしては出力しません)。
![csv.png](https://qiita-image-store.s3.amazonaws.com/0/101019/f8947bde-40f1-7a89-aca9-a2df808c9f1c.png)
###FORGET###
Obj Listで取得したObjectを記憶します。
これ以降のObj Listには記憶されたオブジェクト以外が出力されるため、**オブジェクトの増減などの変化を見る際に有効です。**
*【入力例】obj list forget*
###REMEMBER###
*【入力例】obj list remember*
FORGETオプションで記憶したオブジェクトをリセットします。
##2.2. Obj GARBAGE / Obj GC
定期実行GCのタイマーをリセットします。
##2.3. Obj TRYGC
GCを即時実行し、定期実行GCのタイマーをリセットします。
##2.4. Obj LIST2
オブジェクトのメモリ統計情報を**SubObjectを含む形式**で出力します。Obj listと比較するとSubObjectを含むため階層構造が分かりやすいという点で有利ですが、処理のパフォーマンスは良くないため、利用する際にはClass指定で実行することをお勧めします。
*【入力例】obj list2 Class=ThirdPersonCharacter_C*
![2018-09-16_00h21_20.png](https://qiita-image-store.s3.amazonaws.com/0/101019/3215da34-a19a-6209-db0b-6aad2f79612e.png)
##2.5. Obj PROPANALYSIS
オブジェクトのプロパティ解析を行います。オブジェクトの変数がデフォルト値からどの程度変更されたか等誤差率を算出するものと思われますが、現状は正しく動作しません。現状はデフォルト値から変更があった場合に[REMV]のリストから除外される動作となります。Classオプションによりクラス指定が可能です。Min=[MinPercentDiff]/Max=[MaxPercentDiff]オプションで誤差率を指定することも可能です。
*【入力例】obj propanalysis class=UserWidget Min=0 Max=100*
![2018-09-16_01h50_52.png](https://qiita-image-store.s3.amazonaws.com/0/101019/4aa8a685-e972-a33f-0d6f-3f8e60746fae.png)

##2.6. Obj COMPONENTS
コンポーネントのプロパティを出力するものと思われますが現状は正しく動作しません。
現時点ではオブジェクト名の指定に相違があるだけでクラッシュするため実用的ではありません。
*【入力例】obj components StaticMesh*
![2018-09-16_03h16_06.png](https://qiita-image-store.s3.amazonaws.com/0/101019/b5695e23-5ca4-7e24-babd-d2df70f599cf.png)


##2.7. Obj Dump
Object、及びObjectが所有する現在のパラメータを出力することができます。
Class/Nameオプションで対象のオブジェクトを指定することができます。
RECURSE=[true/false]オプションで再帰的表示のマスクができます。
show/hide=[Properties]オプションで各プロパティ表示のマスクができます。
*【入力例】obj DUMP class=ThirdPersonCharacter_C name=ThirdPersonCharacter_2 RECURSE=false*
![2018-09-15_23h01_57.png](https://qiita-image-store.s3.amazonaws.com/0/101019/2ba2c523-972e-6c54-6a4d-3a765b06d626.png)
*【入力例】obj DUMP class=ThirdPersonCharacter_C name=ThirdPersonCharacter_2 hide=NavTestCharacter*
![2018-09-15_23h02_51.png](https://qiita-image-store.s3.amazonaws.com/0/101019/f049fffd-7be8-e266-ae4a-ea37034d6875.png)
##2.8. Obj HASH
使用中のハッシュバケットを収集して表示します。
*【入力例】obj hash*
![2018-09-15_21h56_32.png](https://qiita-image-store.s3.amazonaws.com/0/101019/614f0a55-be0f-3339-3d2e-8328f8bc018e.png)
オプション 
---
###-SHOWBUCKETCOLLISIONS###
ハッシュバケットのペアキーからコリジョン情報をダンプします。
*【入力例】obj hash -SHOWBUCKETCOLLISIONS*
![hash bucket.png](https://qiita-image-store.s3.amazonaws.com/0/101019/e42e898b-0ddf-6834-046e-3421a5c28248.png)
##2.8. Obj HASHOUTER
オブジェクトハッシュのハッシュ効率の統計を出力します。
*【入力例】obj hashouter*
![obj hashouter.png](https://qiita-image-store.s3.amazonaws.com/0/101019/51e3be72-2fee-98aa-c69f-f578370e5776.png)
###-SHOWBUCKETCOLLISIONS###
ハッシュバケットのペアキーからコリジョン情報をダンプします。
*【入力例】obj hashouter -SHOWBUCKETCOLLISIONS*
![obj hashouter bucket.png](https://qiita-image-store.s3.amazonaws.com/0/101019/5a499520-1ae1-f25b-a621-9b79bc468d6b.png)
##2.9. Obj MEM
クラス名を指定することで、対象のオブジェクトを参照するオブジェクト(Outer Hierarchy)/クラス情報(Class Hierarchy)/全体での情報(Flat)を表示します。
オプション：Cull=[CullingKB]で指定のサイズ以下をフィルタリングが可能です。
デフォルトではCull=50であるため指定が無い場合50KBの表示は省略されます。
*【入力例】obj mem Class=ThirdPersonCharacter_C*
![2018-09-15_21h16_37.png](https://qiita-image-store.s3.amazonaws.com/0/101019/de0edae2-5ff4-b1b0-e439-448d8731f7d9.png)
上記の"7K"はオブジェクトの最大サイズ(MaxKB)、"1K"xはサイズ(NumKB)、"7"はインスタンス数を示します。
*【入力例】obj mem Class=ThirdPersonCharacter_C cull=1*
![2018-09-15_21h16_03.png](https://qiita-image-store.s3.amazonaws.com/0/101019/de2b6b0c-7a90-d8a4-b15b-f8785a68fafb.png)
##2.10. Obj MEMSUB
クラス名を指定することで、指定クラスのオブジェクトに対して外部参照にあたる(キャラクターBPの場合、キャラクターBPが保持するAnimBPやSkeletalMeshComponentなどの参照関係にある)オブジェクトで確保される総メモリ数とカウント数を表示します。
オプション：Cull=[CullingKB]で指定のサイズ以下をフィルタリングが可能です。
デフォルトではCull=16であるため指定が無い場合16KBの表示は省略されます。
*【入力例】obj memsub Class=ThirdPersonCharacter_C*
![2018-09-15_21h47_34.png](https://qiita-image-store.s3.amazonaws.com/0/101019/e9ac6f8a-ca5b-0814-3b9e-144e389de5bd.png)

---
以降は、Obj.cppのStaticExec()に定義されたものです。

##2.11. Obj Refs
オブジェクト自身が参照されているオブジェクトを出力します。参照されたまま残っているオブジェクトを特定したり、ロードなどが長い場合に参照されているオブジェクトを特定する際に有用です。

*【入力例】obj refs name=ThirdPersonCharacter_C*

オプション 
---
【入力例】obj refs name=ThirdPersonCharacter_C direct

###shortest###
各ルートオブジェクトの最短参照チェーンのみを表示します。
###longest###
各ルートオブジェクトの最長参照チェーンのみを表示します。
###all###
全ての結果を表示します(参照が膨大でハングアップする可能性もあります)。
###external###
外部オブジェクトから参照チェーンのみを表示します。
###direct###
直接参照元のみのチェインを表示します。