diff --git a/jcl/src/java.base/share/classes/com/ibm/oti/vm/VM.java b/jcl/src/java.base/share/classes/com/ibm/oti/vm/VM.java index 58e7c7150f0..634f0063fc6 100644 --- a/jcl/src/java.base/share/classes/com/ibm/oti/vm/VM.java +++ b/jcl/src/java.base/share/classes/com/ibm/oti/vm/VM.java @@ -639,6 +639,9 @@ public static Properties internalGetProperties() { } /*[IF JFR_SUPPORT]*/ + +public static native boolean isJFREnabled(); + /** * Check if a JFR recording has been started. * diff --git a/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java b/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java index 045b6bf20ad..1cda70ec78b 100644 --- a/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java +++ b/jcl/src/java.base/share/classes/openj9/internal/tools/attach/target/DiagnosticUtils.java @@ -700,14 +700,17 @@ private static DiagnosticProperties doCheckpointJVM(String diagnosticCommand) { /*[ENDIF] CRAC_SUPPORT */ /*[IF JFR_SUPPORT]*/ - commandTable.put(DIAGNOSTICS_JFR_START, DiagnosticUtils::doJFR); - helpTable.put(DIAGNOSTICS_JFR_START, DIAGNOSTICS_JFR_START_HELP); + if (VM.isJFREnabled()) { + commandTable.put(DIAGNOSTICS_JFR_START, DiagnosticUtils::doJFR); + helpTable.put(DIAGNOSTICS_JFR_START, DIAGNOSTICS_JFR_START_HELP); - commandTable.put(DIAGNOSTICS_JFR_DUMP, DiagnosticUtils::doJFR); - helpTable.put(DIAGNOSTICS_JFR_DUMP, DIAGNOSTICS_JFR_DUMP_HELP); + commandTable.put(DIAGNOSTICS_JFR_DUMP, DiagnosticUtils::doJFR); + helpTable.put(DIAGNOSTICS_JFR_DUMP, DIAGNOSTICS_JFR_DUMP_HELP); + + commandTable.put(DIAGNOSTICS_JFR_STOP, DiagnosticUtils::doJFR); + helpTable.put(DIAGNOSTICS_JFR_STOP, DIAGNOSTICS_JFR_STOP_HELP); + } - commandTable.put(DIAGNOSTICS_JFR_STOP, DiagnosticUtils::doJFR); - helpTable.put(DIAGNOSTICS_JFR_STOP, DIAGNOSTICS_JFR_STOP_HELP); /*[ENDIF] JFR_SUPPORT */ } } diff --git a/runtime/jcl/common/com_ibm_oti_vm_VM.cpp b/runtime/jcl/common/com_ibm_oti_vm_VM.cpp index 2597b7eb756..8225d9f78c6 100644 --- a/runtime/jcl/common/com_ibm_oti_vm_VM.cpp +++ b/runtime/jcl/common/com_ibm_oti_vm_VM.cpp @@ -194,6 +194,14 @@ Java_com_ibm_oti_vm_VM_isJVMInSingleThreadedMode(JNIEnv *env, jclass unused) } #if defined(J9VM_OPT_JFR) +jboolean JNICALL +Java_com_ibm_oti_vm_VM_isJFREnabled(JNIEnv *env, jclass unused) +{ + J9JavaVM *vm = ((J9VMThread *)env)->javaVM; + + return vm->internalVMFunctions->isJFREnabled(vm) ? JNI_TRUE : JNI_FALSE; +} + jboolean JNICALL Java_com_ibm_oti_vm_VM_isJFRRecordingStarted(JNIEnv *env, jclass unused) { diff --git a/runtime/jcl/exports.cmake b/runtime/jcl/exports.cmake index 60023c9142f..f4b04862ac0 100644 --- a/runtime/jcl/exports.cmake +++ b/runtime/jcl/exports.cmake @@ -694,6 +694,7 @@ endif() if(J9VM_OPT_JFR) omr_add_exports(jclse + Java_com_ibm_oti_vm_VM_isJFREnabled Java_com_ibm_oti_vm_VM_isJFRRecordingStarted Java_com_ibm_oti_vm_VM_jfrDump Java_com_ibm_oti_vm_VM_setJFRRecordingFileName diff --git a/runtime/jcl/uma/jfr_jdk11_exports.xml b/runtime/jcl/uma/jfr_jdk11_exports.xml index 65b92c2ac63..4e458eb4f3b 100644 --- a/runtime/jcl/uma/jfr_jdk11_exports.xml +++ b/runtime/jcl/uma/jfr_jdk11_exports.xml @@ -20,6 +20,12 @@ OpenJDK Assembly Exception [2]. SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 --> + + + + + + diff --git a/runtime/jcl/uma/jfr_jdk21_exports.xml b/runtime/jcl/uma/jfr_jdk21_exports.xml index 56d4731ced9..5089f979f74 100644 --- a/runtime/jcl/uma/jfr_jdk21_exports.xml +++ b/runtime/jcl/uma/jfr_jdk21_exports.xml @@ -20,12 +20,6 @@ OpenJDK Assembly Exception [2]. SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 --> - - - - - - diff --git a/runtime/oti/j9consts.h b/runtime/oti/j9consts.h index 7d7cdfff0dd..8739a546fd9 100644 --- a/runtime/oti/j9consts.h +++ b/runtime/oti/j9consts.h @@ -369,6 +369,9 @@ extern "C" { #define J9_EXTENDED_RUNTIME2_RAMSTATE_SNAPSHOT_RUN 0x40000000 #define J9_EXTENDED_RUNTIME2_RAMSTATE_RESTORE_RUN 0x80000000 +/* constants for J9JavaVM.extendedRuntimeFlags3 */ +#define J9_EXTENDED_RUNTIME3_START_FLIGHT_RECORDING 0x1 + #define J9_OBJECT_HEADER_AGE_DEFAULT 0xA /* OBJECT_HEADER_AGE_DEFAULT */ #define J9_OBJECT_HEADER_SHAPE_MASK 0xE /* OBJECT_HEADER_SHAPE_MASK */ #define J9_OBJECT_HEADER_REMEMBERED_BITS_TO_SET 0x10 /* OBJECT_HEADER_LOWEST_REMEMBERED */ diff --git a/runtime/oti/j9nonbuilder.h b/runtime/oti/j9nonbuilder.h index b43c55a3aa3..aaacd66f410 100644 --- a/runtime/oti/j9nonbuilder.h +++ b/runtime/oti/j9nonbuilder.h @@ -5263,6 +5263,7 @@ typedef struct J9InternalVMFunctions { #endif /* defined(J9VM_ZOS_3164_INTEROPERABILITY) && (JAVA_SPEC_VERSION >= 17) */ #if defined(J9VM_OPT_JFR) jint (*initializeJFR)(struct J9JavaVM *vm, BOOLEAN lateInit); + jboolean (*isJFREnabled)(struct J9JavaVM *vm); jboolean (*isJFRRecordingStarted)(struct J9JavaVM *vm); void (*jfrDump)(struct J9VMThread *currentThread, BOOLEAN finalWrite); void (*jfrExecutionSample)(struct J9VMThread *currentThread, struct J9VMThread *sampleThread); @@ -5796,6 +5797,7 @@ typedef struct J9JavaVM { U_32 runtimeFlags; U_32 extendedRuntimeFlags; U_32 extendedRuntimeFlags2; + U_32 extendedRuntimeFlags3; UDATA zeroOptions; struct J9Pool* hotFieldClassInfoPool; omrthread_monitor_t hotFieldClassInfoPoolMutex; diff --git a/runtime/oti/jclprots.h b/runtime/oti/jclprots.h index 99c2c644af3..e5676219b56 100644 --- a/runtime/oti/jclprots.h +++ b/runtime/oti/jclprots.h @@ -1275,6 +1275,8 @@ Java_com_ibm_oti_vm_VM_isJVMInSingleThreadedMode(JNIEnv *env, jclass unused); #if defined(J9VM_OPT_JFR) jboolean JNICALL +Java_com_ibm_oti_vm_VM_isJFREnabled(JNIEnv *env, jclass unused); +jboolean JNICALL Java_com_ibm_oti_vm_VM_isJFRRecordingStarted(JNIEnv *env, jclass unused); void JNICALL Java_com_ibm_oti_vm_VM_jfrDump(JNIEnv *env, jclass unused); diff --git a/runtime/oti/jvminit.h b/runtime/oti/jvminit.h index f2ed58af9fb..6538530e8a3 100644 --- a/runtime/oti/jvminit.h +++ b/runtime/oti/jvminit.h @@ -587,6 +587,8 @@ enum INIT_STAGE { #define VMOPT_XXFLIGHTRECORDER "-XX:+FlightRecorder" #define VMOPT_XXNOFLIGHTRECORDER "-XX:-FlightRecorder" +#define VMOPT_XXSTARTFLIGHTRECORDING "-XX:StartFlightRecording" + #define VMOPT_XXCONTINUATIONCACHE "-XX:ContinuationCache:" #if JAVA_SPEC_VERSION >= 22 diff --git a/runtime/oti/vm_api.h b/runtime/oti/vm_api.h index 00897148e1b..524f48f87aa 100644 --- a/runtime/oti/vm_api.h +++ b/runtime/oti/vm_api.h @@ -5418,6 +5418,16 @@ hasMemoryScope(J9VMThread *walkThread, j9object_t scope); jint initializeJFR(J9JavaVM *vm, BOOLEAN lateInit); +/** + * Check if a JFR is enabled on the JVM. + * + * @param vm[in] the J9JavaVM + * + * @returns JNI_TRUE if a JFR enabled, JNI_FALSE otherwise + */ +jboolean +isJFREnabled(J9JavaVM *vm); + /** * Check if a JFR recording has been started. * diff --git a/runtime/vm/intfunc.c b/runtime/vm/intfunc.c index 75a9bc72126..953ba30c176 100644 --- a/runtime/vm/intfunc.c +++ b/runtime/vm/intfunc.c @@ -463,6 +463,7 @@ J9InternalVMFunctions J9InternalFunctions = { #endif /* defined(J9VM_ZOS_3164_INTEROPERABILITY) && (JAVA_SPEC_VERSION >= 17) */ #if defined(J9VM_OPT_JFR) initializeJFR, + isJFREnabled, isJFRRecordingStarted, jfrDump, jfrExecutionSample, diff --git a/runtime/vm/jfr.cpp b/runtime/vm/jfr.cpp index ddeeeaff221..0c5c174b867 100644 --- a/runtime/vm/jfr.cpp +++ b/runtime/vm/jfr.cpp @@ -852,6 +852,12 @@ initializeEventFields(J9VMThread *currentThread, J9JFREvent *event, UDATA eventT event->vmThread = currentThread; } +jboolean +isJFREnabled(J9JavaVM *vm) +{ + return J9_ARE_ANY_BITS_SET(vm->extendedRuntimeFlags2, J9_EXTENDED_RUNTIME2_JFR_ENABLED) ? JNI_TRUE : JNI_FALSE; +} + jboolean isJFRRecordingStarted(J9JavaVM *vm) { diff --git a/runtime/vm/jvminit.c b/runtime/vm/jvminit.c index f0a57ee5154..5e68e0edb5c 100644 --- a/runtime/vm/jvminit.c +++ b/runtime/vm/jvminit.c @@ -4321,12 +4321,19 @@ processVMArgsFromFirstToLast(J9JavaVM * vm) { IDATA flightRecorder = FIND_AND_CONSUME_VMARG(EXACT_MATCH, VMOPT_XXFLIGHTRECORDER, NULL); IDATA noFlightRecorder = FIND_AND_CONSUME_VMARG(EXACT_MATCH, VMOPT_XXNOFLIGHTRECORDER, NULL); - if (flightRecorder > noFlightRecorder) { - vm->extendedRuntimeFlags2 |= J9_EXTENDED_RUNTIME2_JFR_ENABLED; - } else if (flightRecorder < noFlightRecorder) { + + vm->extendedRuntimeFlags2 |= J9_EXTENDED_RUNTIME2_JFR_ENABLED; + + if (flightRecorder < noFlightRecorder) { vm->extendedRuntimeFlags2 &= ~(UDATA)J9_EXTENDED_RUNTIME2_JFR_ENABLED; } } + { + if (0 <= FIND_AND_CONSUME_VMARG(EXACT_MATCH, VMOPT_XXSTARTFLIGHTRECORDING, NULL)) { + vm->extendedRuntimeFlags3 |= J9_EXTENDED_RUNTIME3_START_FLIGHT_RECORDING; + } + } + #endif /* defined(J9VM_OPT_JFR) */ if (FIND_AND_CONSUME_VMARG(EXACT_MATCH, VMOPT_XXKEEPJNIIDS, NULL) != -1) { @@ -7585,8 +7592,10 @@ protectedInitializeJavaVM(J9PortLibrary* portLibrary, void * userData) #if defined(J9VM_OPT_JFR) if (J9_ARE_ANY_BITS_SET(vm->extendedRuntimeFlags2, J9_EXTENDED_RUNTIME2_JFR_ENABLED)) { - if (JNI_OK != initializeJFR(vm, FALSE)) { - goto error; + if (J9_ARE_ANY_BITS_SET(vm->extendedRuntimeFlags3, J9_EXTENDED_RUNTIME3_START_FLIGHT_RECORDING)) { + if (JNI_OK != initializeJFR(vm, FALSE)) { + goto error; + } } } #endif /* defined(J9VM_OPT_JFR) */ diff --git a/test/functional/cmdLineTests/jfr/jfr.xml b/test/functional/cmdLineTests/jfr/jfr.xml index 9e0675f81d6..1f8f93c2891 100644 --- a/test/functional/cmdLineTests/jfr/jfr.xml +++ b/test/functional/cmdLineTests/jfr/jfr.xml @@ -27,22 +27,56 @@ SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-ex - $EXE$ -XX:+FlightRecorder --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.TriggerExecutionSample + $EXE$ -XX:StartFlightRecording --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.TriggerExecutionSample - $EXE$ -XX:+FlightRecorder --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.WorkLoad + $EXE$ -XX:StartFlightRecording --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.WorkLoad All runs complete. Exception - $EXE$ -XX:+FlightRecorder --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.WorkLoad 200 20000 200 + $EXE$ -XX:StartFlightRecording --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.WorkLoad 200 20000 200 All runs complete. Exception - + $EXE$ --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.VMAPITest All runs complete. Failed + + $EXE$ --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.JFRCMDLineTest + All runs complete + JFR recording has started + JFR is enabled + + + $EXE$ -XX:+FlightRecorder --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.JFRCMDLineTest + All runs complete + JFR recording has started + JFR is enabled + + + $EXE$ -XX:-FlightRecorder --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.JFRCMDLineTest + All runs complete + JFR recording has started + JFR is enabled + JFR is not enabled + + + $EXE$ -XX:-FlightRecorder -XX:StartFlightRecording --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.JFRCMDLineTest + All runs complete + JFR recording has started + JFR is enabled + JFR is not enabled + + + $EXE$ -XX:StartFlightRecording --add-exports java.base/com.ibm.oti.vm=ALL-UNNAMED -cp $RESJAR$ org.openj9.test.JFRCMDLineTest + All runs complete + JFR recording has started + JFR is enabled + JFR is not enabled + + diff --git a/test/functional/cmdLineTests/jfr/src/org/openj9/test/JFRCMDLineTest.java b/test/functional/cmdLineTests/jfr/src/org/openj9/test/JFRCMDLineTest.java new file mode 100644 index 00000000000..c151516e10e --- /dev/null +++ b/test/functional/cmdLineTests/jfr/src/org/openj9/test/JFRCMDLineTest.java @@ -0,0 +1,46 @@ +/* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + */ +package org.openj9.test; + +import com.ibm.oti.vm.VM; + +public class JFRCMDLineTest { + + public static void main(String[] args) throws Throwable { + + final WorkLoad workLoad = new WorkLoad(200, 20000, 200); + + if (VM.isJFRRecordingStarted()) { + System.out.println("JFR recording has started"); + } else { + System.out.println("JFR recording has not started"); + } + + if (VM.isJFREnabled()) { + System.out.println("JFR is enabled"); + } else { + System.out.println("JFR is not enabled"); + } + + System.out.println("All runs complete"); + } +}