Experimental feature in GraalVM

GraalVM Insight

GraalVM Insight 是一個多用途、彈性的工具,可追蹤程式執行時的行為並收集深入解析。

此工具的動態特性可協助使用者選擇性地將追蹤點套用至已在執行的應用程式,而不會損失效能。GraalVM Insight 還可詳細存取程式的執行時行為,讓使用者可以在呼叫或配置點檢查數值和類型。此工具還允許使用者修改計算值、中斷執行,並快速實驗行為變更,而無需修改應用程式程式碼。此工具的實作詳細資訊可以在API 規格中找到。

本頁提供截至 20.1 版 GraalVM Insight 的相關資訊。若要了解 20.0 和 19.3 版的 Insight,請前往此處

開始使用 #

  1. 使用以下內容建立一個簡單的 source-tracing.js 指令碼
    insight.on('source', function(ev) {
     if (ev.characters) {
         print(`Loading ${ev.characters.length} characters from ${ev.name}`);
     }
    });
    
  2. 安裝Node.js 執行階段後,使用 --insight 工具啟動 node 啟動器,並觀察正在載入和評估的指令碼
    ./bin/node --insight=source-tracing.js --js.print --experimental-options -e "print('The result: ' + 6 * 7)" | tail -n 10
    Loading 215 characters from internal/modules/esm/transform_source.js
    Loading 12107 characters from internal/modules/esm/translators.js
    Loading 1756 characters from internal/modules/esm/create_dynamic_module.js
    Loading 12930 characters from internal/vm/module.js
    Loading 2710 characters from internal/modules/run_main.js
    Loading 308 characters from module.js
    Loading 10844 characters from internal/source_map/source_map.js
    Loading 170 characters from [eval]-wrapper
    Loading 29 characters from [eval]
    The result: 42
    

    source-tracing.js 指令碼使用提供的 insight 物件將來源接聽程式附加至執行階段。每當載入指令碼時,接聽程式就會收到通知,並可以採取動作,例如列印處理的指令碼長度和名稱。

Insight 資訊可以收集到列印陳述式或直方圖。以下 function-hotness-tracing.js 指令碼會計算所有方法呼叫,並在程式執行結束時傾印最常呼叫的方法

var map = new Map();

function dumpHotness() {
    print("==== Hotness Top 10 ====");
    var count = 10;
    var digits = 3;
    Array.from(map.entries()).sort((one, two) => two[1] - one[1]).forEach(function (entry) {
        var number = entry[1].toString();
        if (number.length >= digits) {
            digits = number.length;
        } else {
            number = Array(digits - number.length + 1).join(' ') + number;
        }
        if (count-- > 0) print(`${number} calls to ${entry[0]}`);
    });
    print("========================");
}

insight.on('enter', function(ev) {
    var cnt = map.get(ev.name);
    if (cnt) {
        cnt = cnt + 1;
    } else {
        cnt = 1;
    }
    map.set(ev.name, cnt);
}, {
    roots: true
});

insight.on('close', dumpHotness);

