Path: blob/master/test/hotspot/gtest/logging/test_logConfiguration.cpp
41145 views
/*1* Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223#include "precompiled.hpp"24#include "jvm.h"25#include "logTestFixture.hpp"26#include "logTestUtils.inline.hpp"27#include "logging/logConfiguration.hpp"28#include "logging/logFileStreamOutput.hpp"29#include "logging/logLevel.hpp"30#include "logging/logOutput.hpp"31#include "logging/logTag.hpp"32#include "logging/logTagSet.hpp"33#include "memory/resourceArea.hpp"34#include "unittest.hpp"35#include "utilities/ostream.hpp"3637class LogConfigurationTest : public LogTestFixture {38protected:39static char _all_decorators[256];4041public:42static void SetUpTestCase();43};4445char LogConfigurationTest::_all_decorators[256];4647// Prepare _all_decorators to contain the full list of decorators (comma separated)48void LogConfigurationTest::SetUpTestCase() {49char *pos = _all_decorators;50for (size_t i = 0; i < LogDecorators::Count; i++) {51pos += jio_snprintf(pos, sizeof(_all_decorators) - (pos - _all_decorators), "%s%s",52(i == 0 ? "" : ","),53LogDecorators::name(static_cast<LogDecorators::Decorator>(i)));54}55}5657// Check if the given text is included by LogConfiguration::describe()58static bool is_described(const char* text) {59ResourceMark rm;60stringStream ss;61LogConfiguration::describe(&ss);62return string_contains_substring(ss.as_string(), text);63}6465TEST_VM_F(LogConfigurationTest, describe) {66ResourceMark rm;67stringStream ss;68LogConfiguration::describe(&ss);69const char* description = ss.as_string();7071// Verify that stdout and stderr are listed by default72EXPECT_PRED2(string_contains_substring, description, StdoutLog.name());73EXPECT_PRED2(string_contains_substring, description, StderrLog.name());7475// Verify that each tag, level and decorator is listed76for (size_t i = 0; i < LogTag::Count; i++) {77EXPECT_PRED2(string_contains_substring, description, LogTag::name(static_cast<LogTagType>(i)));78}79for (size_t i = 0; i < LogLevel::Count; i++) {80EXPECT_PRED2(string_contains_substring, description, LogLevel::name(static_cast<LogLevelType>(i)));81}82for (size_t i = 0; i < LogDecorators::Count; i++) {83EXPECT_PRED2(string_contains_substring, description, LogDecorators::name(static_cast<LogDecorators::Decorator>(i)));84}8586// Verify that the default configuration is printed87char expected_buf[256];88int ret = jio_snprintf(expected_buf, sizeof(expected_buf), "=%s", LogLevel::name(LogLevel::Default));89ASSERT_NE(-1, ret);90EXPECT_PRED2(string_contains_substring, description, expected_buf);91EXPECT_PRED2(string_contains_substring, description, "#1: stderr all=off");9293// Verify default decorators are listed94LogDecorators default_decorators;95expected_buf[0] = '\0';96for (size_t i = 0; i < LogDecorators::Count; i++) {97LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);98if (default_decorators.is_decorator(d)) {99ASSERT_LT(strlen(expected_buf), sizeof(expected_buf));100ret = jio_snprintf(expected_buf + strlen(expected_buf),101sizeof(expected_buf) - strlen(expected_buf),102"%s%s",103strlen(expected_buf) > 0 ? "," : "",104LogDecorators::name(d));105ASSERT_NE(-1, ret);106}107}108EXPECT_PRED2(string_contains_substring, description, expected_buf);109110// Add a new output and verify that it gets described after it has been added111const char* what = "all=trace";112EXPECT_FALSE(is_described(TestLogFileName)) << "Test output already exists!";113set_log_config(TestLogFileName, what);114EXPECT_TRUE(is_described(TestLogFileName));115EXPECT_TRUE(is_described("all=trace"));116}117118// Test updating an existing log output119TEST_VM_F(LogConfigurationTest, update_output) {120// Update stdout twice, first using it's name, and the second time its index #121const char* test_outputs[] = { "stdout", "#0" };122for (size_t i = 0; i < ARRAY_SIZE(test_outputs); i++) {123set_log_config(test_outputs[i], "all=info");124125// Verify configuration using LogConfiguration::describe126EXPECT_TRUE(is_described("#0: stdout"));127EXPECT_TRUE(is_described("all=info"));128129// Verify by iterating over tagsets130LogOutput* o = &StdoutLog;131for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {132EXPECT_TRUE(ts->has_output(o));133EXPECT_TRUE(ts->is_level(LogLevel::Info));134EXPECT_FALSE(ts->is_level(LogLevel::Debug));135}136137// Now change the level and verify the change propagated138set_log_config(test_outputs[i], "all=debug");139for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {140EXPECT_TRUE(ts->has_output(o));141EXPECT_TRUE(ts->is_level(LogLevel::Debug));142EXPECT_FALSE(ts->is_level(LogLevel::Trace));143}144}145}146147// Test adding a new output to the configuration148TEST_VM_F(LogConfigurationTest, add_new_output) {149const char* what = "all=trace";150151ASSERT_FALSE(is_described(TestLogFileName));152set_log_config(TestLogFileName, what);153154// Verify new output using LogConfiguration::describe155EXPECT_TRUE(is_described(TestLogFileName));156EXPECT_TRUE(is_described("all=trace"));157158// Also verify by iterating over tagsets, checking levels on tagsets159for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {160EXPECT_TRUE(ts->is_level(LogLevel::Trace));161}162}163164TEST_VM_F(LogConfigurationTest, disable_logging) {165// Add TestLogFileName as an output166set_log_config(TestLogFileName, "logging=info");167168// Add a second file output169char other_file_name[2 * K];170jio_snprintf(other_file_name, sizeof(other_file_name), "%s-other", TestLogFileName);171set_log_config(other_file_name, "logging=info");172173LogConfiguration::disable_logging();174175// Verify that both file outputs were disabled176EXPECT_FALSE(is_described(TestLogFileName));177EXPECT_FALSE(is_described(other_file_name));178delete_file(other_file_name);179180// Verify that no tagset has logging enabled181for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {182EXPECT_FALSE(ts->has_output(&StdoutLog));183EXPECT_FALSE(ts->has_output(&StderrLog));184EXPECT_FALSE(ts->is_level(LogLevel::Error));185}186}187188// Test disabling a particular output189TEST_VM_F(LogConfigurationTest, disable_output) {190// Disable the default configuration for stdout191set_log_config("stdout", "all=off");192193// Verify configuration using LogConfiguration::describe194EXPECT_TRUE(is_described("#0: stdout all=off"));195196// Verify by iterating over tagsets197LogOutput* o = &StdoutLog;198for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {199EXPECT_FALSE(ts->has_output(o));200EXPECT_FALSE(ts->is_level(LogLevel::Error));201}202203// Add a new file output204const char* what = "all=debug";205set_log_config(TestLogFileName, what);206EXPECT_TRUE(is_described(TestLogFileName));207208// Now disable it, verifying it is removed completely209set_log_config(TestLogFileName, "all=off");210EXPECT_FALSE(is_described(TestLogFileName));211for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {212EXPECT_FALSE(ts->is_level(LogLevel::Error));213}214}215216// Test reconfiguration of the selected decorators for an output217TEST_VM_F(LogConfigurationTest, reconfigure_decorators) {218// Configure stderr with all decorators219set_log_config("stderr", "all=off", _all_decorators);220char buf[256];221int ret = jio_snprintf(buf, sizeof(buf), "#1: stderr all=off %s", _all_decorators);222ASSERT_NE(-1, ret);223EXPECT_TRUE(is_described(buf)) << "'" << buf << "' not described after reconfiguration";224225// Now reconfigure logging on stderr with no decorators226set_log_config("stderr", "all=off", "none");227EXPECT_TRUE(is_described("#1: stderr all=off none (reconfigured)\n")) << "Expecting no decorators";228}229230// Test that invalid options cause configuration errors231TEST_VM_F(LogConfigurationTest, invalid_configure_options) {232LogConfiguration::disable_logging();233const char* invalid_outputs[] = { "#2", "invalidtype=123", ":invalid/path}to*file?" };234for (size_t i = 0; i < ARRAY_SIZE(invalid_outputs); i++) {235EXPECT_FALSE(set_log_config(invalid_outputs[i], "", "", "", true))236<< "Accepted invalid output '" << invalid_outputs[i] << "'";237}238EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("all=invalid_level"));239EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("what=invalid"));240EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("all::invalid_decorator"));241EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("*"));242}243244// Test empty configuration options245TEST_VM_F(LogConfigurationTest, parse_empty_command_line_arguments) {246const char* empty_variations[] = { "", ":", "::", ":::", "::::" };247for (size_t i = 0; i < ARRAY_SIZE(empty_variations); i++) {248const char* cmdline = empty_variations[i];249bool ret = LogConfiguration::parse_command_line_arguments(cmdline);250EXPECT_TRUE(ret) << "Error parsing command line arguments '" << cmdline << "'";251for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {252EXPECT_EQ(LogLevel::Unspecified, ts->level_for(&StdoutLog));253}254}255}256257// Test basic command line parsing & configuration258TEST_VM_F(LogConfigurationTest, parse_command_line_arguments) {259// Prepare a command line for logging*=debug on stderr with all decorators260int ret;261char buf[256];262ret = jio_snprintf(buf, sizeof(buf), "logging*=debug:stderr:%s", _all_decorators);263ASSERT_NE(-1, ret);264265bool success = LogConfiguration::parse_command_line_arguments(buf);266EXPECT_TRUE(success) << "Error parsing valid command line arguments '" << buf << "'";267// Ensure the new configuration applied268EXPECT_TRUE(is_described("logging*=debug"));269EXPECT_TRUE(is_described(_all_decorators));270271// Test the configuration of file outputs as well272ret = jio_snprintf(buf, sizeof(buf), ":%s", TestLogFileName);273ASSERT_NE(-1, ret);274EXPECT_TRUE(LogConfiguration::parse_command_line_arguments(buf));275}276277// Test split up log configuration arguments278TEST_VM_F(LogConfigurationTest, parse_log_arguments) {279ResourceMark rm;280stringStream ss;281// Verify that it's possible to configure each individual tag282for (size_t t = 1 /* Skip _NO_TAG */; t < LogTag::Count; t++) {283const LogTagType tag = static_cast<LogTagType>(t);284EXPECT_TRUE(LogConfiguration::parse_log_arguments("stdout", LogTag::name(tag), "", "", &ss));285}286// Same for each level287for (size_t l = 0; l < LogLevel::Count; l++) {288const LogLevelType level = static_cast<LogLevelType>(l);289char expected_buf[256];290int ret = jio_snprintf(expected_buf, sizeof(expected_buf), "all=%s", LogLevel::name(level));291ASSERT_NE(-1, ret);292EXPECT_TRUE(LogConfiguration::parse_log_arguments("stderr", expected_buf, "", "", &ss));293}294// And for each decorator295for (size_t d = 0; d < LogDecorators::Count; d++) {296const LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(d);297EXPECT_TRUE(LogConfiguration::parse_log_arguments("#0", "", LogDecorators::name(decorator), "", &ss));298}299}300301TEST_VM_F(LogConfigurationTest, configure_stdout) {302// Start out with all logging disabled303LogConfiguration::disable_logging();304305// Enable 'logging=info', verifying it has been set306LogConfiguration::configure_stdout(LogLevel::Info, true, LOG_TAGS(logging));307EXPECT_TRUE(log_is_enabled(Info, logging));308EXPECT_FALSE(log_is_enabled(Debug, logging));309EXPECT_FALSE(log_is_enabled(Info, gc));310LogTagSet* logging_ts = &LogTagSetMapping<LOG_TAGS(logging)>::tagset();311EXPECT_EQ(LogLevel::Info, logging_ts->level_for(&StdoutLog));312313// Enable 'gc=debug' (no wildcard), verifying no other tags are enabled314LogConfiguration::configure_stdout(LogLevel::Debug, true, LOG_TAGS(gc));315EXPECT_TRUE(log_is_enabled(Debug, gc));316EXPECT_TRUE(log_is_enabled(Info, logging));317EXPECT_FALSE(log_is_enabled(Debug, gc, heap));318for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {319if (ts->contains(PREFIX_LOG_TAG(gc))) {320if (ts->ntags() == 1) {321EXPECT_EQ(LogLevel::Debug, ts->level_for(&StdoutLog));322} else {323EXPECT_EQ(LogLevel::Off, ts->level_for(&StdoutLog));324}325}326}327328// Enable 'gc*=trace' (with wildcard), verifying that all tag combinations with gc are enabled (gc+...)329LogConfiguration::configure_stdout(LogLevel::Trace, false, LOG_TAGS(gc));330EXPECT_TRUE(log_is_enabled(Trace, gc));331EXPECT_TRUE(log_is_enabled(Trace, gc, heap));332for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {333if (ts->contains(PREFIX_LOG_TAG(gc))) {334EXPECT_EQ(LogLevel::Trace, ts->level_for(&StdoutLog));335} else if (ts == logging_ts) {336// Previous setting for 'logging' should remain337EXPECT_EQ(LogLevel::Info, ts->level_for(&StdoutLog));338} else {339EXPECT_EQ(LogLevel::Off, ts->level_for(&StdoutLog));340}341}342343// Disable 'gc*' and 'logging', verifying all logging is properly disabled344LogConfiguration::configure_stdout(LogLevel::Off, true, LOG_TAGS(logging));345EXPECT_FALSE(log_is_enabled(Error, logging));346LogConfiguration::configure_stdout(LogLevel::Off, false, LOG_TAGS(gc));347EXPECT_FALSE(log_is_enabled(Error, gc));348EXPECT_FALSE(log_is_enabled(Error, gc, heap));349for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {350EXPECT_EQ(LogLevel::Off, ts->level_for(&StdoutLog));351}352}353354static int Test_logconfiguration_subscribe_triggered = 0;355static void Test_logconfiguration_subscribe_helper() {356Test_logconfiguration_subscribe_triggered++;357}358359TEST_VM_F(LogConfigurationTest, subscribe) {360ResourceMark rm;361Log(logging) log;362set_log_config("stdout", "logging*=trace");363364LogConfiguration::register_update_listener(&Test_logconfiguration_subscribe_helper);365366LogStream ls(log.error());367LogConfiguration::parse_log_arguments("stdout", "logging=trace", NULL, NULL, &ls);368ASSERT_EQ(1, Test_logconfiguration_subscribe_triggered);369370LogConfiguration::configure_stdout(LogLevel::Debug, true, LOG_TAGS(gc));371ASSERT_EQ(2, Test_logconfiguration_subscribe_triggered);372373LogConfiguration::disable_logging();374ASSERT_EQ(3, Test_logconfiguration_subscribe_triggered);375}376377TEST_VM_F(LogConfigurationTest, parse_invalid_tagset) {378static const char* invalid_tagset = "logging+start+exit+safepoint+gc"; // Must not exist for test to function.379380// Make sure warning is produced if one or more configured tagsets are invalid381ResourceMark rm;382stringStream ss;383bool success = LogConfiguration::parse_log_arguments("stdout", invalid_tagset, NULL, NULL, &ss);384const char* msg = ss.as_string();385EXPECT_TRUE(success) << "Should only cause a warning, not an error";386EXPECT_TRUE(string_contains_substring(msg, "No tag set matches selection:"));387EXPECT_TRUE(string_contains_substring(msg, invalid_tagset));388}389390TEST_VM_F(LogConfigurationTest, output_name_normalization) {391const char* patterns[] = { "%s", "file=%s", "\"%s\"", "file=\"%s\"" };392char buf[1 * K];393for (size_t i = 0; i < ARRAY_SIZE(patterns); i++) {394int ret = jio_snprintf(buf, sizeof(buf), patterns[i], TestLogFileName);395ASSERT_NE(-1, ret);396set_log_config(buf, "logging=trace");397EXPECT_TRUE(is_described("#2: "));398EXPECT_TRUE(is_described(TestLogFileName));399EXPECT_FALSE(is_described("#3: "))400<< "duplicate file output due to incorrect normalization for pattern: " << patterns[i];401}402403// Make sure prefixes are ignored when used within quotes404// (this should create a log with "file=" in its filename)405// Note that the filename cannot contain directories because406// it is being prefixed with "file=".407const char* leafFileName = "\"file=leaf_file_name\"";408set_log_config(leafFileName, "logging=trace");409EXPECT_TRUE(is_described("#3: ")) << "prefix within quotes not ignored as it should be";410set_log_config(leafFileName, "all=off");411412// Remove the extra log file created413delete_file("file=leaf_file_name");414}415416static size_t count_occurrences(const char* haystack, const char* needle) {417size_t count = 0;418for (const char* p = strstr(haystack, needle); p != NULL; p = strstr(p + 1, needle)) {419count++;420}421return count;422}423424TEST_OTHER_VM(LogConfiguration, output_reconfigured) {425ResourceMark rm;426stringStream ss;427428EXPECT_FALSE(is_described("(reconfigured)"));429430bool success = LogConfiguration::parse_log_arguments("#1", "all=warning", NULL, NULL, &ss);431ASSERT_TRUE(success);432EXPECT_EQ(0u, ss.size());433434LogConfiguration::describe(&ss);435EXPECT_EQ(1u, count_occurrences(ss.as_string(), "(reconfigured)"));436437ss.reset();438LogConfiguration::configure_stdout(LogLevel::Info, false, LOG_TAGS(logging));439LogConfiguration::describe(&ss);440EXPECT_EQ(2u, count_occurrences(ss.as_string(), "(reconfigured)"));441}442443TEST_VM_F(LogConfigurationTest, suggest_similar_selection) {444static const char* nonexisting_tagset = "logging+start+exit+safepoint+gc";445446ResourceMark rm;447stringStream ss;448LogConfiguration::parse_log_arguments("stdout", nonexisting_tagset, NULL, NULL, &ss);449450const char* suggestion = ss.as_string();451SCOPED_TRACE(suggestion);452EXPECT_TRUE(string_contains_substring(ss.as_string(), "Did you mean any of the following?"));453EXPECT_TRUE(string_contains_substring(suggestion, "logging") ||454string_contains_substring(suggestion, "start") ||455string_contains_substring(suggestion, "exit") ||456string_contains_substring(suggestion, "safepoint") ||457string_contains_substring(suggestion, "gc")) <<458"suggestion must contain AT LEAST one of the tags in user supplied selection";459}460461462