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) {
     print(`Loading ${ev.characters.length} characters from ${ev.name}`);
    });
    
  2. JAVA_HOMEをGraalVMホーム・ディレクトリに設定した後、--insightツールを使用してnodeランチャを起動し、どのスクリプトがロードおよび評価されるかを確認します:
    $JAVA_HOME/bin/node --insight=source-tracing.js --js.print -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情報は、print文またはヒストグラムに収集できます。次のfunction-hotness-tracing.jsスクリプトは、すべてのメソッド呼出しをカウントし、プログラムの実行が終了した時点で最も頻度が高かったメソッド呼出しをダンプします:

var map = new Map();

function dumpHotness() {
    print("==== Hotness Top 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 (number > 10) 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プロセスの終了時に、関数呼出しの名前と数を含む表が出力されます。

次のように起動します:

$JAVA_HOME/bin/node --insight=function-hotness-tracing.js --js.print -e "print('The result: ' + 6 * 7)"
The result: 42
==== Hotness Top 10 ====
543 calls to isPosixPathSeparator
211 calls to E
211 calls to makeNodeErrorWithCode
205 calls to NativeModule
198 calls to uncurryThis
154 calls to :=>
147 calls to nativeModuleRequire
145 calls to NativeModule.compile
 55 calls to internalBinding
 53 calls to :anonymous
 49 calls to :program
 37 calls to getOptionValue
 24 calls to copyProps
 18 calls to validateString
 13 calls to copyPrototype
 13 calls to hideStackFrames
 13 calls to addReadOnlyProcessAlias
========================

ポリグロット・トレース

前述の例は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プログラムに適用します:
    $JAVA_HOME/bin/ruby --jvm --polyglot --insight=source-trace.js helloworld.rb
    JavaScript instrument observed load of helloworld.rb
    Hello from GraalVM Ruby!
    

    source-tracing.jsスクリプトはJavaScriptで記述されたままであるため、--polyglotパラメータを使用してRubyランチャを起動する必要があります。

ユーザーは、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スクリプトを使用してインストゥルメントします:
    $JAVA_HOME/bin/node --jvm  --polyglot --insight=source-tracing.rb --js.print -e "print('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
});

このインストゥルメントは、2番目の関数の引数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にある場合、次のコマンドを起動すると、プログラムの実行および関数の呼出し間で渡されるパラメータに関する詳細情報が生成されます:

$JAVA_HOME/bin/node --insight=fib-trace.js --js.print 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のアプリケーションへの組込み

GraalVM言語(Truffleフレームワークで実装された言語)は、ポリグロット・コンテキストAPIを介してカスタム・アプリケーションに組み込むことができます。GraalVM Insightも、同じAPIを介して制御できます。

GraalVM Insightの機能を安全な方法でアプリケーションに統合する方法については、組込みに関するドキュメントを参照してください。

GraalVM Insightでのトレース

GraalVM Insightでは、トレース機能が既存のコードに動的に追加されます。アプリケーションを通常どおりに記述し、必要に応じてテレメトリを開くトレースを動的に適用します。InsightとJaegerの統合の詳細は、専用ガイドを参照してください。

APIの仕様

実装の詳細については、APIの仕様を参照してください。そこには、insightオブジェクトのプロパティ、関数などに関する情報が記載されています。