8061219: Implement unit-tests for UL
authormlarsson
Thu, 14 Jul 2016 09:52:03 +0200
changeset 40656 d4964c292b78
parent 40655 9f644073d3a0
child 40657 a61a7a7966f2
child 40658 50dd5daad1e6
8061219: Implement unit-tests for UL Reviewed-by: coleenp, mockner, rprotacio
hotspot/src/share/vm/logging/logDecorations.hpp
hotspot/src/share/vm/logging/logOutputList.hpp
hotspot/test/native/logging/logTestFixture.cpp
hotspot/test/native/logging/logTestFixture.hpp
hotspot/test/native/logging/logTestUtils.inline.hpp
hotspot/test/native/logging/test_logConfiguration.cpp
hotspot/test/native/logging/test_logDecorations.cpp
hotspot/test/native/logging/test_logDecorators.cpp
hotspot/test/native/logging/test_logFileOutput.cpp
hotspot/test/native/logging/test_logLevel.cpp
hotspot/test/native/logging/test_logOutputList.cpp
hotspot/test/native/logging/test_logTag.cpp
hotspot/test/native/logging/test_logTagLevelExpression.cpp
hotspot/test/native/logging/test_logTagSet.cpp
--- a/hotspot/src/share/vm/logging/logDecorations.hpp	Sun Aug 21 20:56:37 2016 -0400
+++ b/hotspot/src/share/vm/logging/logDecorations.hpp	Thu Jul 14 09:52:03 2016 +0200
@@ -53,10 +53,6 @@
 
   LogDecorations(LogLevelType level, const LogTagSet& tagset, const LogDecorators& decorators);
 
-  LogLevelType level() const {
-    return _level;
-  }
-
   void set_level(LogLevelType level) {
     _level = level;
   }
--- a/hotspot/src/share/vm/logging/logOutputList.hpp	Sun Aug 21 20:56:37 2016 -0400
+++ b/hotspot/src/share/vm/logging/logOutputList.hpp	Thu Jul 14 09:52:03 2016 +0200
@@ -60,6 +60,11 @@
   void add_output(LogOutput* output, LogLevelType level);
   void update_output_level(LogOutputNode* node, LogLevelType level);
 
