8152635: Create a UL class to represent a Log + Level combination
authorstefank
Mon, 04 Apr 2016 12:45:04 +0200
changeset 37245 70413a0266d4
parent 37243 34e1821bde93
child 37246 55d19e96f149
8152635: Create a UL class to represent a Log + Level combination Reviewed-by: brutisso, mlarsson, rehn
hotspot/src/share/vm/gc/cms/compactibleFreeListSpace.cpp
hotspot/src/share/vm/logging/log.cpp
hotspot/src/share/vm/logging/log.hpp
hotspot/src/share/vm/utilities/internalVMTests.cpp
--- a/hotspot/src/share/vm/gc/cms/compactibleFreeListSpace.cpp	Mon Apr 04 11:32:04 2016 +0200
+++ b/hotspot/src/share/vm/gc/cms/compactibleFreeListSpace.cpp	Mon Apr 04 12:45:04 2016 +0200
@@ -2373,14 +2373,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<FreeChunk> 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<FreeChunk>::print_labels_on(out, "size");
   size_t total_free = 0;
   for (size_t i = IndexSetStart; i < IndexSetSize; i += IndexSetStride) {
@@ -2402,8 +2402,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));
--- a/hotspot/src/share/vm/logging/log.cpp	Mon Apr 04 11:32:04 2016 +0200
+++ b/hotspot/src/share/vm/logging/log.cpp	Mon Apr 04 12:45:04 2016 +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,68 @@
   }
 }
 
+#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 != NULL, "File read error");
+
+  char output[256 /* Large enough buffer */];
+  char* res = fgets(output, sizeof(output), fp);
+  assert(res != NULL, "assert");
+
+  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;
+
+  if (log.is_enabled()) {
+    // The log config could have been redirected gc=debug to a file. If gc=debug
+    // is enabled, we can only test that the LogTarget returns the same value
+    // as the log_is_enabled function. The rest of the test will be ignored.
+    assert(log.is_enabled() == log_is_enabled(Debug, gc), "assert");
+    log_warning(logging)("This test doesn't support runs with -Xlog");
+    return;
+  }
+
+  // Try to log, but expect this to be filtered out.
+  log.print(Test_logtarget_string_literal);
+
+  // Log a dummy line so that fgets doesn't return NULL because the file is empty.
+  log_info(gc)("Dummy line");
+
+  FILE* fp = fopen(log_file.name(), "r");
+  assert(fp != NULL, "File read error");
+
+  char output[256 /* Large enough buffer */];
+  char* res = fgets(output, sizeof(output), fp);
+  assert(res != NULL, "assert");
+
+  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
--- a/hotspot/src/share/vm/logging/log.hpp	Mon Apr 04 11:32:04 2016 +0200
+++ b/hotspot/src/share/vm/logging/log.hpp	Mon Apr 04 12:45:04 2016 +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<LogLevel::level, LOG_TAGS(__VA_ARGS__)>
+
 template <LogTagType T0, LogTagType T1 = LogTag::__NO_TAG, LogTagType T2 = LogTag::__NO_TAG, LogTagType T3 = LogTag::__NO_TAG,
           LogTagType T4 = LogTag::__NO_TAG, LogTagType GuardTag = LogTag::__NO_TAG>
 class LogImpl VALUE_OBJ_CLASS_SPEC {
@@ -184,4 +202,30 @@
 #undef LOG_LEVEL
 };
 
+// Combines logging tags and a logging level.
+template <LogLevelType level, LogTagType T0, LogTagType T1 = LogTag::__NO_TAG, LogTagType T2 = LogTag::__NO_TAG,
+          LogTagType T3 = LogTag::__NO_TAG, LogTagType T4 = LogTag::__NO_TAG, LogTagType GuardTag = LogTag::__NO_TAG>
+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<T0, T1, T2, T3, T4, GuardTag>::is_level(level);
+  }
+
+  static void print(const char* fmt, ...) ATTRIBUTE_PRINTF(1, 2) {
+    va_list args;
+    va_start(args, fmt);
+    LogImpl<T0, T1, T2, T3, T4, GuardTag>::vwrite(level, fmt, args);
+    va_end(args);
+  }
+
+  static outputStream* stream() {
+    return new logStream(&LogImpl<T0, T1, T2, T3, T4, GuardTag>::template write<level>);
+  }
+};
+
 #endif // SHARE_VM_LOGGING_LOG_HPP
--- a/hotspot/src/share/vm/utilities/internalVMTests.cpp	Mon Apr 04 11:32:04 2016 +0200
+++ b/hotspot/src/share/vm/utilities/internalVMTests.cpp	Mon Apr 04 12:45:04 2016 +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);