ポインター解析レポート

ポインター解析は、解析呼び出しツリーとオブジェクトツリーの2種類のレポートを生成します。この情報は、ビルドプロセスの中間ステップで生成され、コールグラフとヒープオブジェクトグラフの静的解析ビューを表しています。これらのグラフは、事前にバイナリにコンパイルされ、それぞれバイナリヒープに書き込まれる前に、ビルドプロセスでさらに変換されます。

全体の解析宇宙の包括的なレポートに加えて、ポインター解析は、特定の型/メソッド/フィールドが到達可能である理由に関する到達可能性レポートも生成できます。

呼び出しツリー #

呼び出しツリーは、ポインター解析によって見られるコールグラフの幅優先ツリー削減です。ポインター解析は、解析されたレシーバー型に基づいて、実行時に到達不可能と判断したメソッドへの呼び出しを排除します。また、常に失敗する型チェックによって保護されているブロックなど、到達不可能なコードブロック内の呼び出しも完全に排除します。呼び出しツリーレポートは、-H:+PrintAnalysisCallTreeコマンドラインオプションを使用して有効にでき、-H:PrintAnalysisCallTreeType=CSVオプションを使用して、TXTファイル(デフォルト)またはCSVファイルのセットのいずれかの形式でフォーマットできます。

TXT形式 #

TXT形式を使用する場合、次の構造のファイルが生成されます。

VM Entry Points
├── entry <entry-method> id=<entry-method-id>
│   ├── directly calls <callee> id=<callee-id> @bci=<invoke-bci>
│   │   └── <callee-sub-tree>
│   ├── virtually calls <callee> @bci=<invoke-bci>
│   │   ├── is overridden by <override-method-i> id=<override-method-i-id>
│   │   │   └── <callee-sub-tree>
│   │   └── is overridden by <override-method-j> id-ref=<override-method-j-id>
│   └── interfacially calls <callee> @bci=<invoke-bci>
│       ├── is implemented by <implementation-method-x> id=<implementation-method-x-id>
│       │   └── <callee-sub-tree>
│       └── is implemented by <implementation-method-y> id-ref=<implementation-method-y-id>
├── entry <entry-method> id=<entry-method-id>
│   └── <callee-sub-tree>
└── ...

<>の間のタグは具体的な値に展開され、残りは例示のように出力されます。メソッドは<修飾されたホルダー>.<メソッド名>(<修飾されたパラメーター>):<修飾された戻り値型>を使用してフォーマットされ、それ以上呼び出し先が到達できなくなるまで展開されます。

これはコールグラフのツリー削減であるため、各具体的なメソッドは正確に一度展開されます。ツリー表現は、異なるブランチで、または同じブランチで以前にすでに調査されたメソッドへの呼び出しを本質的に省略します。この制限により、再帰の問題が暗黙的に解決されます。ツリー削減によって失われた情報を伝えるために、各具体的なメソッドには一意のIDが与えられます。したがって、メソッドが最初に到達されると、id=<メソッドID>として識別子が宣言されます。同じメソッドのその後の検出では、以前に展開された場所にポイントする識別子参照が使用されます:id-ref=<メソッドID>。各id=<メソッドID>id-ref=<メソッドID>の後には、検索を容易にするための空白が続きます。

各呼び出しは、呼び出しBCIでタグ付けされます:@bci=<呼び出しBCI>。インラインメソッドの呼び出しの場合、<呼び出しBCI>は、元の呼び出し位置まで逆順にインライン位置を列挙した、->で区切られたBCI値のリストです。

CSV形式 #

CSV形式を使用する場合、メソッドとその関係の生データを含むファイルのセットが生成されます。具体的には、3つのファイルが生成され、メソッド、メソッド呼び出し、および呼び出しターゲットを表します。call_tree_methods_*.csvには、次の列があります。

  • Id:このメソッドの一意の識別子。
  • Name:メソッドの名前。
  • Type:宣言型。
  • Parameters:パラメーター型のスペース区切りリスト。
  • Return:戻り値型。
  • Display:メソッドの修飾名のかいつまんだバージョン。視覚化に役立ちます。
  • Flags:可視性修飾子、同期など、その他のメタデータ。
  • IsEntryPointtrueの場合、メソッドはコールグラフのエントリポイント(ルートメソッド)です。それ以外の場合はfalse

call_tree_invokes_*.csvには、次の列があります。

  • Id:呼び出しの一意の識別子。
  • MethodId:呼び出しが存在するメソッドの識別子。
  • BytecodeIndexes:呼び出しのバイトコードインデックス。メソッドがインライン化されていた場合は、->で接続されたバイトコードインデックスのチェーンになる可能性があります。
  • TargetId:ターゲットメソッドのID。
  • IsDirecttrueの場合、呼び出しは直接呼び出しです。それ以外の場合はfalse