+  // Bookkeeping functions to keep track of number of active readers/iterators for the list.
+  jint increase_readers();
+  jint decrease_readers();
+  void wait_until_no_readers() const;
+
  public:
   LogOutputList() : _active_readers(0) {
     for (size_t i = 0; i < LogLevel::Count; i++) {
@@ -83,11 +88,6 @@
   // Set (add/update/remove) the output to the specified level.
   void set_output_level(LogOutput* output, LogLevelType level);
 
-  // Bookkeeping functions to keep track of number of active readers/iterators for the list.
-  jint increase_readers();
-  jint decrease_readers();
-  void wait_until_no_readers() const;
-
   class Iterator VALUE_OBJ_CLASS_SPEC {
     friend class LogOutputList;
    private:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/logTestFixture.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2016, 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 "logTestFixture.hpp"
+#include "logTestUtils.inline.hpp"
+#include "logging/logConfiguration.hpp"
+#include "memory/resourceArea.hpp"
+#include "unittest.hpp"
+#include "utilities/ostream.hpp"
+
+LogTestFixture::LogTestFixture() {
+  // Set up TestLogFileName to include PID, testcase name and test name
+  int ret = jio_snprintf(_filename, sizeof(_filename), "testlog.pid%d.%s.%s.log",
+                         os::current_process_id(),
+                         ::testing::UnitTest::GetInstance()->current_test_info()->test_case_name(),
+                         ::testing::UnitTest::GetInstance()->current_test_info()->name());
+  EXPECT_GT(ret, 0) << "_filename buffer issue";
+  TestLogFileName = _filename;
+}
+
+LogTestFixture::~LogTestFixture() {
+  restore_default_log_config();
+  delete_file(TestLogFileName);
+}
+
+bool LogTestFixture::set_log_config(const char* output,
+                                    const char* what,
+                                    const char* decorators,
+                                    const char* options,
+                                    bool allow_failure) {
+  ResourceMark rm;
+  stringStream stream;
+  bool success = LogConfiguration::parse_log_arguments(output, what, decorators, options, &stream);
+  if (!allow_failure) {
+    const char* errmsg = stream.as_string();
+    EXPECT_STREQ("", errmsg) << "Unexpected error reported";
+    EXPECT_TRUE(success) << "Shouldn't cause errors";
+  }
+  return success;
+}
+
+void LogTestFixture::restore_default_log_config() {
+  LogConfiguration::disable_logging();
+  set_log_config("stdout", "all=warning");
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/logTestFixture.hpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016, 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 "unittest.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+// A fixture base class for tests that need to change the log configuration,
+// or use a log file. After each test, the fixture will automatically restore
+// the log configuration and remove the test file (if used).
+// Provides TestLogFileName which is unique for each test, and is automatically
+// deleted after the test completes.
+class LogTestFixture : public testing::Test {
+ private:
+  char _filename[2 * K];
+
+ protected:
+  const char* TestLogFileName;
+
+  LogTestFixture();
+  ~LogTestFixture();
+
+  static bool set_log_config(const char* output,
+                             const char* what,
+                             const char* decorators = "",
+                             const char* options = "",
+                             bool allow_failure = false);
+
+  static void restore_default_log_config();
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/logTestUtils.inline.hpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016, 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 "runtime/os.hpp"
+#include "unittest.hpp"
+
+#define LOG_TEST_STRING_LITERAL "a (hopefully) unique log message for testing"
+
+static inline bool string_contains_substring(const char* haystack, const char* needle) {
+  return strstr(haystack, needle) != NULL;
+}
+
+static inline bool file_exists(const char* filename) {
+  struct stat st;
+  return os::stat(filename, &st) == 0;
+}
+
+static inline void delete_file(const char* filename) {
+  if (!file_exists(filename)) {
+    return;
+  }
+  int ret = remove(filename);
+  EXPECT_TRUE(ret == 0 || errno == ENOENT) << "failed to remove file '" << filename << "': "
+      << os::strerror(errno) << " (" << errno << ")";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logConfiguration.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2016, 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 "logTestFixture.hpp"
+#include "logTestUtils.inline.hpp"
+#include "logging/logConfiguration.hpp"
+#include "logging/logLevel.hpp"
+#include "logging/logOutput.hpp"
+#include "logging/logTag.hpp"
+#include "logging/logTagSet.hpp"
+#include "memory/resourceArea.hpp"
+#include "unittest.hpp"
+#include "utilities/ostream.hpp"
+
+class LogConfigurationTest : public LogTestFixture {
+ protected:
+  static char _all_decorators[256];
+
+ public:
+  static void SetUpTestCase();
+};
+
+char LogConfigurationTest::_all_decorators[256];
+
+// Prepare _all_decorators to contain the full list of decorators (comma separated)
+void LogConfigurationTest::SetUpTestCase() {
+  char *pos = _all_decorators;
+  for (size_t i = 0; i < LogDecorators::Count; i++) {
+    pos += jio_snprintf(pos, sizeof(_all_decorators) - (pos - _all_decorators), "%s%s",
+                        (i == 0 ? "" : ","),
+                        LogDecorators::name(static_cast<LogDecorators::Decorator>(i)));
+  }
+}
+
+// Check if the given text is included by LogConfiguration::describe()
+static bool is_described(const char* text) {
+  ResourceMark rm;
+  stringStream ss;
+  LogConfiguration::describe(&ss);
+  return string_contains_substring(ss.as_string(), text);
+}
+
+TEST_F(LogConfigurationTest, describe) {
+  ResourceMark rm;
+  stringStream ss;
+  LogConfiguration::describe(&ss);
+  const char* description = ss.as_string();
+
+  // Verify that stdout and stderr are listed by default
+  EXPECT_PRED2(string_contains_substring, description, LogOutput::Stdout->name());
+  EXPECT_PRED2(string_contains_substring, description, LogOutput::Stderr->name());
+
+  // Verify that each tag, level and decorator is listed
+  for (size_t i = 0; i < LogTag::Count; i++) {
+    EXPECT_PRED2(string_contains_substring, description, LogTag::name(static_cast<LogTagType>(i)));
+  }
+  for (size_t i = 0; i < LogLevel::Count; i++) {
+    EXPECT_PRED2(string_contains_substring, description, LogLevel::name(static_cast<LogLevelType>(i)));
+  }
+  for (size_t i = 0; i < LogDecorators::Count; i++) {
+    EXPECT_PRED2(string_contains_substring, description, LogDecorators::name(static_cast<LogDecorators::Decorator>(i)));
+  }
+
+  // Verify that the default configuration is printed
+  char expected_buf[256];
+  int ret = jio_snprintf(expected_buf, sizeof(expected_buf), "=%s", LogLevel::name(LogLevel::Default));
+  ASSERT_NE(-1, ret);
+  EXPECT_PRED2(string_contains_substring, description, expected_buf);
+  EXPECT_PRED2(string_contains_substring, description, "#1: stderr all=off");
+
+  // Verify default decorators are listed
+  LogDecorators default_decorators;
+  expected_buf[0] = '\0';
+  for (size_t i = 0; i < LogDecorators::Count; i++) {
+    LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
+    if (default_decorators.is_decorator(d)) {
+      ASSERT_LT(strlen(expected_buf), sizeof(expected_buf));
+      ret = jio_snprintf(expected_buf + strlen(expected_buf),
+                         sizeof(expected_buf) - strlen(expected_buf),
+                         "%s%s",
+                         strlen(expected_buf) > 0 ? "," : "",
+                         LogDecorators::name(d));
+      ASSERT_NE(-1, ret);
+    }
+  }
+  EXPECT_PRED2(string_contains_substring, description, expected_buf);
+
+  // Add a new output and verify that it gets described after it has been added
+  const char* what = "all=trace";
+  EXPECT_FALSE(is_described(TestLogFileName)) << "Test output already exists!";
+  set_log_config(TestLogFileName, what);
+  EXPECT_TRUE(is_described(TestLogFileName));
+  EXPECT_TRUE(is_described("logging=trace"));
+}
+
+// Test updating an existing log output
+TEST_F(LogConfigurationTest, update_output) {
+  // Update stdout twice, first using it's name, and the second time its index #
+  const char* test_outputs[] = { "stdout", "#0" };
+  for (size_t i = 0; i < ARRAY_SIZE(test_outputs); i++) {
+    set_log_config(test_outputs[i], "all=info");
+
+    // Verify configuration using LogConfiguration::describe
+    EXPECT_TRUE(is_described("#0: stdout"));
+    EXPECT_TRUE(is_described("logging=info"));
+
+    // Verify by iterating over tagsets
+    LogOutput* o = LogOutput::Stdout;
+    for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+      EXPECT_TRUE(ts->has_output(o));
+      EXPECT_TRUE(ts->is_level(LogLevel::Info));
+      EXPECT_FALSE(ts->is_level(LogLevel::Debug));
+    }
+
+    // Now change the level and verify the change propagated
+    set_log_config(test_outputs[i], "all=debug");
+    for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+      EXPECT_TRUE(ts->has_output(o));
+      EXPECT_TRUE(ts->is_level(LogLevel::Debug));
+      EXPECT_FALSE(ts->is_level(LogLevel::Trace));
+    }
+  }
+}
+
+// Test adding a new output to the configuration
+TEST_F(LogConfigurationTest, add_new_output) {
+  const char* what = "all=trace";
+
+  ASSERT_FALSE(is_described(TestLogFileName));
+  set_log_config(TestLogFileName, what);
+
+  // Verify new output using LogConfiguration::describe
+  EXPECT_TRUE(is_described(TestLogFileName));
+  EXPECT_TRUE(is_described("logging=trace"));
+
+  // Also verify by iterating over tagsets, checking levels on tagsets
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    EXPECT_TRUE(ts->is_level(LogLevel::Trace));
+  }
+}
+
+TEST_F(LogConfigurationTest, disable_logging) {
+  // Add TestLogFileName as an output
+  set_log_config(TestLogFileName, "logging=info");
+
+  LogConfiguration::disable_logging();
+
+  // Verify TestLogFileName was disabled
+  EXPECT_FALSE(is_described(TestLogFileName));
+
+  // Verify that no tagset has logging enabled
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    EXPECT_FALSE(ts->has_output(LogOutput::Stdout));
+    EXPECT_FALSE(ts->has_output(LogOutput::Stderr));
+    EXPECT_FALSE(ts->is_level(LogLevel::Error));
+  }
+}
+
+// Test disabling a particular output
+TEST_F(LogConfigurationTest, disable_output) {
+  // Disable the default configuration for stdout
+  set_log_config("stdout", "all=off");
+
+  // Verify configuration using LogConfiguration::describe
+  EXPECT_TRUE(is_described("#0: stdout all=off"));
+
+  // Verify by iterating over tagsets
+  LogOutput* o = LogOutput::Stdout;
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    EXPECT_FALSE(ts->has_output(o));
+    EXPECT_FALSE(ts->is_level(LogLevel::Error));
+  }
+
+  // Add a new file output
+  const char* what = "all=debug";
+  set_log_config(TestLogFileName, what);
+  EXPECT_TRUE(is_described(TestLogFileName));
+
+  // Now disable it, verifying it is removed completely
+  set_log_config(TestLogFileName, "all=off");
+  EXPECT_FALSE(is_described(TestLogFileName));
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    EXPECT_FALSE(ts->is_level(LogLevel::Error));
+  }
+}
+
+// Test reconfiguration of the selected decorators for an output
+TEST_F(LogConfigurationTest, reconfigure_decorators) {
+  // Configure stderr with all decorators
+  set_log_config("stderr", "all=off", _all_decorators);
+  char buf[256];
+  int ret = jio_snprintf(buf, sizeof(buf), "#1: stderr all=off %s", _all_decorators);
+  ASSERT_NE(-1, ret);
+  EXPECT_TRUE(is_described(buf)) << "'" << buf << "' not described after reconfiguration";
+
+  // Now reconfigure logging on stderr with no decorators
+  set_log_config("stderr", "all=off", "none");
+  EXPECT_TRUE(is_described("#1: stderr all=off \n")) << "Expecting no decorators";
+}
+
+// Test that invalid options cause configuration errors
+TEST_F(LogConfigurationTest, invalid_configure_options) {
+  LogConfiguration::disable_logging();
+  const char* invalid_outputs[] = { "#2", "invalidtype=123", ":invalid/path}to*file?" };
+  for (size_t i = 0; i < ARRAY_SIZE(invalid_outputs); i++) {
+    EXPECT_FALSE(set_log_config(invalid_outputs[i], "", "", "", true))
+      << "Accepted invalid output '" << invalid_outputs[i] << "'";
+  }
+  EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("all=invalid_level"));
+  EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("what=invalid"));
+  EXPECT_FALSE(LogConfiguration::parse_command_line_arguments("all::invalid_decorator"));
+}
+
+// Test empty configuration options
+TEST_F(LogConfigurationTest, parse_empty_command_line_arguments) {
+  const char* empty_variations[] = { "", ":", "::", ":::", "::::" };
+  for (size_t i = 0; i < ARRAY_SIZE(empty_variations); i++) {
+    const char* cmdline = empty_variations[i];
+    bool ret = LogConfiguration::parse_command_line_arguments(cmdline);
+    EXPECT_TRUE(ret) << "Error parsing command line arguments '" << cmdline << "'";
+    for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+      EXPECT_EQ(LogLevel::Unspecified, ts->level_for(LogOutput::Stdout));
+    }
+  }
+}
+
+// Test basic command line parsing & configuration
+TEST_F(LogConfigurationTest, parse_command_line_arguments) {
+  // Prepare a command line for logging*=debug on stderr with all decorators
+  int ret;
+  char buf[256];
+  ret = jio_snprintf(buf, sizeof(buf), "logging*=debug:stderr:%s", _all_decorators);
+  ASSERT_NE(-1, ret);
+
+  bool success = LogConfiguration::parse_command_line_arguments(buf);
+  EXPECT_TRUE(success) << "Error parsing valid command line arguments '" << buf << "'";
+  // Ensure the new configuration applied
+  EXPECT_TRUE(is_described("logging=debug"));
+  EXPECT_TRUE(is_described(_all_decorators));
+
+  // Test the configuration of file outputs as well
+  ret = jio_snprintf(buf, sizeof(buf), ":%s", TestLogFileName);
+  ASSERT_NE(-1, ret);
+  EXPECT_TRUE(LogConfiguration::parse_command_line_arguments(buf));
+}
+
+// Test split up log configuration arguments
+TEST_F(LogConfigurationTest, parse_log_arguments) {
+  ResourceMark rm;
+  stringStream ss;
+  // Verify that it's possible to configure each individual tag
+  for (size_t t = 1 /* Skip _NO_TAG */; t < LogTag::Count; t++) {
+    const LogTagType tag = static_cast<LogTagType>(t);
+    EXPECT_TRUE(LogConfiguration::parse_log_arguments("stdout", LogTag::name(tag), "", "", &ss));
+  }
+  // Same for each level
+  for (size_t l = 0; l < LogLevel::Count; l++) {
+    const LogLevelType level = static_cast<LogLevelType>(l);
+    char expected_buf[256];
+    int ret = jio_snprintf(expected_buf, sizeof(expected_buf), "all=%s", LogLevel::name(level));
+    ASSERT_NE(-1, ret);
+    EXPECT_TRUE(LogConfiguration::parse_log_arguments("stderr", expected_buf, "", "", &ss));
+  }
+  // And for each decorator
+  for (size_t d = 0; d < LogDecorators::Count; d++) {
+    const LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(d);
+    EXPECT_TRUE(LogConfiguration::parse_log_arguments("#0", "", LogDecorators::name(decorator), "", &ss));
+  }
+  EXPECT_STREQ("", ss.as_string()) << "Error reported while parsing: " << ss.as_string();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logDecorations.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2016, 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 "logging/logDecorations.hpp"
+#include "logging/logTagSet.hpp"
+#include "runtime/os.hpp"
+#include "unittest.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+static const LogTagSet& tagset = LogTagSetMapping<LOG_TAGS(logging, safepoint)>::tagset();
+static const LogDecorators default_decorators;
+
+TEST(LogDecorations, level) {
+  for (uint l = LogLevel::First; l <= LogLevel::Last; l++) {
+    LogLevelType level = static_cast<LogLevelType>(l);
+    // Create a decorations object for the current level
+    LogDecorations decorations(level, tagset, default_decorators);
+    // Verify that the level decoration matches the specified level
+    EXPECT_STREQ(LogLevel::name(level), decorations.decoration(LogDecorators::level_decorator));
+
+    // Test changing level after object creation time
+    LogLevelType other_level;
+    if (l != LogLevel::Last) {
+      other_level = static_cast<LogLevelType>(l + 1);
+    } else {
+      other_level = static_cast<LogLevelType>(LogLevel::First);
+    }
+    decorations.set_level(other_level);
+    EXPECT_STREQ(LogLevel::name(other_level), decorations.decoration(LogDecorators::level_decorator))
+        << "Decoration reports incorrect value after changing the level";
+  }
+}
+
+TEST(LogDecorations, uptime) {
+  // Verify the format of the decoration
+  int a, b;
+  char decimal_point;
+  LogDecorations decorations(LogLevel::Info, tagset, default_decorators);
+  const char* uptime = decorations.decoration(LogDecorators::uptime_decorator);
+  int read = sscanf(uptime, "%d%c%ds", &a, &decimal_point, &b);
+  EXPECT_EQ(3, read) << "Invalid uptime decoration: " << uptime;
+  EXPECT_TRUE(decimal_point == '.' || decimal_point == ',') << "Invalid uptime decoration: " << uptime;
+
+  // Verify that uptime increases
+  double prev = 0;
+  for (int i = 0; i < 3; i++) {
+    os::naked_short_sleep(10);
+    LogDecorations d(LogLevel::Info, tagset, default_decorators);
+    double cur = strtod(d.decoration(LogDecorators::uptime_decorator), NULL);
+    ASSERT_LT(prev, cur);
+    prev = cur;
+  }
+}
+
+TEST(LogDecorations, tags) {
+  char expected_tags[1 * K];
+  tagset.label(expected_tags, sizeof(expected_tags));
+  // Verify that the expected tags are included in the tags decoration
+  LogDecorations decorations(LogLevel::Info, tagset, default_decorators);
+  EXPECT_STREQ(expected_tags, decorations.decoration(LogDecorators::tags_decorator));
+}
+
+// Test each variation of the different timestamp decorations (ms, ns, uptime ms, uptime ns)
+TEST(LogDecorations, timestamps) {
+  struct {
+    const LogDecorators::Decorator decorator;
+    const char* suffix;
+  } test_decorator[] = {
+    { LogDecorators::timemillis_decorator, "ms" },
+    { LogDecorators::uptimemillis_decorator, "ms" },
+    { LogDecorators::timenanos_decorator, "ns" },
+    { LogDecorators::uptimenanos_decorator, "ns" }
+  };
+
+  for (uint i = 0; i < ARRAY_SIZE(test_decorator); i++) {
+    LogDecorators::Decorator decorator = test_decorator[i].decorator;
+    LogDecorators decorator_selection;
+    ASSERT_TRUE(decorator_selection.parse(LogDecorators::name(decorator)));
+
+    // Create decorations with the decorator we want to test included
+    LogDecorations decorations(LogLevel::Info, tagset, decorator_selection);
+    const char* decoration = decorations.decoration(decorator);
+
+    // Verify format of timestamp
+    const char* suffix;
+    for (suffix = decoration; isdigit(*suffix); suffix++) {
+      // Skip over digits
+    }
+    EXPECT_STREQ(test_decorator[i].suffix, suffix);
+
+    // Verify timestamp values
+    julong prev = 0;
+    for (int i = 0; i < 3; i++) {
+      os::naked_short_sleep(5);
+      LogDecorations d(LogLevel::Info, tagset, decorator_selection);
+      julong val = strtoull(d.decoration(decorator), NULL, 10);
+      ASSERT_LT(prev, val);
+      prev = val;
+    }
+  }
+}
+
+// Test the time decoration
+TEST(LogDecorations, iso8601_time) {
+  LogDecorators decorator_selection;
+  ASSERT_TRUE(decorator_selection.parse("time"));
+  LogDecorations decorations(LogLevel::Info, tagset, decorator_selection);
+
+  const char *timestr = decorations.decoration(LogDecorators::time_decorator);
+  time_t expected_ts = time(NULL);
+
+  // Verify format
+  int y, M, d, h, m;
+  double s;
+  int read = sscanf(timestr, "%d-%d-%dT%d:%d:%lfZ", &y, &M, &d, &h, &m, &s);
+  ASSERT_EQ(6, read);
+
+  // Verify reported time & date
+  struct tm reported_time = {0};
+  reported_time.tm_year = y - 1900;
+  reported_time.tm_mon = M - 1;
+  reported_time.tm_mday = d;
+  reported_time.tm_hour = h;
+  reported_time.tm_min = m;
+  reported_time.tm_sec = s;
+  reported_time.tm_isdst = daylight;
+  time_t reported_ts = mktime(&reported_time);
+  expected_ts = mktime(localtime(&expected_ts));
+  time_t diff = reported_ts - expected_ts;
+  if (diff < 0) {
+    diff = -diff;
+  }
+  // Allow up to 10 seconds in difference
+  ASSERT_LE(diff, 10) << "Reported time: " << reported_ts << " (" << timestr << ")"
+      << ", expected time: " << expected_ts;
+}
+
+// Test the pid and tid decorations
+TEST(LogDecorations, identifiers) {
+  LogDecorators decorator_selection;
+  ASSERT_TRUE(decorator_selection.parse("pid,tid"));
+  LogDecorations decorations(LogLevel::Info, tagset, decorator_selection);
+
+  struct {
+      intx expected;
+      LogDecorators::Decorator decorator;
+  } ids[] = {
+      { os::current_process_id(), LogDecorators::pid_decorator },
+      { os::current_thread_id(), LogDecorators::tid_decorator },
+  };
+
+  for (uint i = 0; i < ARRAY_SIZE(ids); i++) {
+    const char* reported = decorations.decoration(ids[i].decorator);
+
+    // Verify format
+    const char* str;
+    for (str = reported; isdigit(*str); str++) {
+      // Skip over digits
+    }
+    EXPECT_EQ('\0', *str) << "Should only contain digits";
+
+    // Verify value
+    EXPECT_EQ(ids[i].expected, strtol(reported, NULL, 10));
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logDecorators.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2016, 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 "logging/logDecorators.hpp"
+#include "unittest.hpp"
+
+static LogDecorators::Decorator decorator_array[] = {
+#define DECORATOR(name, abbr) LogDecorators::name##_decorator,
+    DECORATOR_LIST
+#undef DECORATOR
+};
+
+static const char* decorator_name_array[] = {
+#define DECORATOR(name, abbr) #name,
+    DECORATOR_LIST
+#undef DECORATOR
+};
+
+static const char* decorator_abbr_array[] = {
+#define DECORATOR(name, abbr) #abbr,
+    DECORATOR_LIST
+#undef DECORATOR
+};
+
+// Assert that the given decorators object has the default decorators (uptime, level, tags)
+// If exclusive = true, also assert that no other decorators are selected
+static void assert_default_decorators(LogDecorators* decorators, bool exclusive = true) {
+  for (int i = 0; i < LogDecorators::Count; i++) {
+    LogDecorators::Decorator decorator = decorator_array[i];
+    if (decorator == LogDecorators::uptime_decorator ||
+        decorator == LogDecorators::level_decorator ||
+        decorator == LogDecorators::tags_decorator) {
+      EXPECT_TRUE(decorators->is_decorator(decorator));
+    } else if (exclusive) {
+      EXPECT_FALSE(decorators->is_decorator(decorator));
+    }
+  }
+}
+
+TEST(LogDecorators, defaults) {
+  LogDecorators decorators;
+  assert_default_decorators(&decorators);
+}
+
+// Test converting between name and decorator (string and enum)
+TEST(LogDecorators, from_and_to_name) {
+  EXPECT_EQ(LogDecorators::Invalid, LogDecorators::from_string("unknown"));
+  EXPECT_EQ(LogDecorators::Invalid, LogDecorators::from_string(""));
+
+  for (int i = 0; i < LogDecorators::Count; i++) {
+    LogDecorators::Decorator decorator = decorator_array[i];
+
+    const char* name = LogDecorators::name(decorator);
+    EXPECT_STREQ(decorator_name_array[i], name);
+
+    LogDecorators::Decorator decorator2 = LogDecorators::from_string(name);
+    EXPECT_EQ(decorator, decorator2);
+
+    // Test case insensitivity
+    char* name_cpy = strdup(name);
+    name_cpy[0] = toupper(name_cpy[0]);
+    decorator2 = LogDecorators::from_string(name_cpy);
+    free(name_cpy);
+    EXPECT_EQ(decorator, decorator2);
+  }
+}
+
+// Test decorator abbreviations
+TEST(LogDecorators, from_and_to_abbr) {
+  for (int i = 0; i < LogDecorators::Count; i++) {
+    LogDecorators::Decorator decorator = decorator_array[i];
+
+    const char* abbr = LogDecorators::abbreviation(decorator);
+    EXPECT_STREQ(decorator_abbr_array[i], abbr);
+
+    LogDecorators::Decorator decorator2 = LogDecorators::from_string(abbr);
+    ASSERT_EQ(decorator, decorator2);
+
+    // Test case insensitivity
+    char* abbr_cpy = strdup(abbr);
+    abbr_cpy[0] = toupper(abbr_cpy[0]);
+    decorator2 = LogDecorators::from_string(abbr_cpy);
+    free(abbr_cpy);
+    EXPECT_EQ(decorator, decorator2);
+  }
+}
+
+TEST(LogDecorators, parse_default) {
+  LogDecorators decorators;
+  decorators.parse(""); // Empty string means we should use the default decorators
+  assert_default_decorators(&decorators);
+}
+
+// Test that "none" gives no decorators at all
+TEST(LogDecorators, parse_none) {
+  LogDecorators decorators;
+  decorators.parse("none");
+  for (int i = 0; i < LogDecorators::Count; i++) {
+    EXPECT_FALSE(decorators.is_decorator(decorator_array[i]));
+  }
+}
+
+// Test a few invalid decorator selections
+TEST(LogDecorators, parse_invalid) {
+  LogDecorators decorators;
+  EXPECT_FALSE(decorators.parse("invalid"));
+  EXPECT_FALSE(decorators.parse(",invalid"));
+  EXPECT_FALSE(decorators.parse(",invalid,"));
+  assert_default_decorators(&decorators);
+}
+
+// Assert that the given decorator has all decorators between first and last
+static void assert_decorations_between(const LogDecorators* decorator, size_t first, size_t last) {
+  for (size_t i = 0; i < ARRAY_SIZE(decorator_array); i++) {
+    if (i >= first && i <= last) {
+      EXPECT_TRUE(decorator->is_decorator(decorator_array[i]));
+    } else {
+      EXPECT_FALSE(decorator->is_decorator(decorator_array[i]));
+    }
+  }
+}
+
+TEST(LogDecorators, parse) {
+  LogDecorators decorators;
+
+  // Verify a bunch of different decorator selections
+  char decstr[1 * K];
+  decstr[0] = '\0';
+  size_t written = 0;
+  for (size_t i = 0; i < ARRAY_SIZE(decorator_array); i++) {
+    for (size_t j = i; j < ARRAY_SIZE(decorator_array); j++) {
+      for (size_t k = i; k <= j; k++) {
+        ASSERT_LT(written, sizeof(decstr)) << "decstr overflow";
+        int ret = jio_snprintf(decstr + written, sizeof(decstr) - written, "%s%s",
+                               written == 0 ? "" : ",",
+                               ((k + j) % 2 == 0) ? decorator_name_array[k] : decorator_abbr_array[k]);
+        ASSERT_NE(-1, ret);
+        written += ret;
+      }
+      EXPECT_TRUE(decorators.parse(decstr)) << "Valid decorator selection did not parse: " << decstr;
+      assert_decorations_between(&decorators, i, j);
+      written = 0;
+      decstr[0] = '\0';
+    }
+  }
+}
+
+TEST(LogDecorators, combine_with) {
+  LogDecorators dec1;
+  LogDecorators dec2;
+
+  // Select first and third decorator for dec1
+  char input[64];
+  sprintf(input, "%s,%s", decorator_name_array[0], decorator_name_array[2]);
+  dec1.parse(input);
+  EXPECT_TRUE(dec1.is_decorator(decorator_array[0]));
+  EXPECT_TRUE(dec1.is_decorator(decorator_array[2]));
+
+  // Select the default decorators for dec2
+  EXPECT_FALSE(dec2.is_decorator(decorator_array[0]));
+  EXPECT_FALSE(dec2.is_decorator(decorator_array[2]));
+  assert_default_decorators(&dec2);
+
+  // Combine and verify that the combination includes first, third and default decorators
+  dec2.combine_with(dec1);
+  EXPECT_TRUE(dec2.is_decorator(decorator_array[0]));
+  EXPECT_TRUE(dec2.is_decorator(decorator_array[2]));
+  assert_default_decorators(&dec2, false);
+}
+
+TEST(LogDecorators, clear) {
+  // Start with default decorators and then clear it
+  LogDecorators dec;
+  EXPECT_FALSE(dec.is_empty());
+
+  dec.clear();
+  EXPECT_TRUE(dec.is_empty());
+  for (size_t i = 0; i < LogDecorators::Count; i++) {
+    EXPECT_FALSE(dec.is_decorator(decorator_array[i]));
+  }
+}
+
+// Test the decorator constant None
+TEST(LogDecorators, none) {
+  LogDecorators dec = LogDecorators::None;
+  for (size_t i = 0; i < LogDecorators::Count; i++) {
+    EXPECT_FALSE(dec.is_decorator(decorator_array[i]));
+  }
+}
+
+TEST(LogDecorators, is_empty) {
+  LogDecorators def, none = LogDecorators::None;
+  EXPECT_FALSE(def.is_empty());
+  EXPECT_TRUE(none.is_empty());
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logFileOutput.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016, 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 "logging/logFileOutput.hpp"
+#include "memory/resourceArea.hpp"
+#include "runtime/os.hpp"
+#include "unittest.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/ostream.hpp"
+
+static const char* name = "testlog.pid%p.%t.log";
+
+// Test parsing a bunch of valid file output options
+TEST(LogFileOutput, parse_valid) {
+  const char* valid_options[] = {
+    "", "filecount=10", "filesize=512",
+    "filecount=11,filesize=256",
+    "filesize=256,filecount=11",
+    "filesize=0", "filecount=1",
+    "filesize=1m", "filesize=1M",
+    "filesize=1k", "filesize=1G"
+  };
+
+  // Override LogOutput's vm_start time to get predictable file name
+  LogFileOutput::set_file_name_parameters(0);
+  char expected_filename[1 * K];
+  int ret = jio_snprintf(expected_filename, sizeof(expected_filename),
+                         "testlog.pid%d.1970-01-01_01-00-00.log",
+                         os::current_process_id());
+  ASSERT_GT(ret, 0) << "Buffer too small";
+
+  for (size_t i = 0; i < ARRAY_SIZE(valid_options); i++) {
+    ResourceMark rm;
+    stringStream ss;
+    {
+      LogFileOutput fo(name);
+      EXPECT_STREQ(name, fo.name());
+      EXPECT_TRUE(fo.initialize(valid_options[i], &ss))
+        << "Did not accept valid option(s) '" << valid_options[i] << "': " << ss.as_string();
+    }
+    remove(expected_filename);
+  }
+}
+
+// Test parsing a bunch of invalid file output options
+TEST(LogFileOutput, parse_invalid) {
+  const char* invalid_options[] = {
+    "invalidopt", "filecount=",
+    "filesize=,filecount=10",
+    "fileco=10", "ilesize=512",
+    "filecount=11,,filesize=256",
+    ",filesize=256,filecount=11",
+    "filesize=256,filecount=11,",
+    "filesize=-1", "filecount=0.1",
+    "filecount=-2", "filecount=2.0",
+    "filecount= 2", "filesize=2 ",
+    "filecount=ab", "filesize=0xz",
+    "filecount=1MB", "filesize=99bytes",
+    "filesize=9999999999999999999999999"
+    "filecount=9999999999999999999999999"
+  };
+
+  for (size_t i = 0; i < ARRAY_SIZE(invalid_options); i++) {
+    ResourceMark rm;
+    stringStream ss;
+    LogFileOutput fo(name);
+    EXPECT_FALSE(fo.initialize(invalid_options[i], &ss))
+      << "Accepted invalid option(s) '" << invalid_options[i] << "': " << ss.as_string();
+  }
+}
+
+// Test for overflows with filesize
+TEST(LogFileOutput, filesize_overflow) {
+  char buf[256];
+  int ret = jio_snprintf(buf, sizeof(buf), "filesize=" SIZE_FORMAT "K", SIZE_MAX);
+  ASSERT_GT(ret, 0) << "Buffer too small";
+
+  ResourceMark rm;
+  stringStream ss;
+  LogFileOutput fo(name);
+  EXPECT_FALSE(fo.initialize(buf, &ss)) << "Accepted filesize that overflows";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logLevel.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016, 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 "logging/logLevel.hpp"
+#include "unittest.hpp"
+
+TEST(LogLevel, from_string) {
+  LogLevelType level;
+
+  // Verify each name defined in the LOG_LEVEL_LIST
+#define LOG_LEVEL(lname, lstring) \
+  level = LogLevel::from_string(#lstring); \
+  EXPECT_EQ(level, LogLevel::lname);
+  LOG_LEVEL_LIST
+#undef LOG_LEVEL
+
+  // Verify a few invalid level strings
+  EXPECT_EQ(LogLevel::Invalid, LogLevel::from_string("bad level"));
+  EXPECT_EQ(LogLevel::Invalid, LogLevel::from_string("debugger"));
+  EXPECT_EQ(LogLevel::Invalid, LogLevel::from_string("inf"));
+  EXPECT_EQ(LogLevel::Invalid, LogLevel::from_string("info "));
+  EXPECT_EQ(LogLevel::Invalid, LogLevel::from_string("  info"));
+  EXPECT_EQ(LogLevel::Invalid, LogLevel::from_string("=info"));
+  EXPECT_EQ(LogLevel::Invalid, LogLevel::from_string("infodebugwarning"));
+}
+
+TEST(LogLevel, name) {
+  // Use names from macro as reference
+#define LOG_LEVEL(lname, lstring) \
+  EXPECT_STREQ(LogLevel::name(LogLevel::lname), #lstring);
+  LOG_LEVEL_LIST
+#undef LOG_LEVEL
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logOutputList.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2016, 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 "logging/logLevel.hpp"
+#include "logging/logOutput.hpp"
+#include "logging/logOutputList.hpp"
+#include "runtime/os.hpp"
+#include "unittest.hpp"
+
+// Count the outputs in the given list, starting from the specified level
+static size_t output_count(LogOutputList* list, LogLevelType from = LogLevel::Error)  {
+  size_t count = 0;
+  for (LogOutputList::Iterator it = list->iterator(from); it != list->end(); it++) {
+    count++;
+  }
+  return count;
+}
+
+// Get the level for an output in the given list
+static LogLevelType find_output_level(LogOutputList* list, LogOutput* o) {
+  for (size_t levelnum = 1; levelnum < LogLevel::Count; levelnum++) {
+    LogLevelType level = static_cast<LogLevelType>(levelnum);
+    for (LogOutputList::Iterator it = list->iterator(level); it != list->end(); it++) {
+      if (*it == o) {
+        return level;
+      }
+    }
+  }
+  return LogLevel::Off;
+}
+
+// Create a dummy output pointer with the specified id.
+// This dummy pointer should not be used for anything
+// but pointer comparisons with other dummies.
+static LogOutput* dummy_output(size_t id) {
+  return reinterpret_cast<LogOutput*>(id + 1);
+}
+
+// Randomly update and verify some outputs some number of times
+TEST(LogOutputList, set_output_level_update) {
+  const size_t TestOutputCount = 10;
+  const size_t TestIterations = 10000;
+  LogOutputList list;
+  size_t outputs_on_level[LogLevel::Count];
+  LogLevelType expected_level_for_output[TestOutputCount];
+
+  os::init_random(0x4711);
+  for (size_t i = 0; i < LogLevel::Count; i++) {
+    outputs_on_level[i] = 0;
+  }
+  outputs_on_level[LogLevel::Off] = TestOutputCount;
+  for (size_t i = 0; i < TestOutputCount; i++) {
+    expected_level_for_output[i] = LogLevel::Off;
+  }
+
+  for (size_t iteration = 0; iteration < TestIterations; iteration++) {
+    size_t output_idx = os::random() % TestOutputCount;
+    size_t levelnum = os::random() % LogLevel::Count;
+    LogLevelType level = static_cast<LogLevelType>(levelnum);
+
+    // Update the expectations
+    outputs_on_level[expected_level_for_output[output_idx]]--;
+    outputs_on_level[levelnum]++;
+    expected_level_for_output[output_idx] = level;
+
+    // Update the actual list
+    list.set_output_level(dummy_output(output_idx), level);
+
+    // Verify expected levels
+    for (size_t i = 0; i < TestOutputCount; i++) {
+      ASSERT_EQ(expected_level_for_output[i], find_output_level(&list, dummy_output(i)));
+    }
+    // Verify output counts
+    size_t expected_count = 0;
+    for (size_t i = 1; i < LogLevel::Count; i++) {
+      expected_count += outputs_on_level[i];
+      ASSERT_EQ(expected_count, output_count(&list, static_cast<LogLevelType>(i)));
+    }
+    ASSERT_EQ(TestOutputCount, expected_count + outputs_on_level[LogLevel::Off]);
+  }
+}
+
+// Test removing outputs from a LogOutputList
+TEST(LogOutputList, set_output_level_remove) {
+  LogOutputList list;
+
+  // Add three dummy outputs per loglevel
+  for (size_t i = 1; i < LogLevel::Count; i++) {
+    list.set_output_level(dummy_output(i), static_cast<LogLevelType>(i));
+    list.set_output_level(dummy_output(i*10), static_cast<LogLevelType>(i));
+    list.set_output_level(dummy_output(i*100), static_cast<LogLevelType>(i));
+  }
+
+  // Verify that they have been added successfully
+  // (Count - 1 since we don't count LogLevel::Off)
+  EXPECT_EQ(3u * (LogLevel::Count - 1), output_count(&list));
+  // Now remove the second output from each loglevel
+  for (size_t i = 1; i < LogLevel::Count; i++) {
+    list.set_output_level(dummy_output(i*10), LogLevel::Off);
+  }
+  // Make sure they have been successfully removed
+  EXPECT_EQ(2u * (LogLevel::Count - 1), output_count(&list));
+
+  // Now remove the remaining outputs
+  for (size_t i = 1; i < LogLevel::Count; i++) {
+    list.set_output_level(dummy_output(i), LogLevel::Off);
+    list.set_output_level(dummy_output(i*100), LogLevel::Off);
+  }
+  EXPECT_EQ(0u, output_count(&list));
+}
+
+// Test adding to a LogOutputList
+TEST(LogOutputList, set_output_level_add) {
+  LogOutputList list;
+
+  // First add 5 outputs to Info level
+  for (size_t i = 10; i < 15; i++) {
+    list.set_output_level(dummy_output(i), LogLevel::Info);
+  }
+
+  // Verify that they have been added successfully
+  size_t count = 0;
+  for (LogOutputList::Iterator it = list.iterator(); it != list.end(); it++) {
+    ASSERT_EQ(dummy_output(10 + count++), *it);
+  }
+  ASSERT_EQ(5u, count);
+
+  // Now add more outputs, but on all different levels
+  for (size_t i = 5; i < 10; i++) {
+    list.set_output_level(dummy_output(i), LogLevel::Warning);
+  }
+  for (size_t i = 0; i < 5; i++) {
+    list.set_output_level(dummy_output(i), LogLevel::Error);
+  }
+  for (size_t i = 15; i < 20; i++) {
+    list.set_output_level(dummy_output(i), LogLevel::Debug);
+  }
+  for (size_t i = 20; i < 25; i++) {
+    list.set_output_level(dummy_output(i), LogLevel::Trace);
+  }
+
+  // Verify that that all outputs have been added, and that the order is Error, Warning, Info, Debug, Trace
+  count = 0;
+  for (LogOutputList::Iterator it = list.iterator(); it != list.end(); it++) {
+    ASSERT_EQ(dummy_output(count++), *it);
+  }
+  ASSERT_EQ(25u, count);
+}
+
+// Test is_level() on lists with a single output on different levels
+TEST(LogOutputList, is_level_single_output) {
+  for (size_t i = LogLevel::First; i < LogLevel::Count; i++) {
+    LogLevelType level = static_cast<LogLevelType>(i);
+    LogOutputList list;
+    list.set_output_level(LogOutput::Stdout, level);
+    for (size_t j = LogLevel::First; j < LogLevel::Count; j++) {
+      LogLevelType other = static_cast<LogLevelType>(j);
+      // Verify that levels finer than the current level for stdout are reported as disabled,
+      // and levels equal to or included in the current level are reported as enabled
+      if (other >= level) {
+        EXPECT_TRUE(list.is_level(other))
+          << LogLevel::name(other) << " >= " << LogLevel::name(level) << " but is_level() returns false";
+      } else {
+        EXPECT_FALSE(list.is_level(other))
+          << LogLevel::name(other) << " < " << LogLevel::name(level) << " but is_level() returns true";
+      }
+    }
+  }
+}
+
+// Test is_level() with an empty list
+TEST(LogOutputList, is_level_empty) {
+  LogOutputList emptylist;
+  for (size_t i = LogLevel::First; i < LogLevel::Count; i++) {
+    LogLevelType other = static_cast<LogLevelType>(i);
+    EXPECT_FALSE(emptylist.is_level(other)) << "is_level() returns true even though the list is empty";
+  }
+}
+
+// Test is_level() on lists with two outputs on different levels
+TEST(LogOutputList, is_level_multiple_outputs) {
+  for (size_t i = LogLevel::First; i < LogLevel::Count - 1; i++) {
+      LogOutput* dummy1 = LogOutput::Stdout;
+      LogOutput* dummy2 = LogOutput::Stderr;
+      LogLevelType first = static_cast<LogLevelType>(i);
+      LogLevelType second = static_cast<LogLevelType>(i + 1);
+      LogOutputList list;
+      list.set_output_level(dummy1, first);
+      list.set_output_level(dummy2, second);
+      for (size_t j = LogLevel::First; j < LogLevel::Count; j++) {
+        LogLevelType other = static_cast<LogLevelType>(j);
+        // The first output's level will be the finest, expect it's level to be reported by the list
+        if (other >= first) {
+          EXPECT_TRUE(list.is_level(other))
+            << LogLevel::name(other) << " >= " << LogLevel::name(first) << " but is_level() returns false";
+        } else {
+          EXPECT_FALSE(list.is_level(other))
+            << LogLevel::name(other) << " < " << LogLevel::name(first) << " but is_level() returns true";
+        }
+      }
+    }
+}
+
+TEST(LogOutputList, level_for) {
+  LogOutputList list;
+
+  // Ask the empty list about stdout, stderr
+  EXPECT_EQ(LogLevel::Off, list.level_for(LogOutput::Stdout));
+  EXPECT_EQ(LogLevel::Off, list.level_for(LogOutput::Stderr));
+
+  // Ask for level in a list with two outputs on different levels
+  list.set_output_level(LogOutput::Stdout, LogLevel::Info);
+  list.set_output_level(LogOutput::Stderr, LogLevel::Trace);
+  EXPECT_EQ(LogLevel::Info, list.level_for(LogOutput::Stdout));
+  EXPECT_EQ(LogLevel::Trace, list.level_for(LogOutput::Stderr));
+
+  // Remove and ask again
+  list.set_output_level(LogOutput::Stdout, LogLevel::Off);
+  EXPECT_EQ(LogLevel::Off, list.level_for(LogOutput::Stdout));
+  EXPECT_EQ(LogLevel::Trace, list.level_for(LogOutput::Stderr));
+
+  // Ask about an unknown output
+  LogOutput* dummy = dummy_output(4711);
+  EXPECT_EQ(LogLevel::Off, list.level_for(dummy));
+
+  for (size_t i = LogLevel::First; i <= LogLevel::Last; i++) {
+    LogLevelType level = static_cast<LogLevelType>(i);
+    list.set_output_level(dummy, level);
+    EXPECT_EQ(level, list.level_for(dummy));
+  }
+
+  // Make sure the stderr level is still the same
+  EXPECT_EQ(LogLevel::Trace, list.level_for(LogOutput::Stderr));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logTag.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016, 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 "logging/logTag.hpp"
+#include "unittest.hpp"
+
+TEST(LogTag, from_string) {
+  // Verify for all tags defined in LOG_TAG_LIST
+#define LOG_TAG(tag) \
+  EXPECT_EQ(PREFIX_LOG_TAG(tag), LogTag::from_string(#tag));
+  LOG_TAG_LIST
+#undef LOG_TAG
+
+  // Verify a couple of invalid strings parsing as invalid tags
+  const char* invalid_tag[] = {
+    "bad tag", ".^@", "**", "*", "gcc", "+gc", "gc+", "gc+safepoint",
+    "gc+safepoint=warning", "warning", "=info", "gcsafepointlogging",
+    "gc+safepointlogging", "gclogging", "+", " gc", "logging ", ","
+  };
+  for (size_t i = 0; i < sizeof(invalid_tag) / sizeof(*invalid_tag); i++) {
+    EXPECT_EQ(LogTag::__NO_TAG, LogTag::from_string(invalid_tag[i]))
+        << "'" << invalid_tag[i] << "' did not parse as an invalid tag";
+  }
+}
+
+TEST(LogTag, name) {
+  // Verify for each tag from the macro
+#define LOG_TAG(tag) \
+  EXPECT_STREQ(#tag, LogTag::name(PREFIX_LOG_TAG(tag)));
+  LOG_TAG_LIST
+#undef LOG_TAG
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logTagLevelExpression.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2016, 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 "logging/logLevel.hpp"
+#include "logging/logTagLevelExpression.hpp"
+#include "logging/logTagSet.hpp"
+#include "unittest.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+TEST(LogTagLevelExpression, parse) {
+  char buf[256];
+  const char* invalid_substr[] = {
+    "=", "+", " ", "+=", "+=*", "*+", " +", "**", "++", ".", ",", ",," ",+",
+    " *", "all+", "all*", "+all", "+all=Warning", "==Info", "=InfoWarning",
+    "BadTag+", "logging++", "logging*+", ",=", "gc+gc+gc+gc+gc+gc"
+  };
+  const char* valid_expression[] = {
+    "all", "gc", "gc,logging", "gc+logging", "logging+gc", "logging+gc,gc", "logging+gc*", "gc=trace",
+    "gc=trace,logging=info", "logging+gc=trace", "logging+gc=trace,gc+logging=warning,logging",
+    "gc,all=info", "logging*", "logging*=info", "gc+logging*=error", "logging*,gc=info"
+  };
+
+  // Verify valid expressions parse without problems
+  for (size_t i = 0; i < ARRAY_SIZE(valid_expression); i++) {
+    LogTagLevelExpression expr;
+    EXPECT_TRUE(expr.parse(valid_expression[i])) << "Valid expression '" << valid_expression[i] << "' did not parse";
+  }
+
+  // Verify we can use 'all' with each available level
+  for (uint level = LogLevel::First; level <= LogLevel::Last; level++) {
+    char buf[32];
+    int ret = jio_snprintf(buf, sizeof(buf), "all=%s", LogLevel::name(static_cast<LogLevelType>(level)));
+    ASSERT_NE(ret, -1);
+
+    LogTagLevelExpression expr;
+    EXPECT_TRUE(expr.parse(buf));
+  }
+
+  // Verify invalid expressions do not parse
+  for (size_t i = 0; i < ARRAY_SIZE(valid_expression); i++) {
+    for (size_t j = 0; j < ARRAY_SIZE(invalid_substr); j++) {
+      // Prefix with invalid substr
+      LogTagLevelExpression expr;
+      jio_snprintf(buf, sizeof(buf), "%s%s", invalid_substr[j], valid_expression[i]);
+      EXPECT_FALSE(expr.parse(buf)) << "'" << buf << "'" << " considered legal";
+
+      // Suffix with invalid substr
+      LogTagLevelExpression expr1;
+      jio_snprintf(buf, sizeof(buf), "%s%s", valid_expression[i], invalid_substr[j]);
+      EXPECT_FALSE(expr1.parse(buf)) << "'" << buf << "'" << " considered legal";
+
+      // Use only the invalid substr
+      LogTagLevelExpression expr2;
+      EXPECT_FALSE(expr2.parse(invalid_substr[j])) << "'" << invalid_substr[j] << "'" << " considered legal";
+    }
+
+    // Suffix/prefix with some unique invalid prefixes/suffixes
+    LogTagLevelExpression expr;
+    jio_snprintf(buf, sizeof(buf), "*%s", valid_expression[i]);
+    EXPECT_FALSE(expr.parse(buf)) << "'" << buf << "'" << " considered legal";
+
+    LogTagLevelExpression expr1;
+    jio_snprintf(buf, sizeof(buf), "logging*%s", valid_expression[i]);
+    EXPECT_FALSE(expr1.parse(buf)) << "'" << buf << "'" << " considered legal";
+  }
+}
+
+// Test the level_for() function for an empty expression
+TEST(LogTagLevelExpression, level_for_empty) {
+  LogTagLevelExpression emptyexpr;
+  ASSERT_TRUE(emptyexpr.parse(""));
+  // All tagsets should be unspecified since the expression doesn't involve any tagset
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    EXPECT_EQ(LogLevel::Unspecified, emptyexpr.level_for(*ts));
+  }
+}
+
+// Test level_for() with "all" without any specified level
+TEST(LogTagLevelExpression, level_for_all) {
+  LogTagLevelExpression allexpr;
+  ASSERT_TRUE(allexpr.parse("all"));
+  // Level will be unspecified since no level was given
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    EXPECT_EQ(LogLevel::Unspecified, allexpr.level_for(*ts));
+  }
+}
+
+// Test level_for() with "all=debug"
+TEST(LogTagLevelExpression, level_for_all_debug) {
+  LogTagLevelExpression alldebugexpr;
+  ASSERT_TRUE(alldebugexpr.parse("all=debug"));
+  // All tagsets should report debug level
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    EXPECT_EQ(LogLevel::Debug, alldebugexpr.level_for(*ts));
+  }
+}
+
+// Test level_for() with "all=off"
+TEST(LogTagLevelExpression, level_for_all_off) {
+  LogTagLevelExpression alloffexpr;
+  ASSERT_TRUE(alloffexpr.parse("all=off"));
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    EXPECT_EQ(LogLevel::Off, alloffexpr.level_for(*ts));
+  }
+}
+
+// Test level_for() with an expression that has overlap (last subexpression should be used)
+TEST(LogTagLevelExpression, level_for_overlap) {
+  LogTagLevelExpression overlapexpr;
+  // The all=warning will be overridden with gc=info and/or logging+safepoint*=trace
+  ASSERT_TRUE(overlapexpr.parse("all=warning,gc=info,logging+safepoint*=trace"));
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    if (ts->contains(PREFIX_LOG_TAG(gc)) && ts->ntags() == 1) {
+      EXPECT_EQ(LogLevel::Info, overlapexpr.level_for(*ts));
+    } else if (ts->contains(PREFIX_LOG_TAG(logging)) && ts->contains(PREFIX_LOG_TAG(safepoint))) {
+      EXPECT_EQ(LogLevel::Trace, overlapexpr.level_for(*ts));
+    } else {
+      EXPECT_EQ(LogLevel::Warning, overlapexpr.level_for(*ts));
+    }
+  }
+  EXPECT_EQ(LogLevel::Warning, overlapexpr.level_for(LogTagSetMapping<LOG_TAGS(class)>::tagset()));
+  EXPECT_EQ(LogLevel::Info, overlapexpr.level_for(LogTagSetMapping<LOG_TAGS(gc)>::tagset()));
+  EXPECT_EQ(LogLevel::Trace, overlapexpr.level_for(LogTagSetMapping<LOG_TAGS(logging, safepoint)>::tagset()));
+  EXPECT_EQ(LogLevel::Trace,
+            overlapexpr.level_for(LogTagSetMapping<LOG_TAGS(logging, gc, class, safepoint, heap)>::tagset()));
+}
+
+// Test level_for() with an expression containing two independent subexpressions
+TEST(LogTagLevelExpression, level_for_disjoint) {
+  LogTagLevelExpression reducedexpr;
+  ASSERT_TRUE(reducedexpr.parse("gc+logging=trace,class*=error"));
+  EXPECT_EQ(LogLevel::Error, reducedexpr.level_for(LogTagSetMapping<LOG_TAGS(class)>::tagset()));
+  EXPECT_EQ(LogLevel::Error, reducedexpr.level_for(LogTagSetMapping<LOG_TAGS(safepoint, class)>::tagset()));
+  EXPECT_EQ(LogLevel::NotMentioned, reducedexpr.level_for(LogTagSetMapping<LOG_TAGS(safepoint)>::tagset()));
+  EXPECT_EQ(LogLevel::NotMentioned, reducedexpr.level_for(LogTagSetMapping<LOG_TAGS(logging)>::tagset()));
+  EXPECT_EQ(LogLevel::NotMentioned, reducedexpr.level_for(LogTagSetMapping<LOG_TAGS(gc)>::tagset()));
+  EXPECT_EQ(LogLevel::Trace, reducedexpr.level_for(LogTagSetMapping<LOG_TAGS(logging, gc)>::tagset()));
+}
+
+// Test level_for() with an expression that is completely overridden in the last part of the expression
+TEST(LogTagLevelExpression, level_for_override) {
+  LogTagLevelExpression overrideexpr;
+  // No matter what, everything should be set to error level because of the last part
+  ASSERT_TRUE(overrideexpr.parse("logging,gc*=trace,all=error"));
+  EXPECT_EQ(LogLevel::Error, overrideexpr.level_for(LogTagSetMapping<LOG_TAGS(class)>::tagset()));
+  EXPECT_EQ(LogLevel::Error, overrideexpr.level_for(LogTagSetMapping<LOG_TAGS(logging)>::tagset()));
+  EXPECT_EQ(LogLevel::Error, overrideexpr.level_for(LogTagSetMapping<LOG_TAGS(gc)>::tagset()));
+  EXPECT_EQ(LogLevel::Error, overrideexpr.level_for(LogTagSetMapping<LOG_TAGS(logging, gc)>::tagset()));
+}
+
+// Test level_for() with a mixed expression with a bit of everything
+TEST(LogTagLevelExpression, level_for_mixed) {
+  LogTagLevelExpression mixedexpr;
+  ASSERT_TRUE(mixedexpr.parse("all=warning,gc*=debug,gc=trace,safepoint*=off"));
+  EXPECT_EQ(LogLevel::Warning, mixedexpr.level_for(LogTagSetMapping<LOG_TAGS(logging)>::tagset()));
+  EXPECT_EQ(LogLevel::Warning, mixedexpr.level_for(LogTagSetMapping<LOG_TAGS(logging, class)>::tagset()));
+  EXPECT_EQ(LogLevel::Debug, mixedexpr.level_for(LogTagSetMapping<LOG_TAGS(gc, class)>::tagset()));
+  EXPECT_EQ(LogLevel::Off, mixedexpr.level_for(LogTagSetMapping<LOG_TAGS(gc, safepoint, logging)>::tagset()));
+  EXPECT_EQ(LogLevel::Off, mixedexpr.level_for(LogTagSetMapping<LOG_TAGS(safepoint)>::tagset()));
+  EXPECT_EQ(LogLevel::Debug, mixedexpr.level_for(LogTagSetMapping<LOG_TAGS(logging, gc)>::tagset()));
+  EXPECT_EQ(LogLevel::Trace, mixedexpr.level_for(LogTagSetMapping<LOG_TAGS(gc)>::tagset()));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/native/logging/test_logTagSet.cpp	Thu Jul 14 09:52:03 2016 +0200
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2016, 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
+ * ac_heapanied 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 "logging/logLevel.hpp"
+#include "logging/logOutput.hpp"
+#include "logging/logTag.hpp"
+#include "logging/logTagSet.hpp"
+#include "unittest.hpp"
+
+// Test the default level for each tagset
+TEST(LogTagSet, defaults) {
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    char buf[256];
+    ts->label(buf, sizeof(buf));
+    SCOPED_TRACE(buf);
+    EXPECT_TRUE(ts->is_level(LogLevel::Error));
+    EXPECT_TRUE(ts->is_level(LogLevel::Warning));
+    EXPECT_FALSE(ts->is_level(LogLevel::Info));
+    EXPECT_TRUE(ts->has_output(LogOutput::Stdout));
+    EXPECT_FALSE(ts->has_output(LogOutput::Stderr));
+  }
+}
+
+TEST(LogTagSet, has_output) {
+  LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
+  ts.set_output_level(LogOutput::Stderr, LogLevel::Trace);
+  EXPECT_TRUE(ts.has_output(LogOutput::Stderr));
+  EXPECT_FALSE(ts.has_output(NULL));
+  ts.set_output_level(LogOutput::Stderr, LogLevel::Off);
+  EXPECT_FALSE(ts.has_output(LogOutput::Stderr));
+}
+
+TEST(LogTagSet, ntags) {
+  const LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
+  EXPECT_EQ(1u, ts.ntags());
+  const LogTagSet& ts2 = LogTagSetMapping<LOG_TAGS(logging, gc, class, safepoint, heap)>::tagset();
+  EXPECT_EQ(5u, ts2.ntags());
+}
+
+TEST(LogTagSet, is_level) {
+  LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
+  // Set info level on stdout and verify that is_level() reports correctly
+  ts.set_output_level(LogOutput::Stdout, LogLevel::Info);
+  EXPECT_TRUE(ts.is_level(LogLevel::Error));
+  EXPECT_TRUE(ts.is_level(LogLevel::Warning));
+  EXPECT_TRUE(ts.is_level(LogLevel::Info));
+  EXPECT_FALSE(ts.is_level(LogLevel::Debug));
+  EXPECT_FALSE(ts.is_level(LogLevel::Trace));
+  ts.set_output_level(LogOutput::Stdout, LogLevel::Default);
+  EXPECT_TRUE(ts.is_level(LogLevel::Default));
+}
+
+TEST(LogTagSet, level_for) {
+  LogOutput* output = LogOutput::Stdout;
+  LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
+  for (uint i = 0; i < LogLevel::Count; i++) {
+    LogLevelType level = static_cast<LogLevelType>(i);
+    // Set the level and verify that level_for() reports it back
+    ts.set_output_level(output, level);
+    EXPECT_EQ(level, ts.level_for(output));
+  }
+  ts.set_output_level(output, LogLevel::Default);
+}
+
+TEST(LogTagSet, contains) {
+  // Verify that contains works as intended for a few predetermined tagsets
+  const LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
+  EXPECT_TRUE(ts.contains(PREFIX_LOG_TAG(logging)));
+  EXPECT_FALSE(ts.contains(PREFIX_LOG_TAG(gc)));
+  EXPECT_FALSE(ts.contains(PREFIX_LOG_TAG(class)));
+
+  const LogTagSet& ts2 = LogTagSetMapping<LOG_TAGS(logging, gc)>::tagset();
+  EXPECT_TRUE(ts2.contains(PREFIX_LOG_TAG(logging)));
+  EXPECT_TRUE(ts2.contains(PREFIX_LOG_TAG(gc)));
+  EXPECT_FALSE(ts2.contains(PREFIX_LOG_TAG(class)));
+
+  const LogTagSet& ts3 = LogTagSetMapping<LOG_TAGS(logging, gc, class)>::tagset();
+  EXPECT_TRUE(ts3.contains(PREFIX_LOG_TAG(logging)));
+  EXPECT_TRUE(ts3.contains(PREFIX_LOG_TAG(gc)));
+  EXPECT_TRUE(ts3.contains(PREFIX_LOG_TAG(class)));
+  EXPECT_FALSE(ts3.contains(PREFIX_LOG_TAG(safepoint)));
+
+  const LogTagSet& ts4 = LogTagSetMapping<LOG_TAGS(logging, gc, class, safepoint, heap)>::tagset();
+  EXPECT_TRUE(ts4.contains(PREFIX_LOG_TAG(logging)));
+  EXPECT_TRUE(ts4.contains(PREFIX_LOG_TAG(gc)));
+  EXPECT_TRUE(ts4.contains(PREFIX_LOG_TAG(class)));
+  EXPECT_TRUE(ts4.contains(PREFIX_LOG_TAG(safepoint)));
+  EXPECT_TRUE(ts4.contains(PREFIX_LOG_TAG(heap)));
+}
+
+TEST(LogTagSet, label) {
+  char buf[256];
+  const LogTagSet& ts = LogTagSetMapping<LOG_TAGS(logging, safepoint)>::tagset();
+  ASSERT_NE(-1, ts.label(buf, sizeof(buf)));
+  EXPECT_STREQ("logging,safepoint", buf);
+  // Verify using a custom separator
+  ASSERT_NE(-1, ts.label(buf, sizeof(buf), "++"));
+  EXPECT_STREQ("logging++safepoint", buf);
+
+  // Verify with three tags
+  const LogTagSet& ts1 = LogTagSetMapping<LOG_TAGS(logging, safepoint, jni)>::tagset();
+  ASSERT_NE(-1, ts1.label(buf, sizeof(buf)));
+  EXPECT_STREQ("logging,safepoint,jni", buf);
+
+  // Verify with a single tag
+  const LogTagSet& ts2 = LogTagSetMapping<LOG_TAGS(logging)>::tagset();
+  ASSERT_NE(-1, ts2.label(buf, sizeof(buf)));
+  EXPECT_STREQ("logging", buf);
+}