diff --git a/src/hotspot/share/logging/logConfiguration.cpp b/src/hotspot/share/logging/logConfiguration.cpp index 87b195f4fbf..dfddfff2f05 100644 --- a/src/hotspot/share/logging/logConfiguration.cpp +++ b/src/hotspot/share/logging/logConfiguration.cpp @@ -491,7 +491,7 @@ bool LogConfiguration::parse_log_arguments(const char* outputstr, return false; } - LogDecorators decorators; + LogDecorators decorators = selections.get_default_decorators(); if (!decorators.parse(decoratorstr, errstream)) { return false; } diff --git a/src/hotspot/share/logging/logDecorators.cpp b/src/hotspot/share/logging/logDecorators.cpp index 91677b35d9f..6c06bd45716 100644 --- a/src/hotspot/share/logging/logDecorators.cpp +++ b/src/hotspot/share/logging/logDecorators.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ #include "logging/logDecorators.hpp" #include "runtime/os.hpp" +const LogLevelType AnyLevel = LogLevelType::NotMentioned; + template struct AllBitmask { // Use recursive template deduction to calculate the bitmask of all decorations. @@ -45,6 +47,19 @@ const char* LogDecorators::_name[][2] = { #undef DECORATOR }; +#define UNDECORATED_DEFAULTS \ + UNDECORATED_DEFAULT(AnyLevel, LOG_TAGS(jit, inlining)) + +const LogDecorators::DefaultUndecoratedSelection LogDecorators::default_decorators[] = { +#define UNDECORATED_DEFAULT(level, ...) LogDecorators::DefaultUndecoratedSelection::make(), + UNDECORATED_DEFAULTS +#undef UNDECORATED_DEFAULT +}; + +#undef UNDERCORATED_DEFAULTS + +const size_t LogDecorators::number_of_default_decorators = ARRAY_SIZE(default_decorators); + LogDecorators::Decorator LogDecorators::from_string(const char* str) { for (size_t i = 0; i < Count; i++) { Decorator d = static_cast(i); @@ -57,7 +72,7 @@ LogDecorators::Decorator LogDecorators::from_string(const char* str) { bool LogDecorators::parse(const char* decorator_args, outputStream* errstream) { if (decorator_args == nullptr || strlen(decorator_args) == 0) { - _decorators = DefaultDecoratorsMask; + // No decorators supplied, keep default decorators return true; } @@ -93,3 +108,16 @@ bool LogDecorators::parse(const char* decorator_args, outputStream* errstream) { } return result; } + +bool LogDecorators::has_disabled_default_decorators(const LogSelection& selection, const DefaultUndecoratedSelection* defaults, size_t defaults_count) { + for (size_t i = 0; i < defaults_count; ++i) { + DefaultUndecoratedSelection current_default = defaults[i]; + const bool ignore_level = current_default.selection().level() == AnyLevel; + const bool level_matches = ignore_level || selection.level() == current_default.selection().level(); + if (!level_matches) continue; + if (selection.superset_of(current_default.selection())) { + return true; + } + } + return false; +} diff --git a/src/hotspot/share/logging/logDecorators.hpp b/src/hotspot/share/logging/logDecorators.hpp index b92de0fdb98..9e38f429974 100644 --- a/src/hotspot/share/logging/logDecorators.hpp +++ b/src/hotspot/share/logging/logDecorators.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ #define SHARE_LOGGING_LOGDECORATORS_HPP #include "utilities/globalDefinitions.hpp" +#include "logging/logSelection.hpp" class outputStream; @@ -59,6 +60,7 @@ class outputStream; // declared above. For example, logging with 'uptime, level, tags' decorators results in: // [0,943s][info ][logging] message. class LogDecorators { + friend class TestLogDecorators; public: enum Decorator { #define DECORATOR(name, abbr) name##_decorator, @@ -68,24 +70,48 @@ class LogDecorators { Invalid }; + class DefaultUndecoratedSelection { + friend class TestLogDecorators; + LogSelection _selection; + + DefaultUndecoratedSelection() : _selection(LogSelection::Invalid) {} + + DefaultUndecoratedSelection(LogLevelType level, LogTagType t0, LogTagType t1, LogTagType t2, + LogTagType t3, LogTagType t4) : _selection(LogSelection::Invalid) { + LogTagType tag_arr[LogTag::MaxTags] = { t0, t1, t2, t3, t4 }; + _selection = LogSelection(tag_arr, false, level); + } + + public: + template + static DefaultUndecoratedSelection make() { + STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); + return DefaultUndecoratedSelection(Level, T0, T1, T2, T3, T4); + } + + const LogSelection& selection() const { return _selection; } + }; + private: uint _decorators; static const char* _name[][2]; - static const uint DefaultDecoratorsMask = (1 << uptime_decorator) | (1 << level_decorator) | (1 << tags_decorator); + static const uint defaultsMask = (1 << uptime_decorator) | (1 << level_decorator) | (1 << tags_decorator); + static const LogDecorators::DefaultUndecoratedSelection default_decorators[]; + static const size_t number_of_default_decorators; static uint mask(LogDecorators::Decorator decorator) { return 1 << decorator; } - constexpr LogDecorators(uint mask) : _decorators(mask) { - } public: static const LogDecorators None; static const LogDecorators All; - LogDecorators() : _decorators(DefaultDecoratorsMask) { - } + constexpr LogDecorators(uint mask) : _decorators(mask) {} + + LogDecorators() : _decorators(defaultsMask) {} void clear() { _decorators = 0; @@ -99,6 +125,20 @@ class LogDecorators { return _name[decorator][1]; } + template + static uint mask_from_decorators(LogDecorators::Decorator first, Decorators... rest) { + uint bitmask = 0; + LogDecorators::Decorator decorators[1 + sizeof...(rest)] = { first, rest... }; + for (const LogDecorators::Decorator decorator : decorators) { + bitmask |= mask(decorator); + } + return bitmask; + } + + // Check if we have some default decorators for a given LogSelection. If that is the case, + // the output parameter mask will contain the defaults-specified decorators mask + static bool has_disabled_default_decorators(const LogSelection& selection, const DefaultUndecoratedSelection* defaults = default_decorators, size_t defaults_count = number_of_default_decorators); + static LogDecorators::Decorator from_string(const char* str); void combine_with(const LogDecorators &source) { diff --git a/src/hotspot/share/logging/logSelection.cpp b/src/hotspot/share/logging/logSelection.cpp index 1e7ba3a8878..99ecc9f87f2 100644 --- a/src/hotspot/share/logging/logSelection.cpp +++ b/src/hotspot/share/logging/logSelection.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,6 +69,23 @@ bool LogSelection::operator!=(const LogSelection& ref) const { return !operator==(ref); } +bool LogSelection::superset_of(const LogSelection& other) const { + bool match; + for (size_t i = 0; i < other.ntags(); ++i) { + match = false; + for (size_t j = 0; j < _ntags; ++j) { + if (other._tags[i] == _tags[j]) { + match = true; + break; + } + } + + if (!match) return false; + } + + return true; +} + static LogSelection parse_internal(char *str, outputStream* errstream) { // Parse the level, if specified LogLevelType level = LogLevel::Unspecified; diff --git a/src/hotspot/share/logging/logSelection.hpp b/src/hotspot/share/logging/logSelection.hpp index 7f4c1fb8e95..4555590295a 100644 --- a/src/hotspot/share/logging/logSelection.hpp +++ b/src/hotspot/share/logging/logSelection.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,6 +54,8 @@ class LogSelection : public StackObj { bool operator==(const LogSelection& ref) const; bool operator!=(const LogSelection& ref) const; + bool superset_of(const LogSelection& ref) const; + size_t ntags() const; LogLevelType level() const; size_t tag_sets_selected() const; diff --git a/src/hotspot/share/logging/logSelectionList.cpp b/src/hotspot/share/logging/logSelectionList.cpp index 1eb4cca0c3e..ac63f20512c 100644 --- a/src/hotspot/share/logging/logSelectionList.cpp +++ b/src/hotspot/share/logging/logSelectionList.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -53,6 +53,14 @@ bool LogSelectionList::verify_selections(outputStream* out) const { return valid; } +LogDecorators LogSelectionList::get_default_decorators() const { + for (size_t i = 0; i < _nselections; ++i) { + if (!LogDecorators::has_disabled_default_decorators(_selections[i])) { + return LogDecorators(); + } + } + return LogDecorators::None; +} bool LogSelectionList::parse(const char* str, outputStream* errstream) { bool success = true; @@ -91,7 +99,7 @@ bool LogSelectionList::parse(const char* str, outputStream* errstream) { LogLevelType LogSelectionList::level_for(const LogTagSet& ts) const { // Return NotMentioned if the given tagset isn't covered by this expression. LogLevelType level = LogLevel::NotMentioned; - for (size_t i= 0; i < _nselections; i++) { + for (size_t i = 0; i < _nselections; i++) { if (_selections[i].selects(ts)) { level = _selections[i].level(); } diff --git a/src/hotspot/share/logging/logSelectionList.hpp b/src/hotspot/share/logging/logSelectionList.hpp index 6bd2c6608bc..0852348cc14 100644 --- a/src/hotspot/share/logging/logSelectionList.hpp +++ b/src/hotspot/share/logging/logSelectionList.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -60,6 +60,8 @@ class LogSelectionList : public StackObj { // Returns false if some invalid selection was found. If given an outputstream, // this function will list all the invalid selections on the stream. bool verify_selections(outputStream* out = nullptr) const; + + LogDecorators get_default_decorators() const; }; #endif // SHARE_LOGGING_LOGSELECTIONLIST_HPP diff --git a/test/hotspot/gtest/logging/test_logDefaultDecorators.cpp b/test/hotspot/gtest/logging/test_logDefaultDecorators.cpp new file mode 100644 index 00000000000..8ffe97da0db --- /dev/null +++ b/test/hotspot/gtest/logging/test_logDefaultDecorators.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include "precompiled.hpp" +#include "jvm.h" +#include "logging/logDecorators.hpp" +#include "logging/logTag.hpp" +#include "unittest.hpp" + + +class TestLogDecorators : public testing::Test { + using LD = LogDecorators; + + static const size_t defaults_cnt = 3; + LD::DefaultUndecoratedSelection defaults[defaults_cnt] = { + LD::DefaultUndecoratedSelection::make(), + LD::DefaultUndecoratedSelection::make(), + LD::DefaultUndecoratedSelection::make(), + }; + +public: + void test_default_decorators() { + LogTagType tags[LogTag::MaxTags] = { LogTag::__NO_TAG, LogTag::__NO_TAG, LogTag::__NO_TAG, LogTag::__NO_TAG, LogTag::__NO_TAG }; + bool result; + + // If a -Xlog selection matches one of the undecorated defaults, the default decorators will be disabled + tags[0] = LogTagType::_jit; + result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Trace), defaults, defaults_cnt); + EXPECT_TRUE(result); + + + // If a -Xlog selection contains one of the undecorated defaults, the default decorators will be disabled + tags[0] = LogTagType::_jit; + tags[1] = LogTagType::_inlining; + result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Trace), defaults, defaults_cnt); + EXPECT_TRUE(result); + + + // Wildcards are ignored + tags[0] = LogTag::_compilation; + result = LD::has_disabled_default_decorators(LogSelection(tags, true, LogLevelType::Debug), defaults, defaults_cnt); + EXPECT_FALSE(result); + + + // If there is no level match, default decorators are kept + tags[0] = LogTagType::_gc; + result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Info), defaults, defaults_cnt); + EXPECT_FALSE(result); + + + // If NotMentioned is specified, it will match every level and so default decorators will never be added if there is a positive tagset match + tags[0] = LogTagType::_ref; + result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Error), defaults, defaults_cnt); + EXPECT_TRUE(result); + result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Warning), defaults, defaults_cnt); + EXPECT_TRUE(result); + result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Info), defaults, defaults_cnt); + EXPECT_TRUE(result); + result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Debug), defaults, defaults_cnt); + EXPECT_TRUE(result); + result = LD::has_disabled_default_decorators(LogSelection(tags, false, LogLevelType::Trace), defaults, defaults_cnt); + EXPECT_TRUE(result); + } + + void test_mask_from_decorators() { + // Single tags should yield 2^{decorator_value_in_enum} + EXPECT_EQ(LD::mask_from_decorators(LD::time_decorator), (uint)(1 << LD::time_decorator)); + EXPECT_EQ(LD::mask_from_decorators(LD::pid_decorator), (uint)(1 << LD::pid_decorator)); + EXPECT_EQ(LD::mask_from_decorators(LD::tid_decorator), (uint)(1 << LD::tid_decorator)); + EXPECT_EQ(LD::mask_from_decorators(LD::tags_decorator), (uint)(1 << LD::tags_decorator)); + + // Combinations of decorators should fill the mask accordingly to their bitmask positions + uint mask = (1 << LD::time_decorator) | (1 << LD::uptimemillis_decorator) | (1 << LD::tid_decorator); + EXPECT_EQ(LD::mask_from_decorators(LD::time_decorator, LD::uptimemillis_decorator, LD::tid_decorator), mask); + } +}; + +TEST_VM_F(TestLogDecorators, MaskFromDecorators) { + test_mask_from_decorators(); +} + +TEST_VM_F(TestLogDecorators, HasDefaultDecorators) { + test_default_decorators(); +} diff --git a/test/hotspot/jtreg/runtime/logging/DefaultLogDecoratorsTest.java b/test/hotspot/jtreg/runtime/logging/DefaultLogDecoratorsTest.java new file mode 100644 index 00000000000..c2374145622 --- /dev/null +++ b/test/hotspot/jtreg/runtime/logging/DefaultLogDecoratorsTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @requires vm.flagless + * @summary Running -Xlog with tags which have disabled default decorators should not yield decorated logs + * @library /test/lib + * @modules java.base/jdk.internal.misc + * java.management + * @run driver DefaultLogDecoratorsTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.Platform; +import jdk.test.lib.process.ProcessTools; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.regex.Pattern; +import java.util.List; +import java.util.Arrays; +import java.util.ArrayList; + +public class DefaultLogDecoratorsTest { + private static Pattern DECORATED_LINE = Pattern.compile("(\\[.+\\])+ .*"); + + private static void doTest(boolean shouldHave, String... xlog) throws Exception { + List argsList = new ArrayList(Arrays.asList(xlog)); + argsList.add(InnerClass.class.getName()); + String[] args = argsList.toArray(new String[0]); + ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldHaveExitValue(0); + List allLines = Files.readAllLines(Path.of("decorators.log")); + for (String line : allLines) { + if (DECORATED_LINE.matcher(line).find() == !shouldHave) { + throw new RuntimeException("Logging should " + (shouldHave ? "" : "not ") + "contain decorators!"); + } + } + } + + public static void main(String[] args) throws Exception { + // JIT inlining logging, as per defaults, shall have all decorators disabled + doTest(false, "-Xlog:jit+inlining*=trace:decorators.log"); + + // If decorators are specified, the defaults are not taken into account + doTest(true, "-Xlog:jit+inlining*=trace:decorators.log:time"); + + // Even if decorators are only supplied for another tag(s), the defaults are not taken into account + doTest(true, "-Xlog:jit+inlining*=trace:decorators.log", "-Xlog:gc*=info:decorators.log:time"); + + // Defaults are not taken into account also when another tag implicitly imposes the "standard" defaults + doTest(true, "-Xlog:jit+inlining*=trace:decorators.log", "-Xlog:gc*=info:decorators.log"); + + // Other logging shall not be affected by a tag with defaults + doTest(true, "-Xlog:gc*=trace:decorators.log"); + } + + public static class InnerClass { + public static void main(String[] args) throws Exception { + System.out.println("DefaultLogDecorators test"); + } + } +}