8196783: Refactor LogTagLevelExpression into separate classes
authormlarsson
Mon, 19 Feb 2018 09:46:10 +0100
changeset 49016 ea85eed8b012
parent 49015 a12c9536d8a6
child 49017 e6a79c032ac6
8196783: Refactor LogTagLevelExpression into separate classes Reviewed-by: rehn, pliden
src/hotspot/share/logging/logConfiguration.cpp
src/hotspot/share/logging/logConfiguration.hpp
src/hotspot/share/logging/logSelection.cpp
src/hotspot/share/logging/logSelection.hpp
src/hotspot/share/logging/logSelectionList.cpp
src/hotspot/share/logging/logSelectionList.hpp
src/hotspot/share/logging/logTagLevelExpression.cpp
src/hotspot/share/logging/logTagLevelExpression.hpp
test/hotspot/gtest/logging/logTestUtils.inline.hpp
test/hotspot/gtest/logging/test_logSelection.cpp
test/hotspot/gtest/logging/test_logSelectionList.cpp
test/hotspot/gtest/logging/test_logTagLevelExpression.cpp
--- a/src/hotspot/share/logging/logConfiguration.cpp	Mon Feb 19 09:16:04 2018 +0100
+++ b/src/hotspot/share/logging/logConfiguration.cpp	Mon Feb 19 09:46:10 2018 +0100
@@ -30,8 +30,8 @@
 #include "logging/logDiagnosticCommand.hpp"
 #include "logging/logFileOutput.hpp"
 #include "logging/logOutput.hpp"
+#include "logging/logSelectionList.hpp"
 #include "logging/logStream.hpp"
-#include "logging/logTagLevelExpression.hpp"
 #include "logging/logTagSet.hpp"
 #include "memory/allocation.inline.hpp"
 #include "memory/resourceArea.hpp"
@@ -207,7 +207,7 @@
   delete output;
 }
 