map 是一個在 Insight 指令碼內共用的全域變數,可讓程式碼在 insight.on('enter') 函式和 dumpHotness 函式之間共用資料。後者會在節點程序執行結束時執行 (透過 insight.on('close', dumpHotness 註冊)。當 node 程序結束時,會印出包含函式呼叫名稱和計數的表格。

如下叫用它

./bin/node --insight=function-hotness-tracing.js --js.print --experimental-options -e "print('The result: ' + 6 * 7)"
The result: 42
==== Hotness Top 10 ====
516 calls to isPosixPathSeparator
311 calls to :=>
269 calls to E
263 calls to makeNodeErrorWithCode
159 calls to :anonymous
157 calls to :program
 58 calls to getOptionValue
 58 calls to getCLIOptionsFromBinding
 48 calls to validateString
 43 calls to hideStackFrames
========================

多語言追蹤 #

先前的範例是以 JavaScript 編寫的,但由於 GraalVM 的多語言特性,您可以採用相同的工具並在以 Ruby 語言編寫的程式中使用它。

  1. 建立 source-trace.js 檔案
    insight.on('source', function(ev) {
    if (ev.uri.indexOf('gems') === -1) {
      let n = ev.uri.substring(ev.uri.lastIndexOf('/') + 1);
      print('JavaScript instrument observed load of ' + n);
    }
    });
    
  2. 準備 helloworld.rb Ruby 檔案
    puts 'Hello from GraalVM Ruby!'
    
  3. 將 JavaScript 工具套用至 Ruby 程式
    ./bin/ruby --polyglot --insight=source-trace.js helloworld.rb
    JavaScript instrument observed load of helloworld.rb
    Hello from GraalVM Ruby!
    

    必須使用 --polyglot 參數啟動 Ruby 啟動器,因為 source-tracing.js 指令碼仍以 JavaScript 編寫。

使用者可以在 GraalVM 之上檢測任何語言,但 Insight 指令碼也可以用 GraalVM 支援的任何語言編寫 (使用 Truffle 語言實作架構實作)。

  1. 建立 source-tracing.rb Ruby 檔案
    puts "Ruby: Initializing GraalVM Insight script"
    insight.on('source', ->(ev) {
     name = ev[:name]
     puts "Ruby: observed loading of #{name}"
    })
    puts 'Ruby: Hooks are ready!'
    
  2. 啟動 Node.js 應用程式並使用 Ruby 指令碼檢測它
    ./bin/node --polyglot --insight=source-tracing.rb -e "console.log('With Ruby: ' + 6 * 7)" | grep Ruby
    Ruby: Initializing GraalVM Insight script
    Ruby: Hooks are ready!
    Ruby: observed loading of internal/per_context/primordials.js
    Ruby: observed loading of internal/per_context/setup.js
    Ruby: observed loading of internal/per_context/domexception.js
    ....
    Ruby: observed loading of internal/modules/cjs/loader.js
    Ruby: observed loading of vm.js
    Ruby: observed loading of fs.js
    Ruby: observed loading of internal/fs/utils.js
    Ruby: observed loading of [eval]-wrapper
    Ruby: observed loading of [eval]
    With Ruby: 42
    

檢查數值 #

GraalVM Insight 不僅允許追蹤程式執行所在的位置,還可以存取程式執行期間的本機變數和函式引數值。例如,您可以編寫一個工具來顯示函式 fib 中引數 n 的值

insight.on('enter', function(ctx, frame) {
   print('fib for ' + frame.n);
}, {
   roots: true,
   rootNameFilter: (name) => 'fib' === name
});

此工具會使用第二個函式引數 frame 來存取每個檢測函式內部的本機變數值。上述指令碼也會使用 rootNameFilter 將其掛鉤僅套用至名為 fib 的函式

function fib(n) {
  if (n < 1) return 0;
  if (n < 2) return 1;
  else return fib(n - 1) + fib(n - 2);
}
print("Two is the result " + fib(3));

當工具儲存在 fib-trace.js 檔案中,而實際程式碼在 fib.js 中時,叫用以下命令會產生有關程式執行和在函式呼叫之間傳遞的參數的詳細資訊

./bin/node --insight=fib-trace.js --js.print --experimental-options fib.js
fib for 3
fib for 2
fib for 1
fib for 0
fib for 1
Two is the result 2

Insight 深入探討 #

任何具備中等技能的開發人員都可以輕鬆建立自己的所謂「掛鉤」,並將其動態套用至實際程式。這可以在不影響執行速度的情況下,提供對應用程式執行和行為的最終深入解析。

若要繼續學習並深入探討 GraalVM Insight,請前往Insight 手冊,其中從必要的 HelloWorld 範例開始,然後示範更具挑戰性的任務。

將 GraalVM Insight 嵌入應用程式 #

可以使用 多語言內容 API 將 GraalVM 語言 (使用 Truffle 架構實作的語言) 嵌入自訂應用程式。也可以透過相同的 API 控制 GraalVM Insight。

閱讀嵌入文件,以了解如何以安全的方式將 GraalVM Insight 功能整合到應用程式中。

使用 GraalVM Insight 追蹤 #

GraalVM Insight 會動態地將追蹤功能新增至現有程式碼。像平常一樣編寫您的應用程式,並在需要時動態套用 Open Telemetry 追蹤。請在專用指南中閱讀有關 Insight 和 Jaeger 整合的更多資訊。

API 規格 #

如果您對實作詳細資訊感興趣,請查看API 規格。您可以在其中找到有關 insight 物件屬性、函式等的資訊。

與我們聯繫