call_tree_targets_*.csvには、呼び出しと呼び出しターゲットを接続する2つの列InvokeIdTargetIdがあります。

これらのファイルの目的は、生データをカスタムスクリプトで簡単に処理したり、グラフデータベースにインポートしたりできるようにすることです。グラフデータベースは、次の機能を提供できます。

  • テキストベースの形式とは異なる視点を与える、コールツリーグラフの洗練されたグラフィカルな視覚化。
  • 特定のコードパスがコールツリー解析に含まれる原因となるツリーのサブセットを表示するなど、複雑なクエリを実行する機能。このクエリ機能は、大規模な解析呼び出しツリーを管理可能にする上で非常に重要です。

ファイルをグラフデータベースにインポートするプロセスは、データベースごとに固有です。グラフデータベースプロバイダーが提供する手順に従ってください。

オブジェクトツリー #

オブジェクトツリーは、ネイティブバイナリヒープに含まれるオブジェクトの網羅的な展開です。ツリーは、ネイティブバイナリヒープオブジェクトグラフの深さ優先探索によって取得されます。これは、-H:+PrintImageObjectTreeオプションを使用して有効になります。ルートは、静的フィールドまたは埋め込み定数を含むメソッドグラフのいずれかです。出力される値は、ネイティブバイナリヒープに追加された具体的な定数オブジェクトです。次の構造のファイルが生成されます。

Heap roots
├── root <root-field> value:
│   └── <value-type> id=<value-id> toString=<value-as-string> fields:
│       ├── <field-1> value=null
│       ├── <field-2> toString=<field-2-value-as-string> (expansion suppressed)
│       ├── <field-3> value:
│       │   └── <field-3-value-type> id=<field-3-value-id> toString=<field-3-value-as-string> fields:
│       │       └── <object-tree-rooted-at-field-3>
│       ├── <array-field-4> value:
│       │   └── <array-field-4-value-type> id=<array-field-4-value-id> toString=<array-field-4-value-as-string> elements (excluding null):
│       │       ├── [<index-i>] <element-index-i-value-type> id=<element-index-i-value-id> toString=<element-index-i-value-as-string> fields:
│       │       │   └── <object-tree-rooted-at-index-i>
│       │       └── [<index-j>] <element-index-j-value-type> id=<element-index-j-value-id> toString=<element-index-j-value-as-string> elements (excluding null):
│       │           └── <object-tree-rooted-at-index-j>
│       ├── <field-5> value:
│       │   └── <field-5-value-type> id-ref=<field-5-value-id> toString=<field-5-value-as-string>
│       ├── <field-6> value:
│       │   └── <field-6-value-type> id=<field-6-value-id> toString=<field-6-value-as-string> (no fields)
│       └── <array-field-7> value:
│           └── <array-field-7-value-type> id=<array-field-7-id> toString=<array-field-7-as-string> (no elements)
├── root <root-field> id-ref=<value-id> toString=<value-as-string>
├── root <root-method> value:
│   └── <object-tree-rooted-at-constant-embeded-in-the-method-graph>
└── ...

<>の間のタグは具体的な値に展開され、残りは例示のように出力されます。ルートフィールドは<修飾されたホルダー>.<フィールド名>:<修飾された宣言型>を使用してフォーマットされます。ルート以外のフィールドは<フィールド名>:<修飾された宣言型>を使用してフォーマットされます。値の型は<修飾された型>を使用してフォーマットされます。ルートメソッドは<修飾されたホルダー>.<メソッド名>(<未修飾パラメーター>):<修飾された戻り値型>を使用してフォーマットされます。配列以外のオブジェクトは、すべてのフィールド(nullを含む)に対して展開されます。フィールドのない配列以外のオブジェクトには(no fields)というタグが付けられます。配列オブジェクトは、すべてのnull以外のインデックスに対して展開されます:[<要素インデックス>] <配列要素をルートとするオブジェクトツリー>。空の配列オブジェクトまたはすべての要素がnullのオブジェクトには(no elements)というタグが付けられます。

各定数値は、フォーマットを圧縮するために正確に一度展開されます。値が複数のブランチから到達されると、最初の時にのみ展開され、識別子が与えられます:id=<値ID>。同じ値のその後の検出では、以前に展開された場所にポイントする識別子参照が使用されます:id-ref=<値ID>

値の展開の抑制 #

