8196783: Refactor LogTagLevelExpression into separate classes

Reviewed-by: rehn, pliden
This commit is contained in:
Marcus Larsson 2018-02-19 09:46:10 +01:00
parent 8cf755c120
commit 29dd30e010
11 changed files with 693 additions and 388 deletions

@ -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 @@ void LogConfiguration::delete_output(size_t idx) {
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 @@ void LogConfiguration::configure_output(size_t idx, const LogTagLevelExpression&
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::disable_logging() {
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 @@ void LogConfiguration::configure_stdout(LogLevelType level, int exact_match, ...
"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_command_line_arguments(const char* opts) {
}
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 @@ bool LogConfiguration::parse_log_arguments(const char* outputstr,
outputstr = "stdout";
}
LogTagLevelExpression expr;
if (!expr.parse(what, errstream)) {
LogSelectionList selections;
if (!selections.parse(selectionstr, errstream)) {
return false;
}
@ -433,9 +430,9 @@ bool LogConfiguration::parse_log_arguments(const char* outputstr,
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;
}

@ -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 @@ class LogConfiguration : public AllStatic {
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();

@ -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;
}

@ -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

@ -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;
}

@ -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

@ -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;
}

@ -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

@ -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;
}

@ -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

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
* 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
@ -24,78 +24,63 @@
#include "precompiled.hpp"
#include "jvm.h"
#include "logging/logLevel.hpp"
#include "logging/logTagLevelExpression.hpp"
#include "logging/logSelectionList.hpp"
#include "logging/logTagSet.hpp"
#include "unittest.hpp"
#include "utilities/globalDefinitions.hpp"
#include "logTestUtils.inline.hpp"
#include "unittest.hpp"
TEST(LogTagLevelExpression, combination_limit) {
size_t max_combinations = LogTagLevelExpression::MaxCombinations;
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(LogTagLevelExpression, parse) {
TEST(LogSelectionList, 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"
"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++) {
LogTagLevelExpression expr;
LogSelectionList 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++) {
for (size_t j = 0; j < ARRAY_SIZE(invalid_selection_substr); j++) {
// Prefix with invalid substr
LogTagLevelExpression expr;
jio_snprintf(buf, sizeof(buf), "%s%s", invalid_substr[j], valid_expression[i]);
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
LogTagLevelExpression expr1;
jio_snprintf(buf, sizeof(buf), "%s%s", valid_expression[i], invalid_substr[j]);
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
LogTagLevelExpression expr2;
EXPECT_FALSE(expr2.parse(invalid_substr[j])) << "'" << invalid_substr[j] << "'" << " considered legal";
LogSelectionList expr2;
EXPECT_FALSE(expr2.parse(invalid_selection_substr[j])) << "'" << invalid_selection_substr[j] << "'" << " considered legal";
}
// Suffix/prefix with some unique invalid prefixes/suffixes
LogTagLevelExpression expr;
LogSelectionList expr;
jio_snprintf(buf, sizeof(buf), "*%s", valid_expression[i]);
EXPECT_FALSE(expr.parse(buf)) << "'" << buf << "'" << " considered legal";
LogTagLevelExpression expr1;
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(LogTagLevelExpression, level_for_empty) {
LogTagLevelExpression emptyexpr;
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()) {
@ -103,38 +88,9 @@ TEST(LogTagLevelExpression, level_for_empty) {
}
}
// 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;
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()) {
@ -154,8 +110,8 @@ TEST(LogTagLevelExpression, level_for_overlap) {
}
// Test level_for() with an expression containing two independent subexpressions
TEST(LogTagLevelExpression, level_for_disjoint) {
LogTagLevelExpression reducedexpr;
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()));
@ -166,8 +122,8 @@ TEST(LogTagLevelExpression, level_for_disjoint) {
}
// Test level_for() with an expression that is completely overridden in the last part of the expression
TEST(LogTagLevelExpression, level_for_override) {
LogTagLevelExpression overrideexpr;
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()));
@ -177,8 +133,8 @@ TEST(LogTagLevelExpression, level_for_override) {
}
// Test level_for() with a mixed expression with a bit of everything
TEST(LogTagLevelExpression, level_for_mixed) {
LogTagLevelExpression mixedexpr;
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()));