#chiroito ’s blog

Java を中心とした趣味の技術について

JFR Under the hood : StartFlightRecordingのVM引数をJFRへ適用する処理

結論

VM起動引数として指定された-XX:StartFlightRecording=xxx=xxx,yyy=yyyという形式のパラメータは、VMを起動する途中で、JFRのパラメータを初期化するメソッド(JfrOptionSet::initialize)でjcmd の方法に変換されます。その後、VMはJFRのパラメータを設定するメソッド(JfrOptionSet::configure)で内部的に jcmd JFR.configure を実行することで設定を適用します。

VMを起動する処理

VMを起動する処理の一部としてjdk/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cppにあるon_create_vm_2メソッドが実行されます。 この処理では、主に以下の処理が実行されます。

  1. JFRのパラメータを初期化JfrOptionSet::initialize
  2. 設定JfrOptionSet::configure

VM を作成する処理の一部

bool JfrRecorder::on_create_vm_2() {
(略)
  Thread* const thread = Thread::current();
  if (!JfrOptionSet::initialize(thread)) {
    return false;
  }
  if (!register_jfr_dcmds()) {
    return false;
  }
(略)
    if (!validate_recording_options(thread)) {
      return false;
    }
    if (!JfrOptionSet::configure(thread)) {
      return false;
    }
(略)
}

1. JFR のパラメータを初期化

この処理では、主に以下の処理が実行されます。

  • デフォルト値の読み込み
  • VM起動引数をjcmdの形式に変換
  • 過去の形式の古いパラメータ(ObsoleteOption)が使われていたらメッセージを出力
bool JfrOptionSet::initialize(Thread* thread) {
  register_parser_options();
  if (!parse_flight_recorder_options_internal(thread)) {
    return false;
  }
(略)
}

2. 設定

jcmd を使って設定します。jcmd JFR.configureの実体であるJfrConfigureFlightRecorderDCmdクラスのインスタンスに入力されたパラメータを指定して内部的にコマンドを実行(execute)します。

bool JfrOptionSet::configure(TRAPS) {
(略)
  // delegate to DCmd execution
  JfrConfigureFlightRecorderDCmd configure(&st, false);
(略)
  configure._stack_depth.set_is_set(_dcmd_stackdepth.is_set());
  configure._stack_depth.set_value(_dcmd_stackdepth.value());
(略)
  configure.set_verbose(false);
  configure.execute(DCmd_Source_Internal, THREAD);
(略)
}

どこに設定は格納されているのか?

設定は Java と C/C++ の範囲でそれぞれ格納されています。Java は jdk.jfrモジュールのjdk/jfr/internal/Optionsクラス、C/C++ はhotspot/share/jfr/recorder/jfrRecorder.cpp に格納されます。

jcmd JFR.configure では Options クラスに格納します。Options クラスでは setter で自分の持つフィールドに格納すると共に、JNI で C/++ のメソッドを呼び出して jfrRecorderにも格納します。

Optionsで設定を変更するメソッド

public static synchronized void setGlobalBufferCount(long globalBufCount) {
    jvm.setGlobalBufferCount(globalBufCount);
    globalBufferCount = globalBufCount;
}

jfrRecorder.cppでC/C++ で設定値が格納される変数

jlong JfrOptionSet::_max_chunk_size = 0;
jlong JfrOptionSet::_global_buffer_size = 0;
jlong JfrOptionSet::_thread_buffer_size = 0;
jlong JfrOptionSet::_memory_size = 0;
jlong JfrOptionSet::_num_global_buffers = 0;
jlong JfrOptionSet::_old_object_queue_size = 0;
u4 JfrOptionSet::_stack_depth = STACK_DEPTH_DEFAULT;
jboolean JfrOptionSet::_sample_threads = JNI_TRUE;
jboolean JfrOptionSet::_retransform = JNI_TRUE;

JNIのマッピングは hotspot/share/jfr/jni/jfrJniMethodRegistration.cpphotspot/share/jfr/jni/jfrJniMethod.cppで行われてます。