StringBigInteger、プリミティブ配列などの値は、デフォルトでは展開されず、(expansion suppressed)でマークされます。その他のすべての型は、デフォルトで展開されます。デフォルトで展開される型の展開を強制的に抑制するには、-H:ImageObjectTreeSuppressTypes=<カンマ区切りのパターン>を使用します。デフォルトで、またはオプションによって抑制されている型の展開を強制するには、-H:ImageObjectTreeExpandTypes=<カンマ区切りのパターン>を使用します。-H:ImageObjectTreeSuppressTypes-H:ImageObjectTreeExpandTypesの両方が指定されている場合、-H:ImageObjectTreeExpandTypesが優先されます。

同様に、多くの文字列を出力するjava.lang.Character$UnicodeBlock.map"などの一部のルートは、まったく展開されず、(expansion suppressed)でもマークされます。その他のすべてのルートは、デフォルトで展開されます。デフォルトで展開されるルートの展開を強制的に抑制するには、-H:ImageObjectTreeSuppressRoots=<カンマ区切りのパターン>を使用します。デフォルトで、またはオプションによって抑制されているルートの展開を強制するには、-H:ImageObjectTreeExpandRoots=<カンマ区切りのパターン>を使用します。-H:ImageObjectTreeSuppressRoots-H:ImageObjectTreeExpandRootsの両方が指定されている場合、-H:ImageObjectTreeExpandRootsが優先されます。

上記のすべての抑制/展開オプションは、カンマ区切りのパターンリストを受け入れます。型の場合、パターンは型の完全修飾名に基づいており、定数の具体的な型を参照します。(配列型の場合、要素型を指定するだけで十分です。すべての次元のその型のすべての配列に一致します。)ルートの場合、パターンは上記のルートの文字列形式に基づいています。パターンは*修飾子を受け入れます。

  • 末尾一致:*<str> - パターンは、<str>で終わるすべてのエントリと正確に一致します。
  • 先頭一致:<str>* - パターンは、<str>で始まるすべてのエントリと正確に一致します。
  • 部分一致:*<str>* - パターンは、<str>を含むすべてのエントリと正確に一致します。
  • 完全一致:<str> - パターンは、<str>と等しいすべてのエントリと正確に一致します。
  • すべて:* - パターンはすべてのエントリと一致します。

型の抑制/展開

  • -H:ImageObjectTreeSuppressTypes=java.io.BufferedWriter - java.io.BufferedWriterオブジェクトの展開を抑制します。
  • -H:ImageObjectTreeSuppressTypes=java.io.BufferedWriter,java.io.BufferedOutputStream - java.io.BufferedWriterjava.io.BufferedOutputStreamオブジェクトの展開を抑制します。
  • -H:ImageObjectTreeSuppressTypes=java.io.* - すべてのjava.io.*オブジェクトの展開を抑制します。
  • -H:ImageObjectTreeExpandTypes=java.lang.String - java.lang.Stringオブジェクトの展開を強制します。
  • -H:ImageObjectTreeExpandTypes=java.lang.String,java.math.BigInteger - java.lang.Stringオブジェクトとjava.math.BigIntegerオブジェクトの展開を強制します。
  • -H:ImageObjectTreeExpandTypes=java.lang.* - すべてのjava.lang.*オブジェクトの展開を強制します。
  • -H:ImageObjectTreeSuppressTypes=java.io.* -H:ImageObjectTreeExpandTypes=java.io.PrintStream - すべてのjava.io.*オブジェクトの展開を抑止しますが、java.io.PrintStreamオブジェクトは除きます。
  • -H:ImageObjectTreeExpandTypes=* - デフォルトで抑止されているものを含め、すべての型のオブジェクトの展開を強制します。

ルートの抑制/展開

  • -H:ImageObjectTreeSuppressRoots="java.nio.charset.Charset.lookup(String)" - com.oracle.svm.core.amd64.FrameAccess.wordSize()のグラフに埋め込まれたすべての定数の展開を抑止します。
  • -H:ImageObjectTreeSuppressRoots=java.util.* - java.util.で始まるすべてのルートの展開を抑止します。
  • -H:ImageObjectTreeExpandRoots=java.lang.Character$UnicodeBlock.map - java.lang.Character$UnicodeBlock.map静的フィールドルートの展開を強制します。
  • -H:ImageObjectTreeSuppressRoots=java.util.* -H:ImageObjectTreeExpandRoots=java.util.Locale - java.util.で始まるすべてのルートの展開を抑止しますが、java.util.Localeは除きます。
  • -H:ImageObjectTreeExpandRoots=* - デフォルトで抑止されているものを含め、すべてのルートの展開を強制します。

到達可能性レポート #

