From 95c47b2a382f6e4d1ed47f5d2a3a2358f64bcc7a Mon Sep 17 00:00:00 2001 From: Johannes Bechberger Date: Wed, 27 Aug 2025 14:01:56 +0200 Subject: [PATCH 1/2] Fix startup message printing --- src/hotspot/share/jfr/dcmd/jfrDcmds.cpp | 8 +++--- src/hotspot/share/logging/log.hpp | 5 ++++ .../share/logging/logConfiguration.cpp | 26 ++++++++++++++++--- .../share/logging/logConfiguration.hpp | 5 +++- src/hotspot/share/logging/logOutputList.hpp | 15 ++++++++++- src/hotspot/share/logging/logTagSet.hpp | 12 +++++++++ .../TestMultipleStartupRecordings.java | 1 + .../jfr/startupargs/TestStartupMessage.java | 6 +++-- 8 files changed, 66 insertions(+), 12 deletions(-) diff --git a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp index d18136e15701a..42a0e0694e20c 100644 --- a/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp +++ b/src/hotspot/share/jfr/dcmd/jfrDcmds.cpp @@ -162,13 +162,13 @@ static void handle_dcmd_result(outputStream* output, assert(!HAS_PENDING_EXCEPTION, "invariant"); if (startup) { - if (log_is_enabled(Warning, jfr, startup)) { - // if warning is set, assume user hasn't configured log level + if (log_is_default(jfr, startup)) { + // The user hasn't configured a log level // Log to Info and reset to Warning. This way user can disable - // default output by setting -Xlog:jfr+startup=error/off + // default output by setting -Xlog:jfr+startup=error/warning/off LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(jfr, startup)); log(result, THREAD); - LogConfiguration::configure_stdout(LogLevel::Warning, true, LOG_TAGS(jfr, startup)); + LogConfiguration::reset_stdout(true, LOG_TAGS(jfr, startup)); } else { log(result, THREAD); } diff --git a/src/hotspot/share/logging/log.hpp b/src/hotspot/share/logging/log.hpp index 7820e2c21d1a4..303a81a89da48 100644 --- a/src/hotspot/share/logging/log.hpp +++ b/src/hotspot/share/logging/log.hpp @@ -66,6 +66,7 @@ class LogMessageBuffer; // Convenience macro to test if the logging is enabled on the specified level for given tags. #define log_is_enabled(level, ...) (LogImpl::is_level(LogLevel::level)) +#define log_is_default(...) (!LogImpl::is_output_level_configured_by_user()) // // Log class for more advanced logging scenarios. @@ -122,6 +123,10 @@ class LogImpl { return LogTagSetMapping::tagset().is_level(level); } + static bool is_output_level_configured_by_user() { + return LogTagSetMapping::tagset().is_output_level_configured_by_user(); + } + ATTRIBUTE_PRINTF(2, 3) static void write(LogLevelType level, const char* fmt, ...) { va_list args; diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index b883dbccacff3..c805ae1b0893e 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -29,6 +29,7 @@ #include "logging/logDecorators.hpp" #include "logging/logDiagnosticCommand.hpp" #include "logging/logFileOutput.hpp" +#include "logging/logLevel.hpp" #include "logging/logOutput.hpp" #include "logging/logSelectionList.hpp" #include "logging/logStream.hpp" @@ -237,7 +238,7 @@ void LogConfiguration::delete_output(size_t idx) { // is a subset of relevant tagsets decorators. After updating output's decorators, it is still safe to shrink all // decorators of tagsets. // -void LogConfiguration::configure_output(size_t idx, const LogSelectionList& selections, const LogDecorators& decorators) { +void LogConfiguration::configure_output(size_t idx, const LogSelectionList& selections, const LogDecorators& decorators, bool is_user_provided) { assert(ConfigurationLock::current_thread_has_lock(), "Must hold configuration lock to call this function."); assert(idx < _n_outputs, "Invalid index, idx = %zu and _n_outputs = %zu", idx, _n_outputs); LogOutput* output = _outputs[idx]; @@ -265,6 +266,11 @@ void LogConfiguration::configure_output(size_t idx, const LogSelectionList& sele // Set the new level, if it changed if (level != LogLevel::NotMentioned) { ts->set_output_level(output, level); + if (is_user_provided) { + ts->set_output_level_configured_by_user(); + } else { + ts->set_output_level_not_configured_by_user(); + } } else { // Look up the previously set level for this output on this tagset level = ts->level_for(output); @@ -365,7 +371,7 @@ void LogConfiguration::disable_tags(int exact_match, ...) { // Apply configuration to all outputs, with the same decorators as before. ConfigurationLock cl; for (size_t i = 0; i < _n_outputs; i++) { - configure_output(i, list, _outputs[i]->decorators()); + configure_output(i, list, _outputs[i]->decorators(), true); } notify_update_listeners(); } @@ -378,7 +384,19 @@ void LogConfiguration::configure_stdout(LogLevelType level, int exact_match, ... // Apply configuration to stdout (output #0), with the same decorators as before. ConfigurationLock cl; - configure_output(0, list, _outputs[0]->decorators()); + configure_output(0, list, _outputs[0]->decorators(), true); + notify_update_listeners(); +} + +void LogConfiguration::reset_stdout(int exact_match, ...) { + va_list ap; + va_start(ap, exact_match); + LogSelectionList list = create_selection_list(LogLevel::Off, exact_match, ap); + va_end(ap); + + // Apply configuration to stdout (output #0), with the same decorators as before. + ConfigurationLock cl; + configure_output(0, list, _outputs[0]->decorators(), false); notify_update_listeners(); } @@ -554,7 +572,7 @@ bool LogConfiguration::parse_log_arguments(const char* outputstr, if (!added && output_options != nullptr && strlen(output_options) > 0) { errstream->print_cr("Output options for existing outputs are ignored."); } - configure_output(idx, selections, decorators); + configure_output(idx, selections, decorators, true); notify_update_listeners(); selections.verify_selections(errstream); return true; diff --git a/src/hotspot/share/logging/logConfiguration.hpp b/src/hotspot/share/logging/logConfiguration.hpp index 6e84199fddec8..1604d380acf73 100644 --- a/src/hotspot/share/logging/logConfiguration.hpp +++ b/src/hotspot/share/logging/logConfiguration.hpp @@ -88,7 +88,7 @@ class LogConfiguration : public AllStatic { static size_t find_output(const char* name); // Configure output (add or update existing configuration) to log on tag-level combination using specified decorators. - static void configure_output(size_t idx, const LogSelectionList& tag_level_expression, const LogDecorators& decorators); + static void configure_output(size_t idx, const LogSelectionList& tag_level_expression, const LogDecorators& decorators, bool is_user_provided); // This should be called after any configuration change while still holding ConfigurationLock static void notify_update_listeners(); @@ -126,6 +126,9 @@ class LogConfiguration : public AllStatic { // LogConfiguration::configure_stdout(LogLevel::, , LOG_TAGS()); static void configure_stdout(LogLevelType level, int exact_match, ...); + // Configures logging on stdout for the given tags to be the default level, not specified by users + static void reset_stdout(int exact_match, ...); + // Parse command line configuration. Parameter 'opts' is the string immediately following the -Xlog: argument ("gc" for -Xlog:gc). static bool parse_command_line_arguments(const char* opts = "all"); diff --git a/src/hotspot/share/logging/logOutputList.hpp b/src/hotspot/share/logging/logOutputList.hpp index e081c02b2b68f..192dd4be94363 100644 --- a/src/hotspot/share/logging/logOutputList.hpp +++ b/src/hotspot/share/logging/logOutputList.hpp @@ -54,6 +54,7 @@ class LogOutputList { }; LogOutputNode* volatile _level_start[LogLevel::Count]; + volatile bool _output_level_configured_by_user; volatile jint _active_readers; LogOutputNode* find(const LogOutput* output) const; @@ -66,7 +67,7 @@ class LogOutputList { jint decrease_readers(); public: - LogOutputList() : _active_readers(0) { + LogOutputList() : _output_level_configured_by_user(false), _active_readers(0) { for (size_t i = 0; i < LogLevel::Count; i++) { _level_start[i] = nullptr; } @@ -88,6 +89,18 @@ class LogOutputList { // Set (add/update/remove) the output to the specified level. void set_output_level(LogOutput* output, LogLevelType level); + void set_output_level_configured_by_user() { + Atomic::release_store(&_output_level_configured_by_user, true); + } + + void set_output_level_not_configured_by_user() { + Atomic::release_store(&_output_level_configured_by_user, false); + } + + bool is_output_level_configured_by_user() const { + return Atomic::load_acquire(&_output_level_configured_by_user); + } + // Removes all outputs. Equivalent of set_output_level(out, Off) // for all outputs. void clear(); diff --git a/src/hotspot/share/logging/logTagSet.hpp b/src/hotspot/share/logging/logTagSet.hpp index f8b37e1a777c3..e759f0205015c 100644 --- a/src/hotspot/share/logging/logTagSet.hpp +++ b/src/hotspot/share/logging/logTagSet.hpp @@ -116,6 +116,18 @@ class LogTagSet { _output_list.set_output_level(output, level); } + void set_output_level_configured_by_user() { + _output_list.set_output_level_configured_by_user(); + } + + void set_output_level_not_configured_by_user() { + _output_list.set_output_level_not_configured_by_user(); + } + + bool is_output_level_configured_by_user() const { + return _output_list.is_output_level_configured_by_user(); + } + // Refresh the decorators for this tagset to contain the decorators for all // of its current outputs combined with the given decorators. void update_decorators(const LogDecorators& decorator = LogDecorators::None); diff --git a/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java b/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java index bfa02fd8a7490..62ca6c7fe1f3e 100644 --- a/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java +++ b/test/jdk/jdk/jfr/startupargs/TestMultipleStartupRecordings.java @@ -63,6 +63,7 @@ private static void launchBinary(String options1, String options2) throws Except String recording1 = START_FLIGHT_RECORDING + (options1 != null ? options1 : ""); String recording2 = START_FLIGHT_RECORDING + (options2 != null ? options2 : ""); ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(recording1, recording2, MainClass.class.getName()); + System.err.println("Out: " + pb.command()); test(pb, "Started recording 1", "Started recording 2"); } diff --git a/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java b/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java index 4a934ebb36da3..3fc6cc2196dde 100644 --- a/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java +++ b/test/jdk/jdk/jfr/startupargs/TestStartupMessage.java @@ -55,8 +55,10 @@ public static void main(String[] args) throws Exception { .shouldNotContain("Started recording") .shouldNotContain("Use jcmd"); - // Known limitation. - // Can't turn off log with -Xlog:jfr+startup=warning + startJfrJvm("-Xlog:jfr+startup=warning") + .shouldNotContain("[jfr,startup") + .shouldNotContain("Started recording") + .shouldNotContain("Use jcmd"); startJfrJvm() .shouldContain("[info][jfr,startup") From 4b827459dcc3dff0606c03d8687c018fc979b6e5 Mon Sep 17 00:00:00 2001 From: Johannes Bechberger Date: Mon, 15 Sep 2025 11:29:18 +0200 Subject: [PATCH 2/2] Rename Atomic:: to AtomicAccess:: --- src/hotspot/share/logging/logOutputList.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/logging/logOutputList.hpp b/src/hotspot/share/logging/logOutputList.hpp index f70c92ea636d3..9eaf1a0fc18b5 100644 --- a/src/hotspot/share/logging/logOutputList.hpp +++ b/src/hotspot/share/logging/logOutputList.hpp @@ -90,15 +90,15 @@ class LogOutputList { void set_output_level(LogOutput* output, LogLevelType level); void set_output_level_configured_by_user() { - Atomic::release_store(&_output_level_configured_by_user, true); + AtomicAccess::release_store(&_output_level_configured_by_user, true); } void set_output_level_not_configured_by_user() { - Atomic::release_store(&_output_level_configured_by_user, false); + AtomicAccess::release_store(&_output_level_configured_by_user, false); } bool is_output_level_configured_by_user() const { - return Atomic::load_acquire(&_output_level_configured_by_user); + return AtomicAccess::load_acquire(&_output_level_configured_by_user); } // Removes all outputs. Equivalent of set_output_level(out, Off)