-void LogConfiguration::configure_output(size_t idx, const LogTagLevelExpression& tag_level_expression, const LogDecorators& decorators) {
+void LogConfiguration::configure_output(size_t idx, const LogSelectionList& selections, const LogDecorators& decorators) {
   assert(ConfigurationLock::current_thread_has_lock(), "Must hold configuration lock to call this function.");
   assert(idx < _n_outputs, "Invalid index, idx = " SIZE_FORMAT " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs);
   LogOutput* output = _outputs[idx];
@@ -217,7 +217,7 @@
 
   bool enabled = false;
   for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
-    LogLevelType level = tag_level_expression.level_for(*ts);
+    LogLevelType level = selections.level_for(*ts);
 
     // Ignore tagsets that do not, and will not log on the output
     if (!ts->has_output(output) && (level == LogLevel::NotMentioned || level == LogLevel::Off)) {
@@ -299,11 +299,11 @@
 void LogConfiguration::configure_stdout(LogLevelType level, int exact_match, ...) {
   size_t i;
   va_list ap;
-  LogTagLevelExpression expr;
+  LogTagType tags[LogTag::MaxTags];
   va_start(ap, exact_match);
   for (i = 0; i < LogTag::MaxTags; i++) {
     LogTagType tag = static_cast<LogTagType>(va_arg(ap, int));
-    expr.add_tag(tag);
+    tags[i] = tag;
     if (tag == LogTag::__NO_TAG) {
       assert(i > 0, "Must specify at least one tag!");
       break;
@@ -313,17 +313,14 @@
          "Too many tags specified! Can only have up to " SIZE_FORMAT " tags in a tag set.", LogTag::MaxTags);
   va_end(ap);
 
-  if (!exact_match) {
-    expr.set_allow_other_tags();
-  }
-  expr.set_level(level);
-  expr.new_combination();
-  assert(expr.verify_tagsets(),
-         "configure_stdout() called with invalid/non-existing tag set");
+  LogSelection selection(tags, !exact_match, level);
+  assert(selection.tag_sets_selected() > 0,
+         "configure_stdout() called with invalid/non-existing log selection");
+  LogSelectionList list(selection);
 
   // Apply configuration to stdout (output #0), with the same decorators as before.
   ConfigurationLock cl;
-  configure_output(0, expr, _outputs[0]->decorators());
+  configure_output(0, list, _outputs[0]->decorators());
   notify_update_listeners();
 }
 
@@ -382,7 +379,7 @@
 }
 
 bool LogConfiguration::parse_log_arguments(const char* outputstr,
-                                           const char* what,
+                                           const char* selectionstr,
                                            const char* decoratorstr,
                                            const char* output_options,
                                            outputStream* errstream) {
@@ -391,8 +388,8 @@
     outputstr = "stdout";
   }
 
-  LogTagLevelExpression expr;
-  if (!expr.parse(what, errstream)) {
+  LogSelectionList selections;
+  if (!selections.parse(selectionstr, errstream)) {
     return false;
   }
 
@@ -433,9 +430,9 @@
       return false;
     }
   }
-  configure_output(idx, expr, decorators);
+  configure_output(idx, selections, decorators);
   notify_update_listeners();
-  expr.verify_tagsets(errstream);
+  selections.verify_selections(errstream);
   return true;
 }
 
--- a/src/hotspot/share/logging/logConfiguration.hpp	Mon Feb 19 09:16:04 2018 +0100
+++ b/src/hotspot/share/logging/logConfiguration.hpp	Mon Feb 19 09:46:10 2018 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, 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
@@ -30,7 +30,7 @@
 
 class LogOutput;
 class LogDecorators;
-class LogTagLevelExpression;
+class LogSelectionList;
 
 // Global configuration of logging. Handles parsing and configuration of the logging framework,
 // and manages the list of configured log outputs. The actual tag and level configuration is
@@ -75,7 +75,7 @@
   static size_t find_output(const char* name);
 
   // Configure output (add or update existing configuration) to log on tag-level combination using specified decorators.
-  static void configure_output(size_t idx, const LogTagLevelExpression& tag_level_expression, const LogDecorators& decorators);
+  static void configure_output(size_t idx, const LogSelectionList& tag_level_expression, const LogDecorators& decorators);
 
   // This should be called after any configuration change while still holding ConfigurationLock
   static void notify_update_listeners();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/logging/logSelection.cpp	Mon Feb 19 09:46:10 2018 +0100
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2018, 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 "utilities/ostream.hpp"
+#include "logging/logSelection.hpp"
+#include "logging/logTagSet.hpp"
+#include "runtime/os.inline.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "utilities/ostream.hpp"
+
+const LogSelection LogSelection::Invalid;
+
+LogSelection::LogSelection() : _ntags(0), _wildcard(false), _level(LogLevel::Invalid), _tag_sets_selected(0) {
+}
+
+LogSelection::LogSelection(const LogTagType tags[LogTag::MaxTags], bool wildcard, LogLevelType level)
+    : _ntags(0), _wildcard(wildcard), _level(level), _tag_sets_selected(0) {
+  while (_ntags < LogTag::MaxTags && tags[_ntags] != LogTag::__NO_TAG) {
+    _tags[_ntags] = tags[_ntags];
+    _ntags++;
+  }
+
+  for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
+    if (selects(*ts)) {
+      _tag_sets_selected++;
+    }
+  }
+}
+
+bool LogSelection::operator==(const LogSelection& ref) const {
+  if (_ntags != ref._ntags ||
+      _wildcard != ref._wildcard ||
+      _level != ref._level ||
+      _tag_sets_selected != ref._tag_sets_selected) {
+    return false;
+  }
+  for (size_t i = 0; i < _ntags; i++) {
+    if (_tags[i] != ref._tags[i]) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool LogSelection::operator!=(const LogSelection& ref) const {
+  return !operator==(ref);
+}
+
+static LogSelection parse_internal(char *str, outputStream* errstream) {
+  // Parse the level, if specified
+  LogLevelType level = LogLevel::Unspecified;
+  char* equals = strchr(str, '=');
+  if (equals != NULL) {
+    level = LogLevel::from_string(equals + 1);
+    if (level == LogLevel::Invalid) {
+      if (errstream != NULL) {
+        errstream->print_cr("Invalid level '%s' in log selection.", equals + 1);
+      }
+      return LogSelection::Invalid;
+    }
+    *equals = '\0';
+  }
+
+  size_t ntags = 0;
+  LogTagType tags[LogTag::MaxTags] = { LogTag::__NO_TAG };
+
+  // Parse special tags such as 'all'
+  if (strcmp(str, "all") == 0) {
+    return LogSelection(tags, true, level);
+  }
+
+  // Check for '*' suffix
+  bool wildcard = false;
+  char* asterisk_pos = strchr(str, '*');
+  if (asterisk_pos != NULL && asterisk_pos[1] == '\0') {
+    wildcard = true;
+    *asterisk_pos = '\0';
+  }
+
+  // Parse the tag expression (t1+t2+...+tn)
+  char* plus_pos;
+  char* cur_tag = str;
+  do {
+    plus_pos = strchr(cur_tag, '+');
+    if (plus_pos != NULL) {
+      *plus_pos = '\0';
+    }
+    LogTagType tag = LogTag::from_string(cur_tag);
+    if (tag == LogTag::__NO_TAG) {
+      if (errstream != NULL) {
+        errstream->print_cr("Invalid tag '%s' in log selection.", cur_tag);
+      }
+      return LogSelection::Invalid;
+    }
+    if (ntags == LogTag::MaxTags) {
+      if (errstream != NULL) {
+        errstream->print_cr("Too many tags in log selection '%s' (can only have up to " SIZE_FORMAT " tags).",
+                               str, LogTag::MaxTags);
+      }
+      return LogSelection::Invalid;
+    }
+    tags[ntags++] = tag;
+    cur_tag = plus_pos + 1;
+  } while (plus_pos != NULL);
+
+  for (size_t i = 0; i < ntags; i++) {
+    for (size_t j = 0; j < ntags; j++) {
+      if (i != j && tags[i] == tags[j]) {
+        if (errstream != NULL) {
+          errstream->print_cr("Log selection contains duplicates of tag %s.", LogTag::name(tags[i]));
+        }
+        return LogSelection::Invalid;
+      }
+    }
+  }
+
+  return LogSelection(tags, wildcard, level);
+}
+
+LogSelection LogSelection::parse(const char* str, outputStream* error_stream) {
+  char* copy = os::strdup_check_oom(str, mtLogging);
+  LogSelection s = parse_internal(copy, error_stream);
+  os::free(copy);
+  return s;
+}
+
+bool LogSelection::selects(const LogTagSet& ts) const {
+  if (!_wildcard && _ntags != ts.ntags()) {
+    return false;
+  }
+  for (size_t i = 0; i < _ntags; i++) {
+    if (!ts.contains(_tags[i])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+size_t LogSelection::ntags() const {
+  return _ntags;
+}
+
+LogLevelType LogSelection::level() const {
+  return _level;
+}
+
+size_t LogSelection::tag_sets_selected() const {
+  return _tag_sets_selected;
+}
+
+int LogSelection::describe_tags(char* buf, size_t bufsize) const {
+  int tot_written = 0;
+  for (size_t i = 0; i < _ntags; i++) {
+    int written = jio_snprintf(buf + tot_written, bufsize - tot_written,
+                               "%s%s", (i == 0 ? "" : "+"), LogTag::name(_tags[i]));
+    if (written == -1) {
+      return written;
+    }
+    tot_written += written;
+  }
+
+  if (_wildcard) {
+    int written = jio_snprintf(buf + tot_written, bufsize - tot_written, "*");
+    if (written == -1) {
+      return written;
+    }
+    tot_written += written;
+  }
+  return tot_written;
+}
+
+int LogSelection::describe(char* buf, size_t bufsize) const {
+  int tot_written = describe_tags(buf, bufsize);
+
+  int written = jio_snprintf(buf + tot_written, bufsize - tot_written, "=%s", LogLevel::name(_level));
+  if (written == -1) {
+    return -1;
+  }
+  tot_written += written;
+  return tot_written;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/logging/logSelection.hpp	Mon Feb 19 09:46:10 2018 +0100
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2018, 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.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGSELECTION_HPP
+#define SHARE_VM_LOGGING_LOGSELECTION_HPP
+
+#include "logging/logLevel.hpp"
+#include "logging/logTag.hpp"
+#include "memory/allocation.hpp"
+
+class LogTagSet;
+
+// Class representing a selection of tags with for a given level.
+// Consists of a set of tags, an optional wildcard flag, and a level, e.g. "tag1+tag2*=level".
+class LogSelection : public StackObj {
+  friend class LogSelectionList;
+
+ private:
+  size_t _ntags;
+  LogTagType _tags[LogTag::MaxTags];
+  bool _wildcard;
+  LogLevelType _level;
+  size_t _tag_sets_selected;
+
+  LogSelection();
+
+ public:
+  static const LogSelection Invalid;
+
+  static LogSelection parse(const char* str, outputStream* error_stream = NULL);
+
+  LogSelection(const LogTagType tags[LogTag::MaxTags], bool wildcard, LogLevelType level);
+
+  bool operator==(const LogSelection& ref) const;
+  bool operator!=(const LogSelection& ref) const;
+
+  size_t ntags() const;
+  LogLevelType level() const;
+  size_t tag_sets_selected() const;
+
+  bool selects(const LogTagSet& ts) const;
+
+  int describe_tags(char* buf, size_t bufsize) const;
+  int describe(char* buf, size_t bufsize) const;
+};
+
+#endif // SHARE_VM_LOGGING_LOGSELECTION_HPP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/logging/logSelectionList.cpp	Mon Feb 19 09:46:10 2018 +0100
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2015, 2018, 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/logSelectionList.hpp"
+#include "logging/logTagSet.hpp"
+#include "runtime/arguments.hpp"
+#include "runtime/os.inline.hpp"
+
+static const char* DefaultExpressionString = "all";
+
+bool LogSelectionList::verify_selections(outputStream* out) const {
+  bool valid = true;
+
+  for (size_t i = 0; i < _nselections; i++) {
+    if (_selections[i].tag_sets_selected() == 0) {
+      // Return immediately unless all invalid selections should be listed
+      if (out == NULL) {
+        return false;
+      }
+
+      if (valid) {
+        out->print("No tag set matches selection(s):");
+      }
+      valid = false;
+
+      char buf[256];
+      _selections[i].describe_tags(buf, sizeof(buf));
+      out->print(" %s", buf);
+    }
+  }
+  if (!valid && out != NULL) {
+    out->cr();
+  }
+  return valid;
+}
+
+
+bool LogSelectionList::parse(const char* str, outputStream* errstream) {
+  bool success = true;
+  if (str == NULL || strcmp(str, "") == 0) {
+    str = DefaultExpressionString;
+  }
+  char* copy = os::strdup_check_oom(str, mtLogging);
+  // Split string on commas
+  for (char *comma_pos = copy, *cur = copy; success && comma_pos != NULL; cur = comma_pos + 1) {
+    if (_nselections == MaxSelections) {
+      if (errstream != NULL) {
+        errstream->print_cr("Can not have more than " SIZE_FORMAT " log selections in a single configuration.",
+                            MaxSelections);
+      }
+      success = false;
+      break;
+    }
+
+    comma_pos = strchr(cur, ',');
+    if (comma_pos != NULL) {
+      *comma_pos = '\0';
+    }
+
+    LogSelection selection = LogSelection::parse(cur, errstream);
+    if (selection == LogSelection::Invalid) {
+      success = false;
+      break;
+    }
+    _selections[_nselections++] = selection;
+  }
+
+  os::free(copy);
+  return success;
+}
+
+LogLevelType LogSelectionList::level_for(const LogTagSet& ts) const {
+  // Return NotMentioned if the given tagset isn't covered by this expression.
+  LogLevelType level = LogLevel::NotMentioned;
+  for (size_t i= 0; i < _nselections; i++) {
+    if (_selections[i].selects(ts)) {
+      level = _selections[i].level();
+    }
+  }
+  return level;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/logging/logSelectionList.hpp	Mon Feb 19 09:46:10 2018 +0100
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, 2018 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.
+ *
+ */
+#ifndef SHARE_VM_LOGGING_LOGSELECTIONLIST_HPP
+#define SHARE_VM_LOGGING_LOGSELECTIONLIST_HPP
+
+#include "logging/logConfiguration.hpp"
+#include "logging/logSelection.hpp"
+#include "logging/logTag.hpp"
+#include "memory/allocation.hpp"
+#include "utilities/debug.hpp"
+#include "utilities/globalDefinitions.hpp"
+
+class LogTagSet;
+
+// Class used to temporary encode a series of log selections during log configuration.
+// Consists of ordered LogSelections, i.e. "tag1+tag2=level1,tag3*=level2".
+class LogSelectionList : public StackObj {
+ public:
+  static const size_t MaxSelections = 256;
+
+ private:
+  friend void LogConfiguration::configure_stdout(LogLevelType, int, ...);
+
+  size_t _nselections;
+  LogSelection _selections[MaxSelections];
+
+ public:
+  LogSelectionList() : _nselections(0) {
+  }
+
+  LogSelectionList(const LogSelection& selection) : _nselections(1) {
+    _selections[0] = selection;
+  }
+
+  bool parse(const char* str, outputStream* errstream = NULL);
+  LogLevelType level_for(const LogTagSet& ts) const;
+
+  // Verify that each selection actually selects something.
+  // Returns false if some invalid selection was found. If given an outputstream,
+  // this function will list all the invalid selections on the stream.
+  bool verify_selections(outputStream* out = NULL) const;
+};
+
+#endif // SHARE_VM_LOGGING_LOGSELECTIONLIST_HPP
--- a/src/hotspot/share/logging/logTagLevelExpression.cpp	Mon Feb 19 09:16:04 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,195 +0,0 @@
-/*
- * Copyright (c) 2015, 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/logTagLevelExpression.hpp"
-#include "logging/logTagSet.hpp"
-#include "runtime/arguments.hpp"
-#include "runtime/os.inline.hpp"
-
-const char* LogTagLevelExpression::DefaultExpressionString = "all";
-
-static bool matches_tagset(const LogTagType tags[],
-                           bool allow_other_tags,
-                           const LogTagSet& ts) {
-  bool contains_all = true;
-  size_t tag_idx;
-  for (tag_idx = 0; tag_idx < LogTag::MaxTags && tags[tag_idx] != LogTag::__NO_TAG; tag_idx++) {
-    if (!ts.contains(tags[tag_idx])) {
-      contains_all = false;
-      break;
-    }
-  }
-  // All tags in the expression must be part of the tagset,
-  // and either the expression allows other tags (has a wildcard),
-  // or the number of tags in the expression and tagset must match.
-  return contains_all && (allow_other_tags || tag_idx == ts.ntags());
-}
-
-bool LogTagLevelExpression::verify_tagsets(outputStream* out) const {
-  bool valid = true;
-
-  for (size_t i = 0; i < _ncombinations; i++) {
-    bool matched = false;
-    for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
-      if (matches_tagset(_tags[i], _allow_other_tags[i], *ts)) {
-        matched = true;
-        break;
-      }
-    }
-
-    if (!matched) {
-      // If this was the first invalid combination, write the message header
-      if (valid && out != NULL) {
-        out->print("No tag set matches selection(s): ");
-      }
-      valid = false;
-
-      // Break as soon as possible unless listing all invalid combinations
-      if (out == NULL) {
-        break;
-      }
-
-      // List the combination on the outputStream
-      for (size_t t = 0; t < LogTag::MaxTags && _tags[i][t] != LogTag::__NO_TAG; t++) {
-        out->print("%s%s", (t == 0 ? "" : "+"), LogTag::name(_tags[i][t]));
-      }
-      if (_allow_other_tags[i]) {
-        out->print("*");
-      }
-      out->print(" ");
-    }
-  }
-
-  if (!valid && out != NULL) {
-    out->cr();
-  }
-
-  return valid;
-}
-
-bool LogTagLevelExpression::parse(const char* str, outputStream* errstream) {
-  bool success = true;
-  if (str == NULL || strcmp(str, "") == 0) {
-    str = DefaultExpressionString;
-  }
-  char* copy = os::strdup_check_oom(str, mtLogging);
-  // Split string on commas
-  for (char *comma_pos = copy, *cur = copy; success && comma_pos != NULL; cur = comma_pos + 1) {
-    if (_ncombinations == MaxCombinations) {
-      if (errstream != NULL) {
-        errstream->print_cr("Can not have more than " SIZE_FORMAT " tag combinations in a what-expression.",
-                            MaxCombinations);
-      }
-      success = false;
-      break;
-    }
-
-    comma_pos = strchr(cur, ',');
-    if (comma_pos != NULL) {
-      *comma_pos = '\0';
-    }
-
-    // Parse the level, if specified
-    LogLevelType level = LogLevel::Unspecified;
-    char* equals = strchr(cur, '=');
-    if (equals != NULL) {
-      level = LogLevel::from_string(equals + 1);
-      if (level == LogLevel::Invalid) {
-        if (errstream != NULL) {
-          errstream->print_cr("Invalid level '%s' in what-expression.", equals + 1);
-        }
-        success = false;
-        break;
-      }
-      *equals = '\0'; // now ignore "=level" part of substr
-    }
-    set_level(level);
-
-    // Parse special tags such as 'all'
-    if (strcmp(cur, "all") == 0) {
-      set_allow_other_tags();
-      new_combination();
-      continue;
-    }
-
-    // Check for '*' suffix
-    char* asterisk_pos = strchr(cur, '*');
-    if (asterisk_pos != NULL && asterisk_pos[1] == '\0') {
-      set_allow_other_tags();
-      *asterisk_pos = '\0';
-    }
-
-    // Parse the tag expression (t1+t2+...+tn)
-    char* plus_pos;
-    char* cur_tag = cur;
-    do {
-      plus_pos = strchr(cur_tag, '+');
-      if (plus_pos != NULL) {
-        *plus_pos = '\0';
-      }
-      LogTagType tag = LogTag::from_string(cur_tag);
-      if (tag == LogTag::__NO_TAG) {
-        if (errstream != NULL) {
-          errstream->print_cr("Invalid tag '%s' in what-expression.", cur_tag);
-        }
-        success = false;
-        break;
-      }
-      if (_ntags == LogTag::MaxTags) {
-        if (errstream != NULL) {
-          errstream->print_cr("Tag combination exceeds the maximum of " SIZE_FORMAT " tags.",
-                              LogTag::MaxTags);
-        }
-        success = false;
-        break;
-      }
-      if (!add_tag(tag)) {
-        if (errstream != NULL) {
-          errstream->print_cr("Tag combination have duplicate tag '%s' in what-expression.",
-                              cur_tag);
-        }
-        success = false;
-        break;
-      }
-      cur_tag = plus_pos + 1;
-    } while (plus_pos != NULL);
-
-    new_combination();
-  }
-
-  os::free(copy);
-  return success;
-}
-
-LogLevelType LogTagLevelExpression::level_for(const LogTagSet& ts) const {
-  // Return NotMentioned if the given tagset isn't covered by this expression.
-  LogLevelType level = LogLevel::NotMentioned;
-  for (size_t combination = 0; combination < _ncombinations; combination++) {
-    if (matches_tagset(_tags[combination], _allow_other_tags[combination], ts)) {
-      level = _level[combination];
-    }
-  }
-  return level;
-}
-
--- a/src/hotspot/share/logging/logTagLevelExpression.hpp	Mon Feb 19 09:16:04 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,99 +0,0 @@
-/*
- * Copyright (c) 2015, 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.
- *
- */
-#ifndef SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP
-#define SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP
-
-#include "logging/logConfiguration.hpp"
-#include "logging/logLevel.hpp"
-#include "logging/logTag.hpp"
-#include "memory/allocation.hpp"
-#include "utilities/debug.hpp"
-#include "utilities/globalDefinitions.hpp"
-
-class LogTagSet;
-
-// Class used to temporary encode a 'what'-expression during log configuration.
-// Consists of a combination of tags and levels, e.g. "tag1+tag2=level1,tag3*=level2".
-class LogTagLevelExpression : public StackObj {
- public:
-  static const size_t MaxCombinations = 256;
-
- private:
-  friend void LogConfiguration::configure_stdout(LogLevelType, int, ...);
-
-  static const char* DefaultExpressionString;
-
-  size_t        _ntags, _ncombinations;
-  LogTagType    _tags[MaxCombinations][LogTag::MaxTags];
-  LogLevelType  _level[MaxCombinations];
-  bool          _allow_other_tags[MaxCombinations];
-
-  void new_combination() {
-    // Make sure either all tags are set or the last tag is __NO_TAG
-    if (_ntags < LogTag::MaxTags) {
-      _tags[_ncombinations][_ntags] = LogTag::__NO_TAG;
-    }
-
-    _ncombinations++;
-    _ntags = 0;
-  }
-
-  bool add_tag(LogTagType tag) {
-    assert(_ntags < LogTag::MaxTags, "Can't have more tags than MaxTags!");
-    for (size_t i = 0; i < _ntags; i++) {
-      if (_tags[_ncombinations][i] == tag) {
-        return false;
-      }
-    }
-    _tags[_ncombinations][_ntags++] = tag;
-    return true;
-  }
-
-  void set_level(LogLevelType level) {
-    _level[_ncombinations] = level;
-  }
-
-  void set_allow_other_tags() {
-    _allow_other_tags[_ncombinations] = true;
-  }
-
- public:
-  LogTagLevelExpression() : _ntags(0), _ncombinations(0) {
-    for (size_t combination = 0; combination < MaxCombinations; combination++) {
-      _level[combination] = LogLevel::Invalid;
-      _allow_other_tags[combination] = false;
-      _tags[combination][0] = LogTag::__NO_TAG;
-    }
-  }
-
-  bool parse(const char* str, outputStream* errstream = NULL);
-  LogLevelType level_for(const LogTagSet& ts) const;
-
-  // Verify the tagsets/selections mentioned in this expression.
-  // Returns false if some invalid tagset was found. If given an outputstream,
-  // this function will list all the invalid selections on the stream.
-  bool verify_tagsets(outputStream* out = NULL) const;
-};
-
-#endif // SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP
--- a/test/hotspot/gtest/logging/logTestUtils.inline.hpp	Mon Feb 19 09:16:04 2018 +0100
+++ b/test/hotspot/gtest/logging/logTestUtils.inline.hpp	Mon Feb 19 09:46:10 2018 +0100
@@ -30,6 +30,12 @@
 
 #define LOG_TEST_STRING_LITERAL "a (hopefully) unique log message for testing"
 
+static const char* invalid_selection_substr[] = {
+  "=", "+", " ", "+=", "+=*", "*+", " +", "**", "++", ".", ",", ",," ",+",
+  " *", "all+", "all*", "+all", "+all=Warning", "==Info", "=InfoWarning",
+  "BadTag+", "logging++", "logging*+", ",=", "gc+gc+"
+};
+
 static inline bool string_contains_substring(const char* haystack, const char* needle) {
   return strstr(haystack, needle) != NULL;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/logging/test_logSelection.cpp	Mon Feb 19 09:46:10 2018 +0100
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "precompiled.hpp"
+#include "jvm.h"
+#include "logging/logLevel.hpp"
+#include "logging/logSelection.hpp"
+#include "logging/logTagSet.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "logTestUtils.inline.hpp"
+#include "unittest.hpp"
+
+// These tests can only run in debug VMs because they rely on the (debug-only) LogTag::_test
+#ifdef ASSERT
+
+#define NON_EXISTING_TAG_SET "logging+test+start+exit+safepoint"
+
+// let google test know how to print LogSelection nicely for better error messages
+void PrintTo(const LogSelection& sel, ::std::ostream* os) {
+  if (sel == LogSelection::Invalid) {
+    *os << "LogSelection::Invalid";
+    return;
+  }
+  char buf[256];
+  sel.describe(buf, sizeof(buf));
+  *os << buf;
+}
+
+TEST(LogSelection, sanity) {
+  LogTagType tags[LogTag::MaxTags] = { PREFIX_LOG_TAG(logging), PREFIX_LOG_TAG(test), PREFIX_LOG_TAG(_NO_TAG) };
+  LogSelection selection(tags, false, LogLevel::Trace);
+
+  EXPECT_EQ(2u, selection.ntags());
+  EXPECT_EQ(LogLevel::Trace, selection.level());
+
+  // Verify that copying the selection also works as expected
+  LogSelection copy = selection;
+  EXPECT_EQ(2u, copy.ntags());
+  EXPECT_EQ(LogLevel::Trace, copy.level());
+
+  tags[0] = PREFIX_LOG_TAG(gc);
+  tags[1] = PREFIX_LOG_TAG(_NO_TAG);
+  LogSelection copy2(tags, true, LogLevel::Off); // start with a completely different selection
+  copy2 = selection; // and test copy assignment
+  EXPECT_EQ(2u, copy2.ntags());
+  EXPECT_EQ(LogLevel::Trace, copy2.level());
+}
+
+TEST(LogSelection, tag_sets_selected) {
+  LogTagType tags[LogTag::MaxTags] = { PREFIX_LOG_TAG(logging), PREFIX_LOG_TAG(test), PREFIX_LOG_TAG(_NO_TAG) };
+  LogSelection selection(tags, false, LogLevel::Trace);
+
+  EXPECT_EQ(1u, selection.tag_sets_selected()) << "there should be a single (it's not a wildcard selection) "
+                                                  "tag set selected by this (in gtest libjvm)";
+
+  EXPECT_EQ(LogTagSet::ntagsets(), LogSelection::parse("all").tag_sets_selected()) << "all should select every tag set";
+  EXPECT_EQ(0u, LogSelection::parse(NON_EXISTING_TAG_SET).tag_sets_selected()) <<
+      "(assuming the tag set doesn't exist) the selection shouldn't select any tag sets";
+}
+
+static const char* valid_expression[] = {
+  "all", "gc", "gc+logging", "logging+gc", "logging+gc*", "gc=trace",
+  "logging+gc=trace", "logging*", "logging*=info", "gc+logging*=error"
+};
+
+TEST(LogSelection, parse) {
+  LogTagType tags[LogTag::MaxTags] = { PREFIX_LOG_TAG(logging), PREFIX_LOG_TAG(test), PREFIX_LOG_TAG(_NO_TAG) };
+  LogSelection selection(tags, true, LogLevel::Off);
+  LogSelection parsed = LogSelection::parse("logging+test*=off");
+  EXPECT_EQ(selection, parsed) << "parsed selection not equal to programmatically constructed";
+
+  // Verify valid expressions parse without problems
+  for (size_t i = 0; i < ARRAY_SIZE(valid_expression); i++) {
+    EXPECT_NE(LogSelection::Invalid, LogSelection::parse(valid_expression[i])) <<
+        "Valid expression '" << valid_expression[i] << "' did not parse";
+  }
+
+  // Test 'all' with each level
+  for (LogLevelType level = LogLevel::First; level <= LogLevel::Last; level = static_cast<LogLevelType>(level + 1)) {
+    char buf[64];
+    int ret = jio_snprintf(buf, sizeof(buf), "all=%s", LogLevel::name(level));
+    ASSERT_NE(-1, ret);
+
+    LogSelection sel = LogSelection::parse(buf);
+    EXPECT_EQ(LogTagSet::ntagsets(), sel.tag_sets_selected()) << "'all' should select all tag sets";
+    EXPECT_EQ(level, sel.level());
+  }
+
+  // Test with 5 tags
+  LogTagType expected_tags[] = { PREFIX_LOG_TAG(logging), PREFIX_LOG_TAG(test), PREFIX_LOG_TAG(start),
+      PREFIX_LOG_TAG(exit), PREFIX_LOG_TAG(safepoint) };
+  LogSelection expected(expected_tags, false, LogLevel::Debug);
+  LogSelection five_tag_selection = LogSelection::parse("logging+test+start+exit+safepoint=debug");
+  EXPECT_EQ(5u, five_tag_selection.ntags()) << "parsed wrong number of tags";
+  EXPECT_EQ(expected, five_tag_selection);
+  EXPECT_EQ(LogLevel::Debug, five_tag_selection.level());
+
+  // Test implicit level
+  selection = LogSelection::parse("logging");
+  EXPECT_EQ(LogLevel::Unspecified, selection.level()) << "parsed implicit level incorrectly";
+  EXPECT_EQ(1u, selection.ntags());
+}
+
+TEST(LogSelection, parse_invalid) {
+
+  // Attempt to parse an expression with too many tags
+  EXPECT_EQ(LogSelection::Invalid, LogSelection::parse(NON_EXISTING_TAG_SET "+gc"));
+
+  // Construct a bunch of invalid expressions and verify that they don't parse
+  for (size_t i = 0; i < ARRAY_SIZE(valid_expression); i++) {
+    char buf[256];
+    for (size_t j = 0; j < ARRAY_SIZE(invalid_selection_substr); j++) {
+      // Prefix with invalid substr
+      jio_snprintf(buf, sizeof(buf), "%s%s", invalid_selection_substr[j], valid_expression[i]);
+      EXPECT_EQ(LogSelection::Invalid, LogSelection::parse(buf)) << "'" << buf << "'" << " considered legal";
+
+      // Suffix with invalid substr
+      jio_snprintf(buf, sizeof(buf), "%s%s", valid_expression[i], invalid_selection_substr[j]);
+      EXPECT_EQ(LogSelection::Invalid, LogSelection::parse(buf)) << "'" << buf << "'" << " considered legal";
+
+      // Use only the invalid substr
+      EXPECT_EQ(LogSelection::Invalid, LogSelection::parse(invalid_selection_substr[j])) <<
+          "'" << invalid_selection_substr[j] << "'" << " considered legal";
+    }
+
+    // Suffix/prefix with some unique invalid prefixes/suffixes
+    jio_snprintf(buf, sizeof(buf), "*%s", valid_expression[i]);
+    EXPECT_EQ(LogSelection::Invalid, LogSelection::parse(buf)) << "'" << buf << "'" << " considered legal";
+
+    jio_snprintf(buf, sizeof(buf), "logging*%s", valid_expression[i]);
+    EXPECT_EQ(LogSelection::Invalid, LogSelection::parse(buf)) << "'" << buf << "'" << " considered legal";
+  }
+}
+
+TEST(LogSelection, equals) {
+  LogTagType tags[LogTag::MaxTags] = { PREFIX_LOG_TAG(logging), PREFIX_LOG_TAG(test), PREFIX_LOG_TAG(_NO_TAG) };
+  LogSelection selection(tags, true, LogLevel::Info);
+  LogSelection copy(tags, true, LogLevel::Info);
+  EXPECT_EQ(selection, selection);
+  EXPECT_EQ(selection, copy);
+
+  tags[0] = PREFIX_LOG_TAG(gc);
+  LogSelection other_tags(tags, true, LogLevel::Info);
+  EXPECT_NE(selection, other_tags);
+
+  tags[0] = PREFIX_LOG_TAG(test);
+  tags[1] = PREFIX_LOG_TAG(logging);
+  LogSelection reversed(tags, true, LogLevel::Info);
+  EXPECT_NE(selection, reversed);
+
+  LogSelection no_wildcard(tags, false, LogLevel::Info);
+  EXPECT_NE(selection, no_wildcard);
+
+  LogSelection different_level(tags, true, LogLevel::Warning);
+  EXPECT_NE(selection, different_level);
+
+  tags[2] = PREFIX_LOG_TAG(gc);
+  tags[3] = PREFIX_LOG_TAG(_NO_TAG);
+  LogSelection more_tags(tags, true, LogLevel::Info);
+  EXPECT_NE(selection, more_tags);
+
+  tags[1] = PREFIX_LOG_TAG(_NO_TAG);
+  LogSelection fewer_tags(tags, true, LogLevel::Info);
+  EXPECT_NE(selection, fewer_tags);
+}
+
+TEST(LogSelection, describe_tags) {
+  char buf[256];
+  LogTagType tags[LogTag::MaxTags] = { PREFIX_LOG_TAG(logging), PREFIX_LOG_TAG(test), PREFIX_LOG_TAG(_NO_TAG) };
+  LogSelection selection(tags, true, LogLevel::Off);
+  selection.describe_tags(buf, sizeof(buf));
+  EXPECT_STREQ("logging+test*", buf);
+}
+
+TEST(LogSelection, describe) {
+  char buf[256];
+  LogTagType tags[LogTag::MaxTags] = { PREFIX_LOG_TAG(logging), PREFIX_LOG_TAG(test), PREFIX_LOG_TAG(_NO_TAG) };
+  LogSelection selection(tags, true, LogLevel::Off);
+  selection.describe(buf, sizeof(buf));
+  EXPECT_STREQ("logging+test*=off", buf);
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/logging/test_logSelectionList.cpp	Mon Feb 19 09:46:10 2018 +0100
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "precompiled.hpp"
+#include "jvm.h"
+#include "logging/logLevel.hpp"
+#include "logging/logSelectionList.hpp"
+#include "logging/logTagSet.hpp"
+#include "utilities/globalDefinitions.hpp"
+#include "logTestUtils.inline.hpp"
+#include "unittest.hpp"
+
+TEST(LogSelectionList, combination_limit) {
+  size_t max_combinations = LogSelectionList::MaxSelections;
+  EXPECT_GT(max_combinations, LogTagSet::ntagsets())
+      << "Combination limit not sufficient for configuring all available tag sets";
+}
+
+TEST(LogSelectionList, parse) {
+  char buf[256];
+  const char* valid_expression[] = {
+    "logging=off,all", "gc,logging", "logging+gc", "logging+gc,gc", "gc=trace,logging=info",
+    "logging+gc=trace,gc+logging=warning,logging", "gc,all=info"
+  };
+
+  // Verify valid expressions parse without problems
+  for (size_t i = 0; i < ARRAY_SIZE(valid_expression); i++) {
+    LogSelectionList expr;
+    EXPECT_TRUE(expr.parse(valid_expression[i])) << "Valid expression '" << valid_expression[i] << "' did not parse";
+  }
+
+  // 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_selection_substr); j++) {
+      // Prefix with invalid substr
+      LogSelectionList expr;
+      jio_snprintf(buf, sizeof(buf), "%s%s", invalid_selection_substr[j], valid_expression[i]);
+      EXPECT_FALSE(expr.parse(buf)) << "'" << buf << "'" << " considered legal";
+
+      // Suffix with invalid substr
+      LogSelectionList expr1;
+      jio_snprintf(buf, sizeof(buf), "%s%s", valid_expression[i], invalid_selection_substr[j]);
+      EXPECT_FALSE(expr1.parse(buf)) << "'" << buf << "'" << " considered legal";
+
+      // Use only the invalid substr
+      LogSelectionList expr2;
+      EXPECT_FALSE(expr2.parse(invalid_selection_substr[j])) << "'" << invalid_selection_substr[j] << "'" << " considered legal";
+    }
+
+    // Suffix/prefix with some unique invalid prefixes/suffixes
+    LogSelectionList expr;
+    jio_snprintf(buf, sizeof(buf), "*%s", valid_expression[i]);
+    EXPECT_FALSE(expr.parse(buf)) << "'" << buf << "'" << " considered legal";
+
+    LogSelectionList 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(LogSelectionList, level_for_empty) {
+  LogSelectionList 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 an expression that has overlap (last subexpression should be used)
+TEST(LogSelectionList, level_for_overlap) {
+  LogSelectionList 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(LogSelectionList, level_for_disjoint) {
+  LogSelectionList 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(LogSelectionList, level_for_override) {
+  LogSelectionList 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(LogSelectionList, level_for_mixed) {
+  LogSelectionList 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()));
+}
--- a/test/hotspot/gtest/logging/test_logTagLevelExpression.cpp	Mon Feb 19 09:16:04 2018 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
-/*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-#include "precompiled.hpp"
-#include "jvm.h"
-#include "logging/logLevel.hpp"
-#include "logging/logTagLevelExpression.hpp"
-#include "logging/logTagSet.hpp"
-#include "unittest.hpp"
-#include "utilities/globalDefinitions.hpp"
-
-TEST(LogTagLevelExpression, combination_limit) {
-  size_t max_combinations = LogTagLevelExpression::MaxCombinations;
-  EXPECT_GT(max_combinations, LogTagSet::ntagsets())
-      << "Combination limit not sufficient for configuring all available tag sets";
-}
-
-TEST(LogTagLevelExpression, parse) {
-  char buf[256];
-  const char* invalid_substr[] = {
-    "=", "+", " ", "+=", "+=*", "*+", " +", "**", "++", ".", ",", ",," ",+",
-    " *", "all+", "all*", "+all", "+all=Warning", "==Info", "=InfoWarning",
-    "BadTag+", "logging++", "logging*+", ",=", "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()));
-}