コードサイズまたはセキュリティの問題を診断する際に、開発者は特定のコード要素(型/メソッド/フィールド)が到達可能である理由を知る必要があることがよくあります。到達可能性レポートは、この目的のために設計されています。型、メソッド、フィールドの到達可能性の理由を診断するためのオプションが3つあります。

  • -H:AbortOnTypeReachable=<パターン>
  • -H:AbortOnMethodReachable=<パターン>
  • -H:AbortOnFieldReachable=<パターン>

各オプションについて、右側は診断対象のコード要素のパターンを指定します。

  • 型とフィールドを指定する構文は、抑制/展開の構文と同じです(上記の-H:ImageObjectTreeSuppressTypesに関するドキュメントを参照してください)。
  • メソッドを指定する構文は、メソッドフィルターの構文と同じです(-Djdk.graal.MethodFilterに関するドキュメントを参照してください)。

いずれかのオプションが有効になっており、対応するコード要素が到達可能な場合、到達可能性トレースがTXTファイルにダンプされ、ネイティブイメージが停止します。-H:AbortOnTypeReachable=java.io.Fileの到達可能性レポートの例を以下に示します。

Type java.io.File is marked as allocated
at virtual method com.oracle.svm.core.jdk.NativeLibrarySupport.loadLibraryRelative(NativeLibrarySupport.java:105), implementation invoked
├── at virtual method com.oracle.svm.core.jdk.JNIPlatformNativeLibrarySupport.loadJavaLibrary(JNIPlatformNativeLibrarySupport.java:44), implementation invoked
│       ├── at virtual method com.oracle.svm.core.posix.PosixNativeLibrarySupport.loadJavaLibrary(PosixNativeLibraryFeature.java:117), implementation invoked
│       │       ├── at virtual method com.oracle.svm.core.posix.PosixNativeLibrarySupport.initializeBuiltinLibraries(PosixNativeLibraryFeature.java:98), implementation invoked
│       │       │       ├── at static method com.oracle.svm.core.graal.snippets.CEntryPointSnippets.initializeIsolate(CEntryPointSnippets.java:346), implementation invoked
│       │       │       │       str: static root method
│       │       │       └── type com.oracle.svm.core.posix.PosixNativeLibrarySupport is marked as in-heap
│       │       │               scanning root com.oracle.svm.core.posix.PosixNativeLibrarySupport@4839bf0d: com.oracle.svm.core.posix.PosixNativeLibrarySupport@4839bf0d embedded in
│       │       │                   org.graalvm.nativeimage.ImageSingletons.lookup(ImageSingletons.java)
│       │       │                   at static method org.graalvm.nativeimage.ImageSingletons.lookup(Class), intrinsified
│       │       │                       at static method com.oracle.svm.core.graal.snippets.CEntryPointSnippets.createIsolate(CEntryPointSnippets.java:209), implementation invoked
│       │       └── type com.oracle.svm.core.posix.PosixNativeLibrarySupport is marked as in-heap
│       └── type com.oracle.svm.core.jdk.JNIPlatformNativeLibrarySupport is reachable
└── type com.oracle.svm.core.jdk.NativeLibrarySupport is marked as in-heap
        scanning root com.oracle.svm.core.jdk.NativeLibrarySupport@6e06bbea: com.oracle.svm.core.jdk.NativeLibrarySupport@6e06bbea embedded in
            org.graalvm.nativeimage.ImageSingletons.lookup(ImageSingletons.java)
            at static method org.graalvm.nativeimage.ImageSingletons.lookup(Class), intrinsified

レポートファイル #

レポートは、ビルドディレクトリを基準としたreportsサブディレクトリに生成されます。native-image実行可能ファイルを起動すると、ビルドディレクトリはデフォルトで作業ディレクトリになり、-H:Path=<dir>オプションを使用して変更できます。

コールツリーレポート名は、TXT形式を使用する場合はcall_tree_<バイナリ名>_<日時>.txtCSV形式を使用する場合はcall_tree_*_<バイナリ名>_<日時>.csvという構造になります。CSV形式のコールツリーレポートを作成する際には、最新のコールツリーCSVレポートを指すcall_tree_*.csvという構造のシンボリックリンクも生成されます。オブジェクトツリーレポート名は、object_tree_<バイナリ名>_<日時>.txtという構造になります。バイナリ名は生成されたバイナリの名前であり、-H:Name=<名前>オプションで設定できます。<日時>yyyyMMdd_HHmmss形式です。

到達可能性レポートもreportsディレクトリにあります。命名規則は同じです。

  • 型到達可能性レポート:trace_types_<バイナリ名>_<日時>.txt
  • メソッド到達可能性レポート:trace_methods_<バイナリ名>_<日時>.txt
  • フィールド到達可能性レポート:trace_fields_<バイナリ名>_<日時>.txt

さらに読む #

お問い合わせ