8205906: jdk.jfr.jcmd.TestJcmdDumpLimited fails due to erronous processing of -XX:FlightRecorderOptions
Reviewed-by: egahlin
--- a/src/hotspot/share/jfr/jfr.cpp Thu Jun 28 11:32:32 2018 +0200
+++ b/src/hotspot/share/jfr/jfr.cpp Thu Jun 28 11:37:02 2018 +0200
@@ -85,12 +85,12 @@
LeakProfiler::oops_do(is_alive, f);
}
-bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* tail) {
- return JfrOptionSet::parse_start_flight_recording_option(option, tail);
+bool Jfr::on_flight_recorder_option(const JavaVMOption** option, char* delimiter) {
+ return JfrOptionSet::parse_flight_recorder_option(option, delimiter);
}
-bool Jfr::on_flight_recorder_option(const JavaVMOption** option, char* tail) {
- return JfrOptionSet::parse_flight_recorder_option(option, tail);
+bool Jfr::on_start_flight_recording_option(const JavaVMOption** option, char* delimiter) {
+ return JfrOptionSet::parse_start_flight_recording_option(option, delimiter);
}
Thread* Jfr::sampler_thread() {
--- a/src/hotspot/share/jfr/jfr.hpp Thu Jun 28 11:32:32 2018 +0200
+++ b/src/hotspot/share/jfr/jfr.hpp Thu Jun 28 11:37:02 2018 +0200
@@ -49,8 +49,8 @@
static void on_thread_exit(JavaThread* thread);
static void on_thread_destruct(Thread* thread);
static void on_vm_shutdown(bool exception_handler = false);
- static bool on_start_flight_recording_option(const JavaVMOption** option, char* tail);
- static bool on_flight_recorder_option(const JavaVMOption** option, char* tail);
+ static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
+ static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
static Thread* sampler_thread();
};
--- a/src/hotspot/share/jfr/recorder/jfrRecorder.cpp Thu Jun 28 11:32:32 2018 +0200
+++ b/src/hotspot/share/jfr/recorder/jfrRecorder.cpp Thu Jun 28 11:37:02 2018 +0200
@@ -46,6 +46,7 @@
#include "runtime/handles.inline.hpp"
#include "runtime/flags/jvmFlag.hpp"
#include "runtime/globals.hpp"
+#include "utilities/growableArray.hpp"
static bool is_disabled_on_command_line() {
static const size_t length = strlen("FlightRecorder");
@@ -85,31 +86,30 @@
return JfrTime::initialize();
}
-static JfrStartFlightRecordingDCmd* _startup_recording = NULL;
+static GrowableArray<JfrStartFlightRecordingDCmd*>* dcmd_recordings_array = NULL;
-static void release_startup_recording() {
- if (_startup_recording != NULL) {
- delete _startup_recording;
- _startup_recording = NULL;
+static void release_recordings() {
+ if (dcmd_recordings_array != NULL) {
+ const int length = dcmd_recordings_array->length();
+ for (int i = 0; i < length; ++i) {
+ delete dcmd_recordings_array->at(i);
+ }
+ delete dcmd_recordings_array;
+ dcmd_recordings_array = NULL;
}
}
static void teardown_startup_support() {
- release_startup_recording();
- JfrOptionSet::release_startup_recordings();
+ release_recordings();
+ JfrOptionSet::release_startup_recording_options();
}
-
// Parsing options here to detect errors as soon as possible
-static bool parse_recording_options(const char* options, TRAPS) {
+static bool parse_recording_options(const char* options, JfrStartFlightRecordingDCmd* dcmd_recording, TRAPS) {
assert(options != NULL, "invariant");
- if (_startup_recording != NULL) {
- delete _startup_recording;
- }
+ assert(dcmd_recording != NULL, "invariant");
CmdLine cmdline(options, strlen(options), true);
- _startup_recording = new (ResourceObj::C_HEAP, mtTracing) JfrStartFlightRecordingDCmd(tty, true);
- assert(_startup_recording != NULL, "invariant");
- _startup_recording->parse(&cmdline, ',', THREAD);
+ dcmd_recording->parse(&cmdline, ',', THREAD);
if (HAS_PENDING_EXCEPTION) {
java_lang_Throwable::print(PENDING_EXCEPTION, tty);
CLEAR_PENDING_EXCEPTION;
@@ -119,24 +119,30 @@
}
static bool validate_recording_options(TRAPS) {
- const GrowableArray<const char*>* startup_options = JfrOptionSet::startup_recordings();
- if (startup_options == NULL) {
+ const GrowableArray<const char*>* options = JfrOptionSet::startup_recording_options();
+ if (options == NULL) {
return true;
}
- const int length = startup_options->length();
+ const int length = options->length();
assert(length >= 1, "invariant");
+ assert(dcmd_recordings_array == NULL, "invariant");
+ dcmd_recordings_array = new (ResourceObj::C_HEAP, mtTracing)GrowableArray<JfrStartFlightRecordingDCmd*>(length, true, mtTracing);
+ assert(dcmd_recordings_array != NULL, "invariant");
for (int i = 0; i < length; ++i) {
- if (!parse_recording_options(startup_options->at(i), THREAD)) {
+ JfrStartFlightRecordingDCmd* const dcmd_recording = new(ResourceObj::C_HEAP, mtTracing) JfrStartFlightRecordingDCmd(tty, true);
+ assert(dcmd_recording != NULL, "invariant");
+ dcmd_recordings_array->append(dcmd_recording);
+ if (!parse_recording_options(options->at(i), dcmd_recording, THREAD)) {
return false;
}
}
return true;
}
-static bool launch_recording(TRAPS) {
- assert(_startup_recording != NULL, "invariant");
+static bool launch_recording(JfrStartFlightRecordingDCmd* dcmd_recording, TRAPS) {
+ assert(dcmd_recording != NULL, "invariant");
log_trace(jfr, system)("Starting a recording");
- _startup_recording->execute(DCmd_Source_Internal, Thread::current());
+ dcmd_recording->execute(DCmd_Source_Internal, THREAD);
if (HAS_PENDING_EXCEPTION) {
log_debug(jfr, system)("Exception while starting a recording");
CLEAR_PENDING_EXCEPTION;
@@ -146,31 +152,20 @@
return true;
}
-static bool launch_recordings(const GrowableArray<const char*>* startup_options, TRAPS) {
- assert(startup_options != NULL, "invariant");
- const int length = startup_options->length();
- assert(length >= 1, "invariant");
- if (length == 1) {
- // already parsed and ready, launch it
- return launch_recording(THREAD);
- }
- for (int i = 0; i < length; ++i) {
- parse_recording_options(startup_options->at(i), THREAD);
- if (!launch_recording(THREAD)) {
- return false;
+static bool launch_recordings(TRAPS) {
+ bool result = true;
+ if (dcmd_recordings_array != NULL) {
+ const int length = dcmd_recordings_array->length();
+ assert(length >= 1, "invariant");
+ for (int i = 0; i < length; ++i) {
+ if (!launch_recording(dcmd_recordings_array->at(i), THREAD)) {
+ result = false;
+ break;
+ }
}
}
- return true;
-}
-
-static bool startup_recordings(TRAPS) {
- const GrowableArray<const char*>* startup_options = JfrOptionSet::startup_recordings();
- if (startup_options == NULL) {
- return true;
- }
- const bool ret = launch_recordings(startup_options, THREAD);
teardown_startup_support();
- return ret;
+ return result;
}
static void log_jdk_jfr_module_resolution_error(TRAPS) {
@@ -180,13 +175,20 @@
JfrJavaSupport::is_jdk_jfr_module_available(&stream, THREAD);
}
-bool JfrRecorder::on_vm_start() {
- if (DumpSharedSpaces && (JfrOptionSet::startup_recordings() != NULL)) {
+static bool is_cds_dump_requested() {
+ // we will not be able to launch recordings if a cds dump is being requested
+ if (DumpSharedSpaces && (JfrOptionSet::startup_recording_options() != NULL)) {
warning("JFR will be disabled during CDS dumping");
teardown_startup_support();
return true;
}
- const bool in_graph = JfrJavaSupport::is_jdk_jfr_module_available();
+ return false;
+}
+
+bool JfrRecorder::on_vm_start() {
+ if (is_cds_dump_requested()) {
+ return true;
+ }
Thread* const thread = Thread::current();
if (!JfrOptionSet::initialize(thread)) {
return false;
@@ -194,10 +196,13 @@
if (!register_jfr_dcmds()) {
return false;
}
- if (!validate_recording_options(thread)) {
- return false;
- }
+
+ const bool in_graph = JfrJavaSupport::is_jdk_jfr_module_available();
+
if (in_graph) {
+ if (!validate_recording_options(thread)) {
+ return false;
+ }
if (!JfrJavaEventWriter::initialize()) {
return false;
}
@@ -205,14 +210,17 @@
return false;
}
}
+
if (!is_enabled()) {
return true;
}
+
if (!in_graph) {
log_jdk_jfr_module_resolution_error(thread);
return false;
}
- return startup_recordings(thread);
+
+ return launch_recordings(thread);
}
static bool _created = false;
--- a/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp Thu Jun 28 11:32:32 2018 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrOptionSet.cpp Thu Jun 28 11:37:02 2018 +0200
@@ -665,59 +665,69 @@
return true;
}
-static GrowableArray<const char*>* startup_recording_array = NULL;
-
-bool JfrOptionSet::parse_start_flight_recording_option(const JavaVMOption** option, char* tail) {
- assert(option != NULL, "invariant");
- assert(tail != NULL, "invariant");
- assert((*option)->optionString != NULL, "invariant");
- assert(strncmp((*option)->optionString, "-XX:StartFlightRecording", 24) == 0, "invariant");
- const char* param_string = NULL;
- if (*tail == '\0') {
- // Add dummy dumponexit=false so -XX:StartFlightRecording can be used without a parameter.
- // The existing option->optionString points to stack memory so no need to deallocate.
- const_cast<JavaVMOption*>(*option)->optionString = (char*)"-XX:StartFlightRecording=dumponexit=false";
- param_string = (*option)->optionString + 25;
- } else {
- *tail = '='; // ":" -> "="
- param_string = tail + 1;
- }
- assert(param_string != NULL, "invariant");
- const size_t param_length = strlen(param_string);
+static const char XXFlightRecorderOptions[] = "-XX:FlightRecorderOptions";
- if (startup_recording_array == NULL) {
- startup_recording_array = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<const char*>(8, true, mtTracing);
- }
- assert(startup_recording_array != NULL, "invariant");
- char* startup_options = NEW_C_HEAP_ARRAY(char, param_length + 1, mtTracing);
- strncpy(startup_options, param_string, strlen(param_string) + 1);
- assert(strncmp(param_string, startup_options, param_length) == 0, "invariant");
- startup_recording_array->append(startup_options);
- return false;
-}
-
-const GrowableArray<const char*>* JfrOptionSet::startup_recordings() {
- return startup_recording_array;
-}
-
-void JfrOptionSet::release_startup_recordings() {
- if (startup_recording_array != NULL) {
- for (int i = 0; i < startup_recording_array->length(); ++i) {
- FREE_C_HEAP_ARRAY(char, startup_recording_array->at(i));
- }
- }
- delete startup_recording_array;
- startup_recording_array = NULL;
-}
-
-bool JfrOptionSet::parse_flight_recorder_option(const JavaVMOption** option, char* tail) {
+bool JfrOptionSet::parse_flight_recorder_option(const JavaVMOption** option, char* delimiter) {
assert(option != NULL, "invariant");
- assert(tail != NULL, "invariant");
+ assert(delimiter != NULL, "invariant");
assert((*option)->optionString != NULL, "invariant");
- assert(strncmp((*option)->optionString, "-XX:FlightRecorderOptions", 25) == 0, "invariant");
- if (tail != NULL) {
- *tail = '='; // ":" -> "="
+ assert(strncmp((*option)->optionString, XXFlightRecorderOptions, sizeof XXFlightRecorderOptions - 1) == 0, "invariant");
+ if (*delimiter == '\0') {
+ // -XX:FlightRecorderOptions without any delimiter and values
+ } else {
+ // -XX:FlightRecorderOptions[=|:]
+ // set delimiter to '='
+ *delimiter = '=';
}
return false;
}
+static GrowableArray<const char*>* startup_recording_options_array = NULL;
+static const char XXStartFlightRecordingOption[] = "-XX:StartFlightRecording";
+
+bool JfrOptionSet::parse_start_flight_recording_option(const JavaVMOption** option, char* delimiter) {
+ assert(option != NULL, "invariant");
+ assert(delimiter != NULL, "invariant");
+ assert((*option)->optionString != NULL, "invariant");
+ assert(strncmp((*option)->optionString, XXStartFlightRecordingOption, sizeof XXStartFlightRecordingOption - 1) == 0, "invariant");
+ const char* value = NULL;
+ if (*delimiter == '\0') {
+ // -XX:StartFlightRecording without any delimiter and values
+ // Add dummy value "dumponexit=false" so -XX:StartFlightRecording can be used without explicit values.
+ // The existing option->optionString points to stack memory so no need to deallocate.
+ const_cast<JavaVMOption*>(*option)->optionString = (char*)"-XX:StartFlightRecording=dumponexit=false";
+ value = (*option)->optionString + sizeof XXStartFlightRecordingOption;
+ } else {
+ // -XX:StartFlightRecording[=|:]
+ // set delimiter to '='
+ *delimiter = '=';
+ value = delimiter + 1;
+ }
+ assert(value != NULL, "invariant");
+ const size_t value_length = strlen(value);
+
+ if (startup_recording_options_array == NULL) {
+ startup_recording_options_array = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<const char*>(8, true, mtTracing);
+ }
+ assert(startup_recording_options_array != NULL, "invariant");
+ char* const startup_value = NEW_C_HEAP_ARRAY(char, value_length + 1, mtTracing);
+ strncpy(startup_value, value, value_length + 1);
+ assert(strncmp(startup_value, value, value_length) == 0, "invariant");
+ startup_recording_options_array->append(startup_value);
+ return false;
+}
+
+const GrowableArray<const char*>* JfrOptionSet::startup_recording_options() {
+ return startup_recording_options_array;
+}
+
+void JfrOptionSet::release_startup_recording_options() {
+ if (startup_recording_options_array != NULL) {
+ const int length = startup_recording_options_array->length();
+ for (int i = 0; i < length; ++i) {
+ FREE_C_HEAP_ARRAY(char, startup_recording_options_array->at(i));
+ }
+ delete startup_recording_options_array;
+ startup_recording_options_array = NULL;
+ }
+}
--- a/src/hotspot/share/jfr/recorder/service/jfrOptionSet.hpp Thu Jun 28 11:32:32 2018 +0200
+++ b/src/hotspot/share/jfr/recorder/service/jfrOptionSet.hpp Thu Jun 28 11:37:02 2018 +0200
@@ -78,11 +78,10 @@
static bool sample_protection();
DEBUG_ONLY(static void set_sample_protection(jboolean protection);)
- static bool parse_start_flight_recording_option(const JavaVMOption** option, char* tail);
- static bool parse_flight_recorder_option(const JavaVMOption** option, char* tail);
-
- static const GrowableArray<const char*>* startup_recordings();
- static void release_startup_recordings();
+ static bool parse_flight_recorder_option(const JavaVMOption** option, char* delimiter);
+ static bool parse_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
+ static const GrowableArray<const char*>* startup_recording_options();
+ static void release_startup_recording_options();
};
#endif // SHARE_VM_JFR_RECORDER_SERVICE_JFROPTIONSET_HPP
--- a/test/jdk/jdk/jfr/jcmd/TestJcmdDumpLimited.java Thu Jun 28 11:32:32 2018 +0200
+++ b/test/jdk/jdk/jfr/jcmd/TestJcmdDumpLimited.java Thu Jun 28 11:37:02 2018 +0200
@@ -48,7 +48,7 @@
* @summary The test verifies JFR.dump command
* @key jfr
* @library /test/lib /test/jdk
- * @run main/othervm -XX:FlightRecorderOptions jdk.jfr.jcmd.TestJcmdDumpLimited
+ * @run main/othervm jdk.jfr.jcmd.TestJcmdDumpLimited
*/
public class TestJcmdDumpLimited {