Truffle 分支檢測

在使用 Truffle 實作的語言中,AST 實作通常包含快速和慢速的執行路徑,通常基於一些條件,例如設定檔。這些執行路徑會組織成不同的條件分支。在這些情況下,通常有助於了解執行程式是否實際執行了這些執行路徑中的程式碼。

分支檢測功能會檢測目標方法中的 if 語句,以追蹤執行期間已採用哪些分支。分支檢測透過檢測分支並使用程式碼寫入全域表格來完成。每個分支在此表格中都有一個條目。當程式結束時,表格的內容會被解碼並以可讀的形式轉儲到標準輸出。

有幾個旗標可控制分支檢測的工作方式。這些旗標會指定為系統屬性

  • --compiler.InstrumentBranches - 控制是否啟用檢測(truefalse,預設為 false
  • --compiler.InstrumentFilter - 過濾應執行檢測的方法(方法過濾語法,基本上是 <package>.<class>.<method>[.<signature>]
  • --compiler.InstrumentationTableSize - 控制檢測位置的最大數量
  • --compiler.InstrumentBranchesPerInlineSite - 控制檢測是否為每個客座語言函式/編譯單元提供獨立的分支設定檔(預設為 false)。

使用範例 #

以下是如何在程式上啟用分支檢測的範例。

當使用檢測來偵測 Truffle 語言實作中熱門或不常使用的分支時,通常從尋找具有問題方法之語言節點開始。以下命令會執行 SimpleLanguage 的單元測試,並檢測所有 if 語句

mx --jdk jvmci sl --engine.BackgroundCompilation=false \
  --compiler.InstrumentBranches \
  '--compiler.InstrumentFilter=*.*.*' \
  ../truffle/truffle/com.oracle.truffle.sl.test/src/tests/LoopObjectDyn.sl

您會得到以下輸出

Execution profile (sorted by hotness)
=====================================
  0: *****************************************************
  1: **************************

com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
[0] state = IF(if=36054#, else=0#)

com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.executeRepeating(SLWhileRepeatingNode.java:102) [bci: 5]
[1] state = BOTH(if=18000#, else=18#)

此輸出表示在檔案 SLWhileRepeatingNode.java 中第 102 行的 if 語句中兩個分支都已造訪,並且在檔案 SLPropertyCacheNode.java 中第 109 行的 if 語句中只有 true 分支已造訪。但是,它並未說明,例如,此特定 SLPropertyCacheNode 節點是從何處使用 – 同一個 execute 方法可以從許多不同的 SimpleLanguage 節點呼叫,您可能希望區分這些發生情況。因此,請將每個內嵌位置旗標設定為 true,並變更篩選器以僅關注 SLPropertyCacheNode

mx --jdk jvmci sl -Djdk.graal.TruffleBackgroundCompilation=false \
  --compiler.InstrumentBranchesPerInlineSite \
  --compiler.InstrumentBranches \
  '--compiler.InstrumentFilter=*.SLPropertyCacheNode.*' \
  ../truffle/truffle/com.oracle.truffle.sl.test/src/tests/LoopObjectDyn.sl

這次您會得到更多輸出,因為方法 namesEqual 已在多個位置內嵌(每個位置都以其內嵌鏈表示)。以下輸出片段首先顯示具有 if 語句 ID 及其出現次數的直方圖。然後,它會顯示分支的確切呼叫堆疊和執行次數。例如,對於 [1],當從 executeRead 呼叫 namesEqual 時,true 分支會執行 18018 次。當從 executeWrite ([0]) 呼叫 namesEqual 時,true 分支只執行 18

Execution profile (sorted by hotness)
=====================================
  1: ***************************************
  2: ***************************************
  0:
  3:

com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
com.oracle.truffle.sl.nodes.access.SLReadPropertyCacheNodeGen.executeRead(SLReadPropertyCacheNodeGen.java:76) [bci: 88]
com.oracle.truffle.sl.nodes.access.SLReadPropertyNode.read(SLReadPropertyNode.java:71) [bci: 7]
com.oracle.truffle.sl.nodes.access.SLReadPropertyNodeGen.executeGeneric(SLReadPropertyNodeGen.java:30) [bci: 35]
com.oracle.truffle.sl.nodes.SLExpressionNode.executeLong(SLExpressionNode.java:81) [bci: 2]
com.oracle.truffle.sl.nodes.expression.SLLessThanNodeGen.executeBoolean_long_long0(SLLessThanNodeGen.java:42) [bci: 5]
com.oracle.truffle.sl.nodes.expression.SLLessThanNodeGen.executeBoolean(SLLessThanNodeGen.java:33) [bci: 14]
com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.evaluateCondition(SLWhileRepeatingNode.java:133) [bci: 5]
com.oracle.truffle.sl.nodes.controlflow.SLWhileRepeatingNode.executeRepeating(SLWhileRepeatingNode.java:102) [bci: 2]
com.oracle.truffle.runtime.OptimizedOSRLoopNode.executeLoop(OptimizedOSRLoopNode.java:113) [bci: 61]
com.oracle.truffle.sl.nodes.controlflow.SLWhileNode.executeVoid(SLWhileNode.java:69) [bci: 5]
com.oracle.truffle.sl.nodes.controlflow.SLBlockNode.executeVoid(SLBlockNode.java:84) [bci: 37]
com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode.executeGeneric(SLFunctionBodyNode.java:81) [bci: 5]
com.oracle.truffle.sl.nodes.SLRootNode.execute(SLRootNode.java:78) [bci: 28]
[1] state = IF(if=18018#, else=0#)

...

com.oracle.truffle.sl.nodes.access.SLPropertyCacheNode.namesEqual(SLPropertyCacheNode.java:109) [bci: 120]
com.oracle.truffle.sl.nodes.access.SLWritePropertyCacheNodeGen.executeWrite(SLWritePropertyCacheNodeGen.java:111) [bci: 244]
com.oracle.truffle.sl.nodes.access.SLWritePropertyNode.write(SLWritePropertyNode.java:73) [bci: 9]
com.oracle.truffle.sl.nodes.access.SLWritePropertyNodeGen.executeGeneric(SLWritePropertyNodeGen.java:33) [bci: 47]
com.oracle.truffle.sl.nodes.access.SLWritePropertyNodeGen.executeVoid(SLWritePropertyNodeGen.java:41) [bci: 2]
com.oracle.truffle.sl.nodes.controlflow.SLBlockNode.executeVoid(SLBlockNode.java:84) [bci: 37]
com.oracle.truffle.sl.nodes.controlflow.SLFunctionBodyNode.executeGeneric(SLFunctionBodyNode.java:81) [bci: 5]
com.oracle.truffle.sl.nodes.SLRootNode.execute(SLRootNode.java:78) [bci: 28]
[0] state = IF(if=18#, else=0#)

...

Truffle 呼叫邊界檢測 #

Truffle 呼叫邊界檢測工具會檢測具有 TruffleCallBoundary 註解之方法的呼叫位置,並計算對這些方法的呼叫次數。它由以下一組旗標控制

  • --compiler.InstrumentBoundaries - 控制是否啟用檢測(truefalse,預設為 false
  • --compiler.InstrumentFilter - 過濾應執行檢測的方法(方法過濾語法,基本上是 <package>.<class>.<method>[.<signature>]
  • --compiler.InstrumentationTableSize - 控制檢測位置的最大數量
  • --compiler.InstrumentBoundariesPerInlineSite - 控制檢測是依每個 Truffle 邊界呼叫的宣告 (false) 執行,還是依該呼叫位置內嵌的每個呼叫堆疊 (true) 執行

此工具可與分支檢測工具一起使用。

假設您需要尋找經常發生的方法,但這些方法並未內嵌。識別 Truffle 呼叫邊界的通常步驟是先執行程式,並將 InstrumentBoundariesPerInlineSite 旗標設定為 false,然後在識別出問題方法後,將該旗標設定為 true,並設定 InstrumentFilter 以識別這些方法的特定呼叫堆疊。

與我們聯絡