--- old/src/share/vm/gc/cms/compactibleFreeListSpace.cpp 2016-03-29 15:12:02.070903078 +0200 +++ new/src/share/vm/gc/cms/compactibleFreeListSpace.cpp 2016-03-29 15:12:01.906897571 +0200 @@ -2374,14 +2374,14 @@ void CompactibleFreeListSpace::printFLCensus(size_t sweep_count) const { assert_lock_strong(&_freelistLock); - Log(gc, freelist, census) log; - if (!log.is_debug()) { + LogTarget(Debug, gc, freelist, census) log; + if (!log.is_enabled()) { return; } AdaptiveFreeList total; - log.debug("end sweep# " SIZE_FORMAT, sweep_count); + log.print("end sweep# " SIZE_FORMAT, sweep_count); ResourceMark rm; - outputStream* out = log.debug_stream(); + outputStream* out = log.stream(); AdaptiveFreeList::print_labels_on(out, "size"); size_t total_free = 0; for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) { @@ -2403,8 +2403,8 @@ total.set_split_deaths(total.split_deaths() + fl->split_deaths()); } total.print_on(out, "TOTAL"); - log.debug("Total free in indexed lists " SIZE_FORMAT " words", total_free); - log.debug("growth: %8.5f deficit: %8.5f", + log.print("Total free in indexed lists " SIZE_FORMAT " words", total_free); + log.print("growth: %8.5f deficit: %8.5f", (double)(total.split_births()+total.coal_births()-total.split_deaths()-total.coal_deaths())/ (total.prev_sweep() != 0 ? (double)total.prev_sweep() : 1.0), (double)(total.desired() - total.count())/(total.desired() != 0 ? (double)total.desired() : 1.0)); --- old/src/share/vm/logging/log.cpp 2016-03-29 15:12:02.386913688 +0200 +++ new/src/share/vm/logging/log.cpp 2016-03-29 15:12:02.230908451 +0200 @@ -93,6 +93,7 @@ Log(logging) _log; public: TestLogSavedConfig(const char* apply_output = NULL, const char* apply_setting = NULL) : _new_output(0) { + ResourceMark rm; _saved_config = os::strdup_check_oom(LogOutput::Stdout->config_string()); bool success = LogConfiguration::parse_log_arguments("stdout", "all=off", NULL, NULL, _log.error_stream()); assert(success, "test unable to turn all off"); @@ -105,6 +106,7 @@ } ~TestLogSavedConfig() { + ResourceMark rm; if (_new_output) { bool success = LogConfiguration::parse_log_arguments(_new_output, "all=off", NULL, NULL, _log.error_stream()); assert(success, "test unable to turn all off"); @@ -154,7 +156,7 @@ ResourceMark rm; Log(logging) log; - TestLogSavedConfig log_cfg("stdout", "logging+test=trace"); + TestLogSavedConfig log_cfg("stdout", "logging*=trace"); LogConfiguration::register_update_listener(&Test_logconfiguration_subscribe_helper); @@ -267,4 +269,55 @@ } } +#define Test_logtarget_string_literal "First line" + + +static void Test_logtarget_on() { + TestLogFile log_file("log_target"); + TestLogSavedConfig tlsc(log_file.name(), "gc=debug"); + + LogTarget(Debug, gc) log; + + assert(log.is_enabled(), "assert"); + + // Log the line and expect it to be available in the output file. + log.print(Test_logtarget_string_literal); + + FILE* fp = fopen(log_file.name(), "r"); + assert(fp, "File read error"); + + char output[256 /* Large enough buffer */]; + if (fgets(output, sizeof(output), fp) != NULL) { + assert(strstr(output, Test_logtarget_string_literal) != NULL, "log line missing"); + } + fclose(fp); +} + +static void Test_logtarget_off() { + TestLogFile log_file("log_target"); + TestLogSavedConfig tlsc(log_file.name(), "gc=info"); + + LogTarget(Debug, gc) log; + + // The log level for gc is info, so this log instance shouldn't be enabled. + assert(!log.is_enabled(), "assert"); + + // Try to log, but expect this to be filtered out. + log.print(Test_logtarget_string_literal); + + FILE* fp = fopen(log_file.name(), "r"); + assert(fp, "File read error"); + + char output[256 /* Large enough buffer */]; + if (fgets(output, sizeof(output), fp) != NULL) { + assert(strstr(output, Test_logtarget_string_literal) == NULL, "log line not missing"); + } + fclose(fp); +} + +void Test_logtarget() { + Test_logtarget_on(); + Test_logtarget_off(); +} + #endif // PRODUCT --- old/src/share/vm/logging/log.hpp 2016-03-29 15:12:02.614921344 +0200 +++ new/src/share/vm/logging/log.hpp 2016-03-29 15:12:02.490917181 +0200 @@ -106,6 +106,24 @@ va_list args); }; +// +// Log class that embeds both log tags and a log level. +// +// The class provides a way to write the tags and log level once, +// so that redundant specification of tags or levels can be avoided. +// +// Example usage: +// LogTarget(Debug, gc) out; +// if (out.is_enabled()) { +// ... +// out.print("Worker: %u", i); +// out.print(" data: %d", x); +// ... +// print_stats(out.stream()); +// } +// +#define LogTarget(level, ...) LogTargetImpl + template class LogImpl VALUE_OBJ_CLASS_SPEC { @@ -184,4 +202,30 @@ #undef LOG_LEVEL }; +// Combines logging tags and a logging level. +template +class LogTargetImpl { +public: + // Empty constructor to avoid warnings on MSVC about unused variables + // when the log instance is only used for static functions. + LogTargetImpl() { + } + + static bool is_enabled() { + return LogImpl::is_level(level); + } + + static void print(const char* fmt, ...) ATTRIBUTE_PRINTF(1, 2) { + va_list args; + va_start(args, fmt); + LogImpl::vwrite(level, fmt, args); + va_end(args); + } + + static outputStream* stream() { + return new logStream(&LogImpl::template write); + } +}; + #endif // SHARE_VM_LOGGING_LOG_HPP --- old/src/share/vm/utilities/internalVMTests.cpp 2016-03-29 15:12:02.858929539 +0200 +++ new/src/share/vm/utilities/internalVMTests.cpp 2016-03-29 15:12:02.718924838 +0200 @@ -67,6 +67,7 @@ run_unit_test(Test_linked_list); run_unit_test(TestChunkedList_test); run_unit_test(JSON_test); + run_unit_test(Test_logtarget); run_unit_test(Test_configure_stdout); run_unit_test(Test_logconfiguration_subscribe); run_unit_test(Test_log_prefix);