指標分析報告

指標分析會產生兩種報告:一個分析呼叫樹狀結構和一個物件樹狀結構。此資訊由建置流程中的中間步驟產生,並表示呼叫圖和堆積物件圖的靜態分析檢視。這些圖在建置流程中會進一步轉換,然後分別預先編譯為二進位檔並寫入二進位堆積。

除了整個分析宇宙的綜合報告之外,指標分析還可以產生關於為何可存取特定類型/方法/欄位之可達性報告。

呼叫樹狀結構 #

呼叫樹狀結構是指標分析所看到的呼叫圖的廣度優先樹狀結構簡化版本。指標分析會根據分析的接收器類型,消除對其判斷為執行階段不可存取的方法之呼叫。它還會完全消除不可存取程式碼區塊中的調用,例如由永遠失敗的類型檢查所守護的區塊。可以使用 -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>
└── ...

<>之間的標籤會以具體值展開,其餘部分則會依圖示列印。方法會使用 <qualified-holder>.<method-name>(<qualified-parameters>):<qualified-return-type> 格式化,並展開直到無法再存取被呼叫者。

由於這是呼叫圖的樹狀結構簡化版本,因此每個具體方法都會精確展開一次。樹狀結構表示法會固有地省略對已在不同分支中或先前在同一分支中探索過的方法之呼叫。此限制會隱含地修正遞迴問題。為了傳達透過樹狀結構簡化遺失的資訊,每個具體方法都會獲得一個唯一 ID。因此,當第一次存取方法時,它會宣告一個識別碼,例如 id=<method-id>。後續發現的相同方法會使用一個識別碼參考來指向先前展開的位置:id-ref=<method-id>。每個 id=<method-id>id-ref=<method-id> 後面都會有一個空格,以便於搜尋。

每個調用都會標記調用 bci:@bci=<invoke-bci>。對於內嵌方法的調用,<invoke-bci> 是一個 bci 值清單,以 -> 分隔,列舉內嵌位置,反向到原始調用位置。

CSV 格式 #

使用 CSV 格式時,會產生一組包含方法及其關係之原始資料的檔案。特別是,會產生三個檔案—它們表示方法、方法調用和呼叫目標。call_tree_methods_*.csv 具有以下欄:

  • Id:此方法的唯一識別碼。
  • Name:方法的名稱。
  • Type:宣告類型。
  • Parameters:以空格分隔的參數類型清單。
  • Return:傳回類型。
  • Display:方法限定名稱的縮短版本,適用於視覺化。
  • Flags:其他中繼資料,例如可見性修飾符、同步等。
  • IsEntryPoint:如果為 true,則該方法是呼叫圖中的進入點(根方法),否則為 false

call_tree_invokes_*.csv 具有以下欄:

  • Id:調用的唯一識別碼。
  • MethodId:調用所在的方法之識別碼。
  • BytecodeIndexes:調用的位元組碼索引。如果方法已內嵌,則它可以是透過 -> 連接的位元組碼索引鏈。
  • TargetId:目標方法的 ID。
  • IsDirect:如果為 true,則調用是直接的,否則為 false

call_tree_targets_*.csv 具有兩個欄: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>
└── ...

<>之間的標籤會以具體值展開,其餘部分則會依圖示列印。根欄位使用 <qualified-holder>.<field-name>:<qualified-declared-type> 格式化。非根欄位使用 <field-name>:<qualified-declared-type> 格式化。值類型使用 <qualified-type> 格式化。根方法使用 <qualified-holder>.<method-name>(<unqualified-parameters>):<qualified-return-type> 格式化。會針對所有欄位(包括 null)展開非陣列物件。沒有欄位的非陣列物件會標記為 (no fields)。會針對所有非 null 索引展開陣列物件:[<element-index>] <object-tree-rooted-at-array-element>。空陣列物件或具有所有 null 元素的陣列物件會標記為 (no elements)

為了壓縮格式,每個常數值都會精確展開一次。當從多個分支存取值時,它只會在第一次展開時展開,並給定一個識別碼:id=<value-id>。後續發現的相同值會使用一個識別碼參考來指向先前展開的位置:id-ref=<value-id>

抑制值的展開 #

某些值,例如 StringBigInteger 和基本陣列,預設不會展開,並標記為 (expansion suppressed)。預設會展開所有其他類型。若要強制抑制預設展開的類型,您可以使用 -H:ImageObjectTreeSuppressTypes=<comma-separated-patterns>。若要強制展開預設或透過選項抑制的類型,您可以使用 -H:ImageObjectTreeExpandTypes=<comma-separated-patterns>。當同時指定 -H:ImageObjectTreeSuppressTypes-H:ImageObjectTreeExpandTypes 時,-H:ImageObjectTreeExpandTypes 具有優先權。

同樣地,某些根目錄,例如列印大量字串的 java.lang.Character$UnicodeBlock.map",根本不會展開,並標記為 (expansion suppressed)。預設會展開所有其他根目錄。若要強制抑制預設展開的根目錄,您可以使用 -H:ImageObjectTreeSuppressRoots=<comma-separated-patterns>。若要強制展開預設或透過選項抑制的根目錄,您可以使用 -H:ImageObjectTreeExpandRoots=<comma-separated-patterns>。當同時指定 -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.Stringjava.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=* - 強制展開所有根物件,包括預設被抑制的根物件

可達性報告 #

在診斷程式碼大小或安全性問題時,開發人員通常需要知道為什麼某些程式碼元素(類型/方法/欄位)是可達的。可達性報告旨在達成此目的。有三個選項可分別診斷類型、方法和欄位的可達性原因

  • -H:AbortOnTypeReachable=<模式>
  • -H:AbortOnMethodReachable=<模式>
  • -H:AbortOnFieldReachable=<模式>

對於每個選項,右側指定要診斷的程式碼元素的模式。

  • 指定類型和欄位的語法與抑制/展開的語法相同(請參閱上面 -H:ImageObjectTreeSuppressTypes 的文件)。
  • 指定方法的語法與方法篩選器的語法相同(請參閱 -Djdk.graal.MethodFilter 的文件)。

當啟用其中一個選項並且相應的程式碼元素可達時,可達性追蹤將會轉儲到 TXT 檔案中,並且 Native Image 將會停止。以下是 -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_<binary_name>_<date_time>.txt,而使用 CSV 格式時,呼叫樹狀報告的名稱結構為 call_tree_*_<binary_name>_<date_time>.csv。當產生 CSV 格式的呼叫樹狀報告時,也會產生指向最新呼叫樹狀 CSV 報告的符號連結,其結構為 call_tree_*.csv。物件樹狀報告的名稱結構為:object_tree_<binary_name>_<date_time>.txt。二進制名稱是產生的二進制檔的名稱,可以使用 -H:Name=<name> 選項設定。<date_time> 的格式為 yyyyMMdd_HHmmss

可達性報告也位於 reports 目錄中。它們遵循相同的命名慣例

  • 類型可達性報告:trace_types_<binary_name>_<date_time>.txt
  • 方法可達性報告:trace_methods_<binary_name>_<date_time>.txt
  • 欄位可達性報告:trace_fields_<binary_name>_<date_time>.txt

延伸閱讀 #

與我們聯繫