8046148: JEP 158: Unified JVM Logging

Reviewed-by: coleenp, sla
This commit is contained in:
Marcus Larsson 2015-09-24 12:36:04 +02:00
parent 0835a6e311
commit 3c2211a492
39 changed files with 2842 additions and 6 deletions

@ -163,6 +163,7 @@ VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc/serial
VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc/cms
VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/gc/g1
VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/asm
VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/logging
VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/memory
VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/oops
VM_PATH=$(VM_PATH);$(WorkSpace)/src/share/vm/prims
@ -250,6 +251,9 @@ bytecodeInterpreterWithChecks.obj: ..\generated\jvmtifiles\bytecodeInterpreterWi
{$(COMMONSRC)\share\vm\asm}.cpp.obj::
$(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
{$(COMMONSRC)\share\vm\logging}.cpp.obj::
$(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
{$(COMMONSRC)\share\vm\memory}.cpp.obj::
$(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
@ -330,6 +334,9 @@ bytecodeInterpreterWithChecks.obj: ..\generated\jvmtifiles\bytecodeInterpreterWi
{$(ALTSRC)\share\vm\asm}.cpp.obj::
$(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
{$(ALTSRC)\share\vm\logging}.cpp.obj::
$(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<
{$(ALTSRC)\share\vm\memory}.cpp.obj::
$(CXX) $(CXX_FLAGS) $(CXX_USE_PCH) /c $<

@ -7,6 +7,7 @@
-Xbootclasspath/p:<directories and zip/jar files separated by ;>
prepend in front of bootstrap class path
-Xnoclassgc disable class garbage collection
-Xlog:<opts> control JVM logging, use -Xlog:help for details
-Xloggc:<file> log GC status to a file with time stamps
-Xbatch disable background compilation
-Xms<size> set initial Java heap size

@ -0,0 +1,139 @@
/*
* 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_LOG_HPP
#define SHARE_VM_LOGGING_LOG_HPP
#include "logging/logLevel.hpp"
#include "logging/logPrefix.hpp"
#include "logging/logTagSet.hpp"
#include "logging/logTag.hpp"
#include "memory/allocation.hpp"
#include "utilities/debug.hpp"
#include "utilities/ostream.hpp"
//
// Logging macros
//
// Usage:
// log_<level>(<comma separated log tags>)(<printf-style log arguments>);
// e.g.
// log_debug(logging)("message %d", i);
//
// Note that these macros will not evaluate the arguments unless the logging is enabled.
//
#define log_error(...) (!log_is_enabled(Error, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Error>
#define log_warning(...) (!log_is_enabled(Warning, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Warning>
#define log_info(...) (!log_is_enabled(Info, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Info>
#define log_debug(...) (!log_is_enabled(Debug, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Debug>
#define log_trace(...) (!log_is_enabled(Trace, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Trace>
#ifndef PRODUCT
#define log_develop(...) (!log_is_enabled(Develop, __VA_ARGS__)) ? (void)0 : Log<LOG_TAGS(__VA_ARGS__)>::write<LogLevel::Develop>
#else
#define DUMMY_ARGUMENT_CONSUMER(...)
#define log_develop(...) DUMMY_ARGUMENT_CONSUMER
#endif
// Convenience macro to test if the logging is enabled on the specified level for given tags.
#define log_is_enabled(level, ...) (Log<LOG_TAGS(__VA_ARGS__)>::is_level(LogLevel::level))
//
// Log class for more advanced logging scenarios.
// Has printf-style member functions for each log level (trace(), debug(), etc).
//
// Also has outputStream compatible API for the different log-levels.
// The streams are resource allocated when requested and are accessed through
// calls to <level>_stream() functions (trace_stream(), debug_stream(), etc).
//
// Example usage:
// LogHandle(logging) log;
// if (log.is_debug()) {
// ...
// log.debug("result = %d", result).trace(" tracing info");
// obj->print_on(log.debug_stream());
// }
//
#define LogHandle(...) Log<LOG_TAGS(__VA_ARGS__)>
template <LogTagType T0, LogTagType T1 = LogTag::__NO_TAG, LogTagType T2 = LogTag::__NO_TAG, LogTagType T3 = LogTag::__NO_TAG,
LogTagType T4 = LogTag::__NO_TAG, LogTagType GuardTag = LogTag::__NO_TAG>
class Log VALUE_OBJ_CLASS_SPEC {
private:
static const size_t LogBufferSize = 512;
public:
// Make sure no more than the maximum number of tags have been given.
// The GuardTag allows this to be detected if/when it happens. If the GuardTag
// is not __NO_TAG, the number of tags given exceeds the maximum allowed.
STATIC_ASSERT(GuardTag == LogTag::__NO_TAG); // Number of logging tags exceeds maximum supported!
static bool is_level(LogLevelType level) {
return LogTagSetMapping<T0, T1, T2, T3, T4>::tagset().is_level(level);
}
template <LogLevelType Level>
ATTRIBUTE_PRINTF(1, 2)
static void write(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vwrite<Level>(fmt, args);
va_end(args);
};
template <LogLevelType Level>
ATTRIBUTE_PRINTF(1, 0)
static void vwrite(const char* fmt, va_list args) {
char buf[LogBufferSize];
size_t prefix_len = LogPrefix<T0, T1, T2, T3, T4>::prefix(buf, sizeof(buf));
int ret = vsnprintf(buf + prefix_len, sizeof(buf) - prefix_len, fmt, args);
assert(ret >= 0 && (size_t)ret < sizeof(buf), "Log message too long");
puts<Level>(buf);
}
template <LogLevelType Level>
static void puts(const char* string) {
LogTagSetMapping<T0, T1, T2, T3, T4>::tagset().log(Level, string);
}
#define LOG_LEVEL(level, name) ATTRIBUTE_PRINTF(2, 0) \
Log& v##name(const char* fmt, va_list args) { \
vwrite<LogLevel::level>(fmt, args); \
return *this; \
} \
Log& name(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3) { \
va_list args; \
va_start(args, fmt); \
vwrite<LogLevel::level>(fmt, args); \
va_end(args); \
return *this; \
} \
static bool is_##name() { \
return is_level(LogLevel::level); \
} \
static outputStream* name##_stream() { \
return new logStream(write<LogLevel::level>); \
}
LOG_LEVEL_LIST
#undef LOG_LEVEL
};
#endif // SHARE_VM_LOGGING_LOG_HPP

@ -0,0 +1,353 @@
/*
* 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/log.hpp"
#include "logging/logConfiguration.hpp"
#include "logging/logDecorations.hpp"
#include "logging/logDecorators.hpp"
#include "logging/logDiagnosticCommand.hpp"
#include "logging/logFileOutput.hpp"
#include "logging/logOutput.hpp"
#include "logging/logTagLevelExpression.hpp"
#include "logging/logTagSet.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/os.inline.hpp"
#include "utilities/globalDefinitions.hpp"
LogOutput** LogConfiguration::_outputs = NULL;
size_t LogConfiguration::_n_outputs = 0;
void LogConfiguration::post_initialize() {
assert(LogConfiguration_lock != NULL, "Lock must be initialized before post-initialization");
LogDiagnosticCommand::registerCommand();
LogHandle(logging) log;
log.info("Log configuration fully initialized.");
if (log.is_trace()) {
ResourceMark rm;
MutexLocker ml(LogConfiguration_lock);
describe(log.trace_stream());
}
}
void LogConfiguration::initialize(jlong vm_start_time) {
LogFileOutput::set_file_name_parameters(vm_start_time);
LogDecorations::set_vm_start_time_millis(vm_start_time);
assert(_outputs == NULL, "Should not initialize _outputs before this function, initialize called twice?");
_outputs = NEW_C_HEAP_ARRAY(LogOutput*, 2, mtLogging);
_outputs[0] = LogOutput::Stdout;
_outputs[1] = LogOutput::Stderr;
_n_outputs = 2;
}
void LogConfiguration::finalize() {
for (size_t i = 2; i < _n_outputs; i++) {
delete _outputs[i];
}
FREE_C_HEAP_ARRAY(LogOutput*, _outputs);
}
size_t LogConfiguration::find_output(const char* name) {
for (size_t i = 0; i < _n_outputs; i++) {
if (strcmp(_outputs[i]->name(), name) == 0) {
return i;
}
}
return SIZE_MAX;
}
LogOutput* LogConfiguration::new_output(char* name, const char* options) {
const char* type;
char* equals_pos = strchr(name, '=');
if (equals_pos == NULL) {
type = "file";
} else {
*equals_pos = '\0';
type = name;
name = equals_pos + 1;
}
LogOutput* output;
if (strcmp(type, "file") == 0) {
output = new LogFileOutput(name);
} else {
// unsupported log output type
return NULL;
}
bool success = output->initialize(options);
if (!success) {
delete output;
return NULL;
}
return output;
}
size_t LogConfiguration::add_output(LogOutput* output) {
size_t idx = _n_outputs++;
_outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
_outputs[idx] = output;
return idx;
}
void LogConfiguration::delete_output(size_t idx) {
assert(idx > 1 && idx < _n_outputs,
err_msg("idx must be in range 1 < idx < _n_outputs, but idx = " SIZE_FORMAT
" and _n_outputs = " SIZE_FORMAT, idx, _n_outputs));
LogOutput* output = _outputs[idx];
// Swap places with the last output and shrink the array
_outputs[idx] = _outputs[--_n_outputs];
_outputs = REALLOC_C_HEAP_ARRAY(LogOutput*, _outputs, _n_outputs, mtLogging);
delete output;
}
void LogConfiguration::configure_output(size_t idx, const LogTagLevelExpression& tag_level_expression, const LogDecorators& decorators) {
assert(idx < _n_outputs, err_msg("Invalid index, idx = " SIZE_FORMAT " and _n_outputs = " SIZE_FORMAT, idx, _n_outputs));
LogOutput* output = _outputs[idx];
output->set_decorators(decorators);
output->set_config_string(tag_level_expression.to_string());
bool enabled = false;
for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
LogLevelType level = tag_level_expression.level_for(*ts);
if (level != LogLevel::Off) {
enabled = true;
}
ts->update_decorators(decorators);
ts->set_output_level(output, level);
}
// If the output is not used by any tagset it should be removed, unless it is stdout/stderr.
if (!enabled && idx > 1) {
delete_output(idx);
}
}
void LogConfiguration::disable_output(size_t idx) {
LogOutput* out = _outputs[idx];
LogDecorators empty_decorators;
empty_decorators.clear();
// Remove the output from all tagsets.
for (LogTagSet* ts = LogTagSet::first(); ts != NULL; ts = ts->next()) {
ts->set_output_level(out, LogLevel::Off);
ts->update_decorators(empty_decorators);
}
// Delete the output unless stdout/stderr
if (out != LogOutput::Stderr && out != LogOutput::Stdout) {
delete_output(find_output(out->name()));
} else {
out->set_config_string("all=off");
}
}
void LogConfiguration::disable_logging() {
assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
"LogConfiguration lock must be held when calling this function");
for (size_t i = 0; i < _n_outputs; i++) {
disable_output(i);
}
}
bool LogConfiguration::parse_command_line_arguments(const char* opts) {
char* copy = os::strdup_check_oom(opts, mtLogging);
// Split the option string to its colon separated components.
char* what = NULL;
char* output_str = NULL;
char* decorators_str = NULL;
char* output_options = NULL;
what = copy;
char* colon = strchr(what, ':');
if (colon != NULL) {
*colon = '\0';
output_str = colon + 1;
colon = strchr(output_str, ':');
if (colon != NULL) {
*colon = '\0';
decorators_str = colon + 1;
colon = strchr(decorators_str, ':');
if (colon != NULL) {
*colon = '\0';
output_options = colon + 1;
}
}
}
// Parse each argument
char errbuf[512];
stringStream ss(errbuf, sizeof(errbuf));
bool success = parse_log_arguments(output_str, what, decorators_str, output_options, &ss);
if (!success) {
errbuf[strlen(errbuf) - 1] = '\0'; // Strip trailing newline.
log_error(logging)("%s", errbuf);
}
os::free(copy);
return success;
}
bool LogConfiguration::parse_log_arguments(const char* outputstr,
const char* what,
const char* decoratorstr,
const char* output_options,
outputStream* errstream) {
assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
"LogConfiguration lock must be held when calling this function");
if (outputstr == NULL || strlen(outputstr) == 0) {
outputstr = "stdout";
}
size_t idx;
if (outputstr[0] == '#') {
int ret = sscanf(outputstr+1, SIZE_FORMAT, &idx);
if (ret != 1 || idx >= _n_outputs) {
errstream->print_cr("Invalid output index '%s'", outputstr);
return false;
}
} else {
idx = find_output(outputstr);
if (idx == SIZE_MAX) {
char* tmp = os::strdup_check_oom(outputstr, mtLogging);
LogOutput* output = new_output(tmp, output_options);
os::free(tmp);
if (output == NULL) {
errstream->print("Unable to add output '%s'", outputstr);
if (output_options != NULL && strlen(output_options) > 0) {
errstream->print(" with options '%s'", output_options);
}
errstream->cr();
return false;
}
idx = add_output(output);
} else if (output_options != NULL && strlen(output_options) > 0) {
errstream->print_cr("Output options for existing outputs are ignored.");
}
}
LogTagLevelExpression expr;
if (!expr.parse(what, errstream)) {
return false;
}
LogDecorators decorators;
if (!decorators.parse(decoratorstr, errstream)) {
return false;
}
configure_output(idx, expr, decorators);
return true;
}
void LogConfiguration::describe(outputStream* out) {
assert(LogConfiguration_lock == NULL || LogConfiguration_lock->owned_by_self(),
"LogConfiguration lock must be held when calling this function");
out->print("Available log levels:");
for (size_t i = 0; i < LogLevel::Count; i++) {
out->print("%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
}
out->cr();
out->print("Available log decorators:");
for (size_t i = 0; i < LogDecorators::Count; i++) {
LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
out->print("%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
}
out->cr();
out->print("Available log tags:");
for (size_t i = 1; i < LogTag::Count; i++) {
out->print("%s %s", (i == 1 ? "" : ","), LogTag::name(static_cast<LogTagType>(i)));
}
out->cr();
out->print_cr("Log output configuration:");
for (size_t i = 0; i < _n_outputs; i++) {
out->print("#" SIZE_FORMAT ": %s %s ", i, _outputs[i]->name(), _outputs[i]->config_string());
for (size_t d = 0; d < LogDecorators::Count; d++) {
LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(d);
if (_outputs[i]->decorators().is_decorator(decorator)) {
out->print("%s,", LogDecorators::name(decorator));
}
}
out->cr();
}
}
void LogConfiguration::print_command_line_help(FILE* out) {
jio_fprintf(out, "-Xlog Usage: -Xlog[:[what][:[output][:[decorators][:output-options]]]]\n"
"\t where 'what' is a combination of tags and levels on the form tag1[+tag2...][*][=level][,...]\n"
"\t Unless wildcard (*) is specified, only log messages tagged with exactly the tags specified will be matched.\n\n");
jio_fprintf(out, "Available log levels:\n");
for (size_t i = 0; i < LogLevel::Count; i++) {
jio_fprintf(out, "%s %s", (i == 0 ? "" : ","), LogLevel::name(static_cast<LogLevelType>(i)));
}
jio_fprintf(out, "\n\nAvailable log decorators: \n");
for (size_t i = 0; i < LogDecorators::Count; i++) {
LogDecorators::Decorator d = static_cast<LogDecorators::Decorator>(i);
jio_fprintf(out, "%s %s (%s)", (i == 0 ? "" : ","), LogDecorators::name(d), LogDecorators::abbreviation(d));
}
jio_fprintf(out, "\n Decorators can also be specified as 'none' for no decoration.\n\n");
jio_fprintf(out, "Available log tags:\n");
for (size_t i = 1; i < LogTag::Count; i++) {
jio_fprintf(out, "%s %s", (i == 1 ? "" : ","), LogTag::name(static_cast<LogTagType>(i)));
}
jio_fprintf(out, "\n Specifying 'all' instead of a tag combination matches all tag combinations.\n\n");
jio_fprintf(out, "Available log outputs:\n"
" stdout, stderr, file=<filename>\n"
" Specifying %%p and/or %%t in the filename will expand to the JVM's PID and startup timestamp, respectively.\n\n"
"Some examples:\n"
" -Xlog\n"
"\t Log all messages using 'info' level to stdout with 'uptime', 'levels' and 'tags' decorations.\n"
"\t (Equivalent to -Xlog:all=info:stdout:uptime,levels,tags).\n\n"
" -Xlog:gc\n"
"\t Log messages tagged with 'gc' tag using 'info' level to stdout, with default decorations.\n\n"
" -Xlog:gc=debug:file=gc.txt:none\n"
"\t Log messages tagged with 'gc' tag using 'debug' level to file 'gc.txt' with no decorations.\n\n"
" -Xlog:gc=trace:file=gctrace.txt:uptimemillis,pids:filecount=5,filesize=1024\n"
"\t Log messages tagged with 'gc' tag using 'trace' level to a rotating fileset of 5 files of size 1MB,\n"
"\t using the base name 'gctrace.txt', with 'uptimemillis' and 'pid' decorations.\n\n"
" -Xlog:gc::uptime,tid\n"
"\t Log messages tagged with 'gc' tag using 'info' level to output 'stdout', using 'uptime' and 'tid' decorations.\n\n"
" -Xlog:gc*=info,rt*=off\n"
"\t Log messages tagged with at least 'gc' using 'info' level, but turn off logging of messages tagged with 'rt'.\n"
"\t (Messages tagged with both 'gc' and 'rt' will not be logged.)\n\n"
" -Xlog:disable -Xlog:rt=trace:rttrace.txt\n"
"\t Turn off all logging, including warnings and errors,\n"
"\t and then enable messages tagged with 'rt' using 'trace' level to file 'rttrace.txt'.\n");
}

@ -0,0 +1,90 @@
/*
* 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_LOGCONFIGURATION_HPP
#define SHARE_VM_LOGGING_LOGCONFIGURATION_HPP
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
class LogOutput;
class LogDecorators;
class LogTagLevelExpression;
// 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
// kept implicitly in the LogTagSets and their LogOutputLists. During configuration the tagsets
// are iterated over and updated accordingly.
class LogConfiguration : public AllStatic {
private:
static LogOutput** _outputs;
static size_t _n_outputs;
// Create a new output. Returns NULL if failed.
static LogOutput* new_output(char* name, const char* options = NULL);
// Add an output to the list of configured outputs. Returns the assigned index.
static size_t add_output(LogOutput* out);
// Delete a configured output. The stderr/stdout outputs can not be removed.
// Output should be completely disabled before it is deleted.
static void delete_output(size_t idx);
// Disable all logging to the specified output and then delete it (unless it is stdout/stderr).
static void disable_output(size_t idx);
// Get output index by name. Returns SIZE_MAX if output not found.
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);
public:
// Initialization and finalization of log configuration, to be run at vm startup and shutdown respectively.
static void initialize(jlong vm_start_time);
static void finalize();
// Perform necessary post-initialization after VM startup. Enables reconfiguration of logging.
static void post_initialize();
// Disable all logging, equivalent to -Xlog:disable.
static void disable_logging();
// Parse command line configuration. Parameter 'opts' is the string immediately following the -Xlog: argument ("gc" for -Xlog:gc).
static bool parse_command_line_arguments(const char* opts = "all");
// Parse separated configuration arguments (from JCmd/MBean and command line).
static bool parse_log_arguments(const char* outputstr,
const char* what,
const char* decoratorstr,
const char* output_options,
outputStream* errstream);
// Prints log configuration to outputStream, used by JCmd/MBean.
static void describe(outputStream* out);
// Prints usage help for command line log configuration.
static void print_command_line_help(FILE* out);
};
#endif // SHARE_VM_LOGGING_LOGCONFIGURATION_HPP

@ -0,0 +1,111 @@
/*
* 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/logConfiguration.hpp"
#include "logging/logDecorations.hpp"
#include "runtime/os.inline.hpp"
#include "runtime/thread.inline.hpp"
#include "services/management.hpp"
jlong LogDecorations::_vm_start_time_millis = 0;
LogDecorations::LogDecorations(LogLevelType level, const LogTagSet &tagset, const LogDecorators &decorators)
: _level(level), _tagset(tagset), _millis(-1) {
create_decorations(decorators);
}
void LogDecorations::create_decorations(const LogDecorators &decorators) {
char* position = _decorations_buffer;
#define DECORATOR(full_name, abbr) \
if (decorators.is_decorator(LogDecorators::full_name##_decorator)) { \
_decoration_offset[LogDecorators::full_name##_decorator] = position; \
position = create_##full_name##_decoration(position) + 1; \
}
DECORATOR_LIST
#undef DECORATOR
}
jlong LogDecorations::java_millis() {
if (_millis < 0) {
_millis = os::javaTimeMillis();
}
return _millis;
}
#define ASSERT_AND_RETURN(written, pos) \
assert(written >= 0, "Decorations buffer overflow"); \
return pos + written;
char* LogDecorations::create_time_decoration(char* pos) {
char* buf = os::iso8601_time(pos, 29);
int written = buf == NULL ? -1 : 29;
ASSERT_AND_RETURN(written, pos)
}
char * LogDecorations::create_uptime_decoration(char* pos) {
int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), "%.3fs", os::elapsedTime());
ASSERT_AND_RETURN(written, pos)
}
char * LogDecorations::create_timemillis_decoration(char* pos) {
int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), INT64_FORMAT "ms", java_millis());
ASSERT_AND_RETURN(written, pos)
}
char * LogDecorations::create_uptimemillis_decoration(char* pos) {
int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer),
INT64_FORMAT "ms", java_millis() - _vm_start_time_millis);
ASSERT_AND_RETURN(written, pos)
}
char * LogDecorations::create_timenanos_decoration(char* pos) {
int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), INT64_FORMAT "ns", os::javaTimeNanos());
ASSERT_AND_RETURN(written, pos)
}
char * LogDecorations::create_uptimenanos_decoration(char* pos) {
int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), INT64_FORMAT "ns", os::elapsed_counter());
ASSERT_AND_RETURN(written, pos)
}
char * LogDecorations::create_pid_decoration(char* pos) {
int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), "%d", os::current_process_id());
ASSERT_AND_RETURN(written, pos)
}
char * LogDecorations::create_tid_decoration(char* pos) {
int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer),
INTX_FORMAT, Thread::current()->osthread()->thread_id());
ASSERT_AND_RETURN(written, pos)
}
char* LogDecorations::create_level_decoration(char* pos) {
int written = jio_snprintf(pos, DecorationsBufferSize - (pos - _decorations_buffer), "%s", LogLevel::name(_level));
ASSERT_AND_RETURN(written, pos)
}
char* LogDecorations::create_tags_decoration(char* pos) {
int written = _tagset.label(pos, DecorationsBufferSize - (pos - _decorations_buffer));
ASSERT_AND_RETURN(written, pos)
}

@ -0,0 +1,62 @@
/*
* 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_LOGDECORATIONS_HPP
#define SHARE_VM_LOGGING_LOGDECORATIONS_HPP
#include "logging/logDecorators.hpp"
#include "logging/logTagSet.hpp"
#include "memory/allocation.hpp"
// Temporary object containing the necessary data for a log call's decorations (timestamps, etc).
class LogDecorations VALUE_OBJ_CLASS_SPEC {
public:
static const int DecorationsBufferSize = 256;
private:
char _decorations_buffer[DecorationsBufferSize];
char* _decoration_offset[LogDecorators::Count];
LogLevelType _level;
LogTagSet _tagset;
jlong _millis;
static jlong _vm_start_time_millis;
jlong java_millis();
void create_decorations(const LogDecorators& decorators);
#define DECORATOR(name, abbr) char* create_##name##_decoration(char* pos);
DECORATOR_LIST
#undef DECORATOR
public:
LogDecorations(LogLevelType level, const LogTagSet& tagset, const LogDecorators& decorators);
const char* decoration(LogDecorators::Decorator decorator) const {
return _decoration_offset[decorator];
}
static void set_vm_start_time_millis(jlong start_time) {
_vm_start_time_millis = start_time;
}
};
#endif // SHARE_VM_LOGGING_LOGDECORATIONS_HPP

@ -0,0 +1,81 @@
/*
* 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/logDecorators.hpp"
#include "runtime/os.inline.hpp"
const char* LogDecorators::_name[][2] = {
#define DECORATOR(n, a) {#n, #a},
DECORATOR_LIST
#undef DECORATOR
};
LogDecorators::Decorator LogDecorators::from_string(const char* str) {
for (size_t i = 0; i < Count; i++) {
Decorator d = static_cast<Decorator>(i);
if (strcasecmp(str, name(d)) == 0 || strcasecmp(str, abbreviation(d)) == 0) {
return d;
}
}
return Invalid;
}
bool LogDecorators::parse(const char* decorator_args, outputStream* errstream) {
if (decorator_args == NULL || strlen(decorator_args) == 0) {
_decorators = DefaultDecoratorsMask;
return true;
}
if (strcasecmp(decorator_args, "none") == 0 ) {
_decorators = 0;
return true;
}
bool result = true;
uint tmp_decorators = 0;
char* args_copy = os::strdup_check_oom(decorator_args, mtLogging);
char* token = args_copy;
char* comma_pos;
do {
comma_pos = strchr(token, ',');
if (comma_pos != NULL) {
*comma_pos = '\0';
}
Decorator d = from_string(token);
if (d == Invalid) {
if (errstream != NULL) {
errstream->print_cr("Invalid decorator '%s'.", token);
}
result = false;
break;
}
tmp_decorators |= mask(d);
token = comma_pos + 1;
} while (comma_pos != NULL);
os::free(args_copy);
if (result) {
_decorators = tmp_decorators;
}
return result;
}

@ -0,0 +1,105 @@
/*
* 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_LOGDECORATORS_HPP
#define SHARE_VM_LOGGING_LOGDECORATORS_HPP
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
// The list of available decorators:
// time - Current time and date in ISO-8601 format
// uptime - Time since the start of the JVM in seconds and milliseconds (e.g., 6.567s)
// timemillis - The same value as generated by System.currentTimeMillis()
// uptimemillis - Milliseconds since the JVM started
// timenanos - The same value as generated by System.nanoTime()
// uptimenanos - Nanoseconds since the JVM started
// pid - The process identifier
// tid - The thread identifier
// level - The level associated with the log message
// tags - The tag-set associated with the log message
#define DECORATOR_LIST \
DECORATOR(time, t) \
DECORATOR(uptime, u) \
DECORATOR(timemillis, tm) \
DECORATOR(uptimemillis, um) \
DECORATOR(timenanos, tn) \
DECORATOR(uptimenanos, un) \
DECORATOR(pid, p) \
DECORATOR(tid, ti) \
DECORATOR(level, l) \
DECORATOR(tags, tg)
// LogDecorators represents a selection of decorators that should be prepended to
// each log message for a given output. Decorators are always prepended in the order
// declared above. For example, logging with 'uptime, level, tags' decorators results in:
// [0,943s][info ][logging] message.
class LogDecorators VALUE_OBJ_CLASS_SPEC {
public:
enum Decorator {
#define DECORATOR(name, abbr) name##_decorator,
DECORATOR_LIST
#undef DECORATOR
Count,
Invalid
};
private:
uint _decorators;
static const char* _name[][2];
static const uint DefaultDecoratorsMask = (1 << uptime_decorator) | (1 << level_decorator) | (1 << tags_decorator);
static uint mask(LogDecorators::Decorator decorator) {
return 1 << decorator;
}
public:
LogDecorators() : _decorators(DefaultDecoratorsMask) {
};
void clear() {
_decorators = 0;
}
static const char* name(LogDecorators::Decorator decorator) {
return _name[decorator][0];
}
static const char* abbreviation(LogDecorators::Decorator decorator) {
return _name[decorator][1];
}
static LogDecorators::Decorator from_string(const char* str);
void combine_with(const LogDecorators &source) {
_decorators |= source._decorators;
}
bool is_decorator(LogDecorators::Decorator decorator) const {
return (_decorators & mask(decorator)) != 0;
}
bool parse(const char* decorator_args, outputStream* errstream = NULL);
};
#endif // SHARE_VM_LOGGING_LOGDECORATORS_HPP

@ -0,0 +1,94 @@
/*
* 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/logConfiguration.hpp"
#include "logging/logDiagnosticCommand.hpp"
#include "memory/resourceArea.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/globalDefinitions.hpp"
LogDiagnosticCommand::LogDiagnosticCommand(outputStream* output, bool heap_allocated)
: DCmdWithParser(output, heap_allocated),
_output("output", "The name or index (#<index>) of output to configure.", "STRING", false),
_output_options("output_options", "Options for the output.", "STRING", false),
_what("what", "Configures what tags to log.", "STRING", false),
_decorators("decorators", "Configures which decorators to use. Use 'none' or an empty value to remove all.", "STRING", false),
_disable("disable", "Turns off all logging and clears the log configuration.", "BOOLEAN", false),
_list("list", "Lists current log configuration.", "BOOLEAN", false) {
_dcmdparser.add_dcmd_option(&_output);
_dcmdparser.add_dcmd_option(&_output_options);
_dcmdparser.add_dcmd_option(&_what);
_dcmdparser.add_dcmd_option(&_decorators);
_dcmdparser.add_dcmd_option(&_disable);
_dcmdparser.add_dcmd_option(&_list);
}
int LogDiagnosticCommand::num_arguments() {
ResourceMark rm;
LogDiagnosticCommand* dcmd = new LogDiagnosticCommand(NULL, false);
if (dcmd != NULL) {
DCmdMark mark(dcmd);
return dcmd->_dcmdparser.num_arguments();
} else {
return 0;
}
}
void LogDiagnosticCommand::registerCommand() {
uint32_t full_visibility = DCmd_Source_Internal | DCmd_Source_AttachAPI | DCmd_Source_MBean;
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<LogDiagnosticCommand>(full_visibility, true, false));
}
void LogDiagnosticCommand::execute(DCmdSource source, TRAPS) {
bool any_command = false;
if (_disable.has_value()) {
MutexLocker ml(LogConfiguration_lock);
LogConfiguration::disable_logging();
any_command = true;
}
if (_output.has_value() || _what.has_value() || _decorators.has_value()) {
MutexLocker ml(LogConfiguration_lock);
if (!LogConfiguration::parse_log_arguments(_output.value(),
_what.value(),
_decorators.value(),
_output_options.value(),
output())) {
return;
}
any_command = true;
}
if (_list.has_value()) {
MutexLocker ml(LogConfiguration_lock);
LogConfiguration::describe(output());
any_command = true;
}
if (!any_command) {
// If no argument was provided, print usage
print_help(LogDiagnosticCommand::name());
}
}

@ -0,0 +1,68 @@
/*
* 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_LOGDIAGNOSTICCOMMAND_HPP
#define SHARE_VM_LOGGING_LOGDIAGNOSTICCOMMAND_HPP
#include "services/diagnosticCommand.hpp"
// The LogDiagnosticCommand represents the 'VM.log' DCMD
// that allows configuration of the logging at runtime.
// It can be used to view or modify the current log configuration.
// VM.log without additional arguments prints the usage description.
// The 'list' argument will list all available log tags,
// levels, decorators and currently configured log outputs.
// Specifying 'disable' will disable logging completely.
// The remaining arguments are used to set a log output to log everything
// with the specified tags and levels using the given decorators.
class LogDiagnosticCommand : public DCmdWithParser {
protected:
DCmdArgument<char *> _output;
DCmdArgument<char *> _output_options;
DCmdArgument<char *> _what;
DCmdArgument<char *> _decorators;
DCmdArgument<bool> _disable;
DCmdArgument<bool> _list;
public:
LogDiagnosticCommand(outputStream* output, bool heap_allocated);
void execute(DCmdSource source, TRAPS);
static void registerCommand();
static int num_arguments();
static const char* name() {
return "VM.log";
}
static const char* description() {
return "Lists, enables, disables or changes a log output configuration.";
}
// Used by SecurityManager. This DCMD requires ManagementPermission = control.
static const JavaPermission permission() {
JavaPermission p = {"java.lang.management.ManagementPermission", "control", NULL};
return p;
}
};
#endif // SHARE_VM_LOGGING_LOGDIAGNOSTICCOMMAND_HPP

@ -0,0 +1,283 @@
/*
* 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/log.hpp"
#include "logging/logConfiguration.hpp"
#include "logging/logFileOutput.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.inline.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/defaultStream.hpp"
const char* LogFileOutput::FileOpenMode = "a";
const char* LogFileOutput::PidFilenamePlaceholder = "%p";
const char* LogFileOutput::TimestampFilenamePlaceholder = "%t";
const char* LogFileOutput::TimestampFormat = "%Y-%m-%d_%H-%M-%S";
const char* LogFileOutput::FileSizeOptionKey = "filesize";
const char* LogFileOutput::FileCountOptionKey = "filecount";
char LogFileOutput::_pid_str[PidBufferSize];
char LogFileOutput::_vm_start_time_str[StartTimeBufferSize];
LogFileOutput::LogFileOutput(const char* name)
: LogFileStreamOutput(NULL), _name(os::strdup_check_oom(name, mtLogging)),
_file_name(NULL), _archive_name(NULL), _archive_name_len(0), _current_size(0),
_rotate_size(0), _current_file(1), _file_count(0),
_rotation_lock(Mutex::leaf, "LogFileOutput rotation lock", true, Mutex::_safepoint_check_sometimes) {
_file_name = make_file_name(name, _pid_str, _vm_start_time_str);
}
void LogFileOutput::set_file_name_parameters(jlong vm_start_time) {
int res = jio_snprintf(_pid_str, sizeof(_pid_str), "%d", os::current_process_id());
assert(res > 0, "PID buffer too small");
struct tm local_time;
time_t utc_time = vm_start_time / 1000;
os::localtime_pd(&utc_time, &local_time);
res = (int)strftime(_vm_start_time_str, sizeof(_vm_start_time_str), TimestampFormat, &local_time);
assert(res > 0, "VM start time buffer too small.");
}
LogFileOutput::~LogFileOutput() {
if (_stream != NULL) {
if (_archive_name != NULL) {
archive();
}
if (fclose(_stream) != 0) {
jio_fprintf(defaultStream::error_stream(), "Could not close log file '%s' (%s).\n",
_file_name, strerror(errno));
}
}
os::free(_archive_name);
os::free(_file_name);
os::free(const_cast<char*>(_name));
}
size_t LogFileOutput::parse_value(const char* value_str) {
char* end;
unsigned long long value = strtoull(value_str, &end, 10);
if (!isdigit(*value_str) || end != value_str + strlen(value_str) || value >= SIZE_MAX) {
return SIZE_MAX;
}
return value;
}
bool LogFileOutput::configure_rotation(const char* options) {
if (options == NULL || strlen(options) == 0) {
return true;
}
bool success = true;
char* opts = os::strdup_check_oom(options, mtLogging);
char* comma_pos;
char* pos = opts;
do {
comma_pos = strchr(pos, ',');
if (comma_pos != NULL) {
*comma_pos = '\0';
}
char* equals_pos = strchr(pos, '=');
if (equals_pos == NULL) {
success = false;
break;
}
char* key = pos;
char* value_str = equals_pos + 1;
*equals_pos = '\0';
if (strcmp(FileCountOptionKey, key) == 0) {
size_t value = parse_value(value_str);
if (value == SIZE_MAX || value >= UINT_MAX) {
success = false;
break;
}
_file_count = static_cast<uint>(value);
_file_count_max_digits = static_cast<uint>(log10(static_cast<double>(_file_count)) + 1);
_archive_name_len = 2 + strlen(_file_name) + _file_count_max_digits;
_archive_name = NEW_C_HEAP_ARRAY(char, _archive_name_len, mtLogging);
} else if (strcmp(FileSizeOptionKey, key) == 0) {
size_t value = parse_value(value_str);
if (value == SIZE_MAX || value > SIZE_MAX / K) {
success = false;
break;
}
_rotate_size = value * K;
} else {
success = false;
break;
}
pos = comma_pos + 1;
} while (comma_pos != NULL);
os::free(opts);
return success;
}
bool LogFileOutput::initialize(const char* options) {
if (!configure_rotation(options)) {
return false;
}
_stream = fopen(_file_name, FileOpenMode);
if (_stream == NULL) {
log_error(logging)("Could not open log file '%s' (%s).\n", _file_name, strerror(errno));
return false;
}
return true;
}
int LogFileOutput::write(const LogDecorations& decorations, const char* msg) {
if (_stream == NULL) {
// An error has occurred with this output, avoid writing to it.
return 0;
}
int written = LogFileStreamOutput::write(decorations, msg);
_current_size += written;
if (should_rotate()) {
MutexLockerEx ml(&_rotation_lock, true /* no safepoint check */);
if (should_rotate()) {
rotate();
}
}
return written;
}
void LogFileOutput::archive() {
assert(_archive_name != NULL && _archive_name_len > 0, "Rotation must be configured before using this function.");
int ret = jio_snprintf(_archive_name, _archive_name_len, "%s.%0*u",
_file_name, _file_count_max_digits, _current_file);
assert(ret >= 0, "Buffer should always be large enough");
// Attempt to remove possibly existing archived log file before we rename.
// Don't care if it fails, we really only care about the rename that follows.
remove(_archive_name);
// Rename the file from ex hotspot.log to hotspot.log.2
if (rename(_file_name, _archive_name) == -1) {
jio_fprintf(defaultStream::error_stream(), "Could not rename log file '%s' to '%s' (%s).\n",
_file_name, _archive_name, strerror(errno));
}
}
void LogFileOutput::rotate() {
// Archive the current log file
archive();
// Open the active log file using the same stream as before
_stream = freopen(_file_name, FileOpenMode, _stream);
if (_stream == NULL) {
jio_fprintf(defaultStream::error_stream(), "Could not reopen file '%s' during log rotation (%s).\n",
_file_name, strerror(errno));
return;
}
// Reset accumulated size, increase current file counter, and check for file count wrap-around.
_current_size = 0;
_current_file = (_current_file >= _file_count ? 1 : _current_file + 1);
}
char* LogFileOutput::make_file_name(const char* file_name,
const char* pid_string,
const char* timestamp_string) {
char* result = NULL;
// Lets start finding out if we have any %d and/or %t in the name.
// We will only replace the first occurrence of any placeholder
const char* pid = strstr(file_name, PidFilenamePlaceholder);
const char* timestamp = strstr(file_name, TimestampFilenamePlaceholder);
if (pid == NULL && timestamp == NULL) {
// We found no place-holders, return the simple filename
return os::strdup_check_oom(file_name, mtLogging);
}
// At least one of the place-holders were found in the file_name
const char* first = "";
size_t first_pos = -1;
size_t first_replace_len = 0;
const char* second = "";
size_t second_pos = -1;
size_t second_replace_len = 0;
// If we found a %p, then setup our variables accordingly
if (pid != NULL) {
if (timestamp == NULL || pid < timestamp) {
first = pid_string;
first_pos = pid - file_name;
first_replace_len = strlen(PidFilenamePlaceholder);
} else {
second = pid_string;
second_pos = pid - file_name;
second_replace_len = strlen(PidFilenamePlaceholder);
}
}
if (timestamp != NULL) {
if (pid == NULL || timestamp < pid) {
first = timestamp_string;
first_pos = timestamp - file_name;
first_replace_len = strlen(TimestampFilenamePlaceholder);
} else {
second = timestamp_string;
second_pos = timestamp - file_name;
second_replace_len = strlen(TimestampFilenamePlaceholder);
}
}
size_t first_len = strlen(first);
size_t second_len = strlen(second);
// Allocate the new buffer, size it to hold all we want to put in there +1.
size_t result_len = strlen(file_name) + first_len - first_replace_len + second_len - second_replace_len;
result = NEW_C_HEAP_ARRAY(char, result_len + 1, mtLogging);
// Assemble the strings
size_t file_name_pos = 0;
size_t i = 0;
while (i < result_len) {
if (file_name_pos == first_pos) {
// We are in the range of the first placeholder
strcpy(result + i, first);
// Bump output buffer position with length of replacing string
i += first_len;
// Bump source buffer position to skip placeholder
file_name_pos += first_replace_len;
} else if (file_name_pos == second_pos) {
// We are in the range of the second placeholder
strcpy(result + i, second);
i += second_len;
file_name_pos += second_replace_len;
} else {
// Else, copy char by char of the original file
result[i] = file_name[file_name_pos++];
i++;
}
}
// Add terminating char
result[result_len] = '\0';
return result;
}

@ -0,0 +1,83 @@
/*
* 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_LOGFILEOUTPUT_HPP
#define SHARE_VM_LOGGING_LOGFILEOUTPUT_HPP
#include "logging/logFileStreamOutput.hpp"
#include "runtime/mutex.hpp"
#include "utilities/globalDefinitions.hpp"
class LogDecorations;
// The log file output, with support for file rotation based on a target size.
class LogFileOutput : public LogFileStreamOutput {
private:
static const char* FileOpenMode;
static const char* FileCountOptionKey;
static const char* FileSizeOptionKey;
static const char* PidFilenamePlaceholder;
static const char* TimestampFilenamePlaceholder;
static const char* TimestampFormat;
static const size_t StartTimeBufferSize = 20;
static const size_t PidBufferSize = 21;
static char _pid_str[PidBufferSize];
static char _vm_start_time_str[StartTimeBufferSize];
Mutex _rotation_lock;
const char* _name;
char* _file_name;
char* _archive_name;
uint _current_file;
uint _file_count;
uint _file_count_max_digits;
size_t _archive_name_len;
size_t _rotate_size;
size_t _current_size;
void archive();
void rotate();
bool configure_rotation(const char* options);
char *make_file_name(const char* file_name, const char* pid_string, const char* timestamp_string);
static size_t parse_value(const char* value_str);
bool should_rotate() const {
return _file_count > 0 && _rotate_size > 0 && _current_size >= _rotate_size;
}
public:
LogFileOutput(const char *name);
virtual ~LogFileOutput();
virtual bool initialize(const char* options);
virtual int write(const LogDecorations& decorations, const char* msg);
virtual const char* name() const {
return _name;
}
static void set_file_name_parameters(jlong start_time);
};
#endif // SHARE_VM_LOGGING_LOGFILEOUTPUT_HPP

@ -0,0 +1,62 @@
/*
* 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/logDecorators.hpp"
#include "logging/logDecorations.hpp"
#include "logging/logFileStreamOutput.hpp"
#include "memory/allocation.inline.hpp"
LogStdoutOutput LogStdoutOutput::_instance;
LogStderrOutput LogStderrOutput::_instance;
int LogFileStreamOutput::write(const LogDecorations& decorations, const char* msg) {
char decoration_buf[LogDecorations::DecorationsBufferSize];
char* position = decoration_buf;
int total_written = 0;
for (uint i = 0; i < LogDecorators::Count; i++) {
LogDecorators::Decorator decorator = static_cast<LogDecorators::Decorator>(i);
if (!_decorators.is_decorator(decorator)) {
continue;
}
int written = jio_snprintf(position, sizeof(decoration_buf) - total_written, "[%-*s]",
_decorator_padding[decorator],
decorations.decoration(decorator));
if (written <= 0) {
return -1;
} else if (static_cast<size_t>(written - 2) > _decorator_padding[decorator]) {
_decorator_padding[decorator] = written - 2;
}
position += written;
total_written += written;
}
if (total_written == 0) {
total_written = jio_fprintf(_stream, "%s\n", msg);
} else {
total_written = jio_fprintf(_stream, "%s %s\n", decoration_buf, msg);
}
fflush(_stream);
return total_written;
}

@ -0,0 +1,82 @@
/*
* 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_LOGFILESTREAMOUTPUT_HPP
#define SHARE_VM_LOGGING_LOGFILESTREAMOUTPUT_HPP
#include "logging/logDecorators.hpp"
#include "logging/logOutput.hpp"
#include "utilities/globalDefinitions.hpp"
class LogDecorations;
// Base class for all FileStream-based log outputs.
class LogFileStreamOutput : public LogOutput {
protected:
FILE* _stream;
size_t _decorator_padding[LogDecorators::Count];
LogFileStreamOutput(FILE *stream) : _stream(stream) {
for (size_t i = 0; i < LogDecorators::Count; i++) {
_decorator_padding[i] = 0;
}
_decorator_padding[LogDecorators::level_decorator] = 7;
}
public:
virtual int write(const LogDecorations &decorations, const char* msg);
};
class LogStdoutOutput : public LogFileStreamOutput {
friend class LogOutput;
private:
static LogStdoutOutput _instance;
LogStdoutOutput() : LogFileStreamOutput(stdout) {
set_config_string("all=off");
}
virtual bool initialize(const char* options) {
return false;
}
public:
virtual const char* name() const {
return "stdout";
}
};
class LogStderrOutput : public LogFileStreamOutput {
friend class LogOutput;
private:
static LogStderrOutput _instance;
LogStderrOutput() : LogFileStreamOutput(stderr) {
set_config_string("all=warning");
}
virtual bool initialize(const char* options) {
return false;
}
public:
virtual const char* name() const {
return "stderr";
}
};
#endif // SHARE_VM_LOGGING_LOGFILESTREAMOUTPUT_HPP

@ -0,0 +1,42 @@
/*
* 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/logLevel.hpp"
#include "utilities/globalDefinitions.hpp"
const char* LogLevel::_name[] = {
"off",
#define LOG_LEVEL(name, printname) #printname,
LOG_LEVEL_LIST
#undef LOG_LEVEL
};
LogLevelType LogLevel::from_string(const char* str) {
for (uint i = 0; i < Count; i++) {
if (strcasecmp(str, _name[i]) == 0) {
return static_cast<LogLevelType>(i);
}
}
return Invalid;
}

@ -0,0 +1,86 @@
/*
* 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_LOGLEVEL_HPP
#define SHARE_VM_LOGGING_LOGLEVEL_HPP
#include "memory/allocation.hpp"
#include "utilities/macros.hpp"
// The list of log levels:
//
// develop - A non-product level that is finer than trace.
// Should be used for really expensive and/or
// extensive logging, or logging that shouldn't
// or can't be included in a product build.
//
// trace - Finest level of logging in product builds.
// Use for extensive/noisy logging that can
// give slow-down when enabled.
//
// debug - A finer level of logging. Use for semi-noisy
// logging that is does not fit the info level.
//
// info - General level of logging. Use for significant
// events and/or informative summaries.
//
// warning - Important messages that are not strictly errors.
//
// error - Critical messages caused by errors.
//
#define LOG_LEVEL_LIST \
NOT_PRODUCT(LOG_LEVEL(Develop, develop)) \
LOG_LEVEL(Trace, trace) \
LOG_LEVEL(Debug, debug) \
LOG_LEVEL(Info, info) \
LOG_LEVEL(Warning, warning) \
LOG_LEVEL(Error, error)
class LogLevel : public AllStatic {
public:
enum type {
Off,
#define LOG_LEVEL(name, printname) name,
LOG_LEVEL_LIST
#undef LOG_LEVEL
Count,
Invalid,
First = Off + 1,
Last = Error,
Default = Warning,
Unspecified = Info
};
static const char *name(LogLevel::type level) {
return _name[level];
}
static LogLevel::type from_string(const char* str);
private:
static const char* _name[];
};
typedef LogLevel::type LogLevelType;
#endif // SHARE_VM_LOGGING_LOGLEVEL_HPP

@ -0,0 +1,40 @@
/*
* 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/logFileStreamOutput.hpp"
#include "logging/logOutput.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/os.inline.hpp"
LogOutput* const LogOutput::Stdout = &LogStdoutOutput::_instance;
LogOutput* const LogOutput::Stderr = &LogStderrOutput::_instance;
LogOutput::~LogOutput() {
os::free(_config_string);
}
void LogOutput::set_config_string(const char* string) {
os::free(_config_string);
_config_string = os::strdup_check_oom(string, mtLogging);
}

@ -0,0 +1,68 @@
/*
* 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_LOGOUTPUT_HPP
#define SHARE_VM_LOGGING_LOGOUTPUT_HPP
#include "logging/logDecorators.hpp"
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
class LogDecorations;
// The base class/interface for log outputs.
// Keeps track of the latest configuration string,
// and its selected decorators.
class LogOutput : public CHeapObj<mtLogging> {
protected:
LogDecorators _decorators;
char* _config_string;
public:
static LogOutput* const Stdout;
static LogOutput* const Stderr;
void set_decorators(const LogDecorators &decorators) {
_decorators = decorators;
}
const LogDecorators& decorators() const {
return _decorators;
}
const char* config_string() const {
return _config_string;
}
LogOutput() : _config_string(NULL) {
}
virtual ~LogOutput();
void set_config_string(const char* string);
virtual const char* name() const = 0;
virtual bool initialize(const char* options) = 0;
virtual int write(const LogDecorations &decorations, const char* msg) = 0;
};
#endif // SHARE_VM_LOGGING_LOGOUTPUT_HPP

@ -0,0 +1,128 @@
/*
* 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/logLevel.hpp"
#include "logging/logOutputList.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/atomic.inline.hpp"
#include "runtime/orderAccess.inline.hpp"
#include "utilities/globalDefinitions.hpp"
jint LogOutputList::increase_readers() {
jint result = Atomic::add(1, &_active_readers);
assert(_active_readers > 0, "Ensure we have consistent state");
return result;
}
jint LogOutputList::decrease_readers() {
jint result = Atomic::add(-1, &_active_readers);
assert(result >= 0, "Ensure we have consistent state");
return result;
}
void LogOutputList::wait_until_no_readers() const {
OrderAccess::storeload();
while (_active_readers != 0) {
// Busy wait
}
}
void LogOutputList::set_output_level(LogOutput* output, LogLevelType level) {
LogOutputNode* node = find(output);
if (level == LogLevel::Off && node != NULL) {
remove_output(node);
} else if (level != LogLevel::Off && node == NULL) {
add_output(output, level);
} else if (node != NULL) {
update_output_level(node, level);
}
}
LogOutputList::LogOutputNode* LogOutputList::find(LogOutput* output) {
for (LogOutputNode* node = _level_start[LogLevel::Last]; node != NULL; node = node->_next) {
if (output == node->_value) {
return node;
}
}
return NULL;
}
void LogOutputList::remove_output(LogOutputList::LogOutputNode* node) {
assert(node != NULL, "Node must be non-null");
// Remove node from _level_start first
bool found = false;
for (uint level = LogLevel::First; level < LogLevel::Count; level++) {
if (_level_start[level] == node) {
found = true;
_level_start[level] = node->_next;
}
}
// Now remove it from the linked list
for (LogOutputNode* cur = _level_start[LogLevel::Last]; cur != NULL; cur = cur->_next) {
if (cur->_next == node) {
found = true;
cur->_next = node->_next;
break;
}
}
assert(found, "Node to be removed should always be found");
wait_until_no_readers();
delete node;
}
void LogOutputList::add_output(LogOutput* output, LogLevelType level) {
LogOutputNode* node = new LogOutputNode();
node->_value = output;
node->_level = level;
// Set the next pointer to the first node of a lower level
for (node->_next = _level_start[level];
node->_next != NULL && node->_next->_level == level;
node->_next = node->_next->_next) {
}
// Update the _level_start index
for (int l = LogLevel::Last; l >= level; l--) {
if (_level_start[l] == NULL || _level_start[l]->_level < level) {
_level_start[l] = node;
}
}
// Add the node the list
for (LogOutputNode* cur = _level_start[LogLevel::Last]; cur != NULL; cur = cur->_next) {
if (cur != node && cur->_next == node->_next) {
cur->_next = node;
break;
}
}
}
void LogOutputList::update_output_level(LogOutputList::LogOutputNode* node, LogLevelType level) {
add_output(node->_value, level);
wait_until_no_readers();
remove_output(node);
}

@ -0,0 +1,120 @@
/*
* 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_LOGOUTPUTLIST_HPP
#define SHARE_VM_LOGGING_LOGOUTPUTLIST_HPP
#include "logging/logLevel.hpp"
#include "memory/allocation.hpp"
#include "runtime/atomic.hpp"
#include "utilities/globalDefinitions.hpp"
class LogOutput;
// Data structure to keep track of log outputs for a given tagset.
// Essentially a sorted linked list going from error level outputs
// to outputs of finer levels. Keeps an index from each level to
// the first node in the list for the corresponding level.
// This allows a log message on, for example, info level to jump
// straight into the list where the first info level output can
// be found. The log message will then be printed on that output,
// as well as all outputs in nodes that follow in the list (which
// can be additional info level outputs and/or debug and trace outputs).
//
// Each instance keeps track of the number of current readers of the list.
// To remove a node from the list the node must first be unlinked,
// and the memory for that node can be freed whenever the removing
// thread observes an active reader count of 0 (after unlinking it).
class LogOutputList VALUE_OBJ_CLASS_SPEC {
private:
struct LogOutputNode : public CHeapObj<mtLogging> {
LogOutput* _value;
LogOutputNode* _next;
LogLevelType _level;
};
LogOutputNode* _level_start[LogLevel::Count];
volatile jint _active_readers;
LogOutputNode* find(LogOutput* output);
void remove_output(LogOutputNode* node);
void add_output(LogOutput* output, LogLevelType level);
void update_output_level(LogOutputNode* node, LogLevelType level);
public:
LogOutputList() : _active_readers(0) {
for (size_t i = 0; i < LogLevel::Count; i++) {
_level_start[i] = NULL;
}
}
// Test if the outputlist has an output for the given level.
bool is_level(LogLevelType level) {
return _level_start[level] != NULL;
}
// Set (add/update/remove) the output to the specified level.
void set_output_level(LogOutput* output, LogLevelType level);
// Bookkeeping functions to keep track of number of active readers/iterators for the list.
jint increase_readers();
jint decrease_readers();
void wait_until_no_readers() const;
class Iterator VALUE_OBJ_CLASS_SPEC {
friend class LogOutputList;
private:
LogOutputNode* _current;
LogOutputList* _list;
Iterator(LogOutputList* list, LogOutputNode* start) : _current(start), _list(list) {
}
public:
~Iterator() {
_list->decrease_readers();
}
LogOutput* operator*() {
return _current->_value;
}
void operator++(int) {
_current = _current->_next;
}
bool operator!=(const LogOutputNode *ref) const {
return _current != ref;
}
};
Iterator iterator(LogLevelType level = LogLevel::Last) {
increase_readers();
return Iterator(this, _level_start[level]);
}
LogOutputNode* end() const {
return NULL;
}
};
#endif // SHARE_VM_LOGGING_LOGOUTPUTLIST_HPP

@ -0,0 +1,59 @@
/*
* 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_LOGPREFIX_HPP
#define SHARE_VM_LOGGING_LOGPREFIX_HPP
#include "gc/shared/gcId.hpp"
#include "logging/logTag.hpp"
// Prefixes prepend each log message for a specified tagset with the given prefix.
// A prefix consists of a format string and a value or callback. Prefixes are added
// after the decorations but before the log message.
//
// List of prefixes for specific tags and/or tagsets.
// Syntax: LOG_PREFIX(<printf format>, <value/callback for value>, LOG_TAGS(<chosen log tags>))
#define LOG_PREFIX_LIST // Currently unused/empty
// The empty prefix, used when there's no prefix defined.
template <LogTagType T0, LogTagType T1, LogTagType T2, LogTagType T3, LogTagType T4, LogTagType GuardTag = LogTag::__NO_TAG>
struct LogPrefix : public AllStatic {
STATIC_ASSERT(GuardTag == LogTag::__NO_TAG);
static size_t prefix(char* buf, size_t len) {
return 0;
}
};
#define LOG_PREFIX(fmt, fn, ...) \
template <> struct LogPrefix<__VA_ARGS__> { \
static size_t prefix(char* buf, size_t len) { \
int ret = jio_snprintf(buf, len, fmt, fn); \
assert(ret >= 0, \
err_msg("Failed to prefix log message using prefix ('%s', '%s'), log buffer too small?", fmt, #fn)); \
return ret; \
} \
};
LOG_PREFIX_LIST
#undef LOG_PREFIX
#endif // SHARE_VM_LOGGING_LOGPREFIX_HPP

@ -0,0 +1,42 @@
/*
* 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/logTag.hpp"
#include "utilities/globalDefinitions.hpp"
const char* LogTag::_name[] = {
"", // __NO_TAG
#define LOG_TAG(name) #name,
LOG_TAG_LIST
#undef LOG_TAG
};
LogTagType LogTag::from_string(const char* str) {
for (uint i = 0; i < LogTag::Count; i++) {
if (strcasecmp(str, _name[i]) == 0) {
return static_cast<LogTagType>(i);
}
}
return __NO_TAG;
}

@ -0,0 +1,79 @@
/*
* 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_LOGTAG_HPP
#define SHARE_VM_LOGGING_LOGTAG_HPP
#include "memory/allocation.hpp"
#include "utilities/globalDefinitions.hpp"
// List of available logging tags. New tags should be added here.
// (The tags 'all', 'disable' and 'help' are special tags that can
// not be used in log calls, and should not be listed below.)
#define LOG_TAG_LIST \
LOG_TAG(logging)
#define PREFIX_LOG_TAG(T) (LogTag::T)
// Expand a set of log tags to their prefixed names.
// For error detection purposes, the macro passes one more tag than what is supported.
// If too many tags are given, a static assert in the log class will fail.
#define LOG_TAGS_EXPANDED(T0, T1, T2, T3, T4, T5, ...) PREFIX_LOG_TAG(T0), PREFIX_LOG_TAG(T1), PREFIX_LOG_TAG(T2), \
PREFIX_LOG_TAG(T3), PREFIX_LOG_TAG(T4), PREFIX_LOG_TAG(T5)
// The EXPAND_VARARGS macro is required for MSVC, or it will resolve the LOG_TAGS_EXPANDED macro incorrectly.
#define EXPAND_VARARGS(x) x
#define LOG_TAGS(...) EXPAND_VARARGS(LOG_TAGS_EXPANDED(__VA_ARGS__, __NO_TAG, __NO_TAG, __NO_TAG, __NO_TAG, __NO_TAG, __NO_TAG))
// Log tags are used to classify log messages.
// Each log message can be assigned between 1 to LogTag::MaxTags number of tags.
// Specifying multiple tags for a log message means that only outputs configured
// for those exact tags, or a subset of the tags with a wildcard, will see the logging.
// Multiple tags should be comma separated, e.g. log_error(tag1, tag2)("msg").
class LogTag : public AllStatic {
public:
// The maximum number of tags that a single log message can have.
// E.g. there might be hundreds of different tags available,
// but a specific log message can only be tagged with up to MaxTags of those.
static const size_t MaxTags = 5;
enum type {
__NO_TAG,
#define LOG_TAG(name) name,
LOG_TAG_LIST
#undef LOG_TAG
Count
};
static const char* name(LogTag::type tag) {
return _name[tag];
}
static LogTag::type from_string(const char *str);
private:
static const char* _name[];
};
typedef LogTag::type LogTagType;
#endif // SHARE_VM_LOGGING_LOGTAG_HPP

@ -0,0 +1,160 @@
/*
* 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";
LogTagLevelExpression::~LogTagLevelExpression() {
os::free(_string);
}
void LogTagLevelExpression::clear() {
_ntags = 0;
_ncombinations = 0;
for (size_t combination = 0; combination < MaxCombinations; combination++) {
_level[combination] = LogLevel::Invalid;
_allow_other_tags[combination] = false;
for (size_t tag = 0; tag < LogTag::MaxTags; tag++) {
_tags[combination][tag] = LogTag::__NO_TAG;
}
}
os::free(_string);
_string = NULL;
}
bool LogTagLevelExpression::parse(const char* str, outputStream* errstream) {
bool success = true;
clear();
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;
}
add_tag(tag);
cur_tag = plus_pos + 1;
} while (plus_pos != NULL);
new_combination();
}
// Save the (unmodified) string for printing purposes.
_string = copy;
strcpy(_string, str);
return success;
}
LogLevelType LogTagLevelExpression::level_for(const LogTagSet& ts) const {
LogLevelType level = LogLevel::Off;
for (size_t combination = 0; combination < _ncombinations; combination++) {
bool contains_all = true;
size_t tag_idx;
for (tag_idx = 0; tag_idx < LogTag::MaxTags && _tags[combination][tag_idx] != LogTag::__NO_TAG; tag_idx++) {
if (!ts.contains(_tags[combination][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.
if (contains_all && (_allow_other_tags[combination] || tag_idx == ts.ntags())) {
level = _level[combination];
}
}
return level;
}

@ -0,0 +1,81 @@
/*
* 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/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 {
private:
static const size_t MaxCombinations = 32;
static const char* DefaultExpressionString;
size_t _ntags, _ncombinations;
LogTagType _tags[MaxCombinations][LogTag::MaxTags];
LogLevelType _level[MaxCombinations];
bool _allow_other_tags[MaxCombinations];
char* _string;
void new_combination() {
_ncombinations++;
_ntags = 0;
}
void add_tag(LogTagType tag) {
assert(_ntags < LogTag::MaxTags, "Can't have more tags than MaxTags!");
_tags[_ncombinations][_ntags++] = tag;
}
void set_level(LogLevelType level) {
_level[_ncombinations] = level;
}
void set_allow_other_tags() {
_allow_other_tags[_ncombinations] = true;
}
void clear();
public:
LogTagLevelExpression() : _ntags(0), _ncombinations(0), _string(NULL) {
}
const char* to_string() const {
return _string;
}
~LogTagLevelExpression();
bool parse(const char* str, outputStream* errstream = NULL);
LogLevelType level_for(const LogTagSet& ts) const;
};
#endif // SHARE_VM_LOGGING_LOGTAGLEVELEXPRESSION_HPP

@ -0,0 +1,92 @@
/*
* 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/logDecorations.hpp"
#include "logging/logLevel.hpp"
#include "logging/logOutput.hpp"
#include "logging/logTag.hpp"
#include "logging/logTagSet.hpp"
LogTagSet* LogTagSet::_list = NULL;
size_t LogTagSet::_ntagsets = 0;
// This constructor is called only during static initialization.
// See the declaration in logTagSet.hpp for more information.
LogTagSet::LogTagSet(LogTagType t0, LogTagType t1, LogTagType t2, LogTagType t3, LogTagType t4)
: _next(_list) {
_tag[0] = t0;
_tag[1] = t1;
_tag[2] = t2;
_tag[3] = t3;
_tag[4] = t4;
for (_ntags = 0; _ntags < LogTag::MaxTags && _tag[_ntags] != LogTag::__NO_TAG; _ntags++) {
}
_list = this;
_ntagsets++;
// Set the default output to warning and error level for all new tagsets.
_output_list.set_output_level(LogOutput::Stderr, LogLevel::Default);
}
bool LogTagSet::is_level(LogLevelType level) {
return _output_list.is_level(level);
}
void LogTagSet::update_decorators(const LogDecorators& decorator) {
LogDecorators new_decorators = decorator;
for (LogOutputList::Iterator it = _output_list.iterator(); it != _output_list.end(); it++) {
new_decorators.combine_with((*it)->decorators());
}
_decorators = new_decorators;
}
bool LogTagSet::has_output(const LogOutput* output) {
for (LogOutputList::Iterator it = _output_list.iterator(); it != _output_list.end(); it++) {
if (*it == output) {
return true;
}
}
return false;
}
void LogTagSet::log(LogLevelType level, const char* msg) {
LogDecorations decorations(level, *this, _decorators);
for (LogOutputList::Iterator it = _output_list.iterator(level); it != _output_list.end(); it++) {
(*it)->write(decorations, msg);
}
}
int LogTagSet::label(char* buf, size_t len) {
int tot_written = 0;
for (size_t i = 0; i < _ntags; i++) {
int written = jio_snprintf(buf + tot_written, len - tot_written, "%s%s",
(i == 0 ? "" : ","),
LogTag::name(_tag[i]));
if (written < 0) {
return -1;
}
tot_written += written;
}
return tot_written;
}

@ -0,0 +1,114 @@
/*
* 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_LOGTAGSET_HPP
#define SHARE_VM_LOGGING_LOGTAGSET_HPP
#include "logging/logDecorators.hpp"
#include "logging/logLevel.hpp"
#include "logging/logOutputList.hpp"
#include "logging/logTag.hpp"
#include "utilities/globalDefinitions.hpp"
// The tagset represents a combination of tags that occur in a log call somewhere.
// Tagsets are created automatically by the LogTagSetMappings and should never be
// instantiated directly somewhere else.
class LogTagSet VALUE_OBJ_CLASS_SPEC {
private:
static LogTagSet* _list;
static size_t _ntagsets;
LogTagSet* const _next;
size_t _ntags;
LogTagType _tag[LogTag::MaxTags];
LogOutputList _output_list;
LogDecorators _decorators;
// Keep constructor private to prevent incorrect instantiations of this class.
// Only LogTagSetMappings can create/contain instances of this class.
// The constructor links all tagsets together in a global list of tagsets.
// This list is used during configuration to be able to update all tagsets
// and their configurations to reflect the new global log configuration.
LogTagSet(LogTagType t0, LogTagType t1, LogTagType t2, LogTagType t3, LogTagType t4);
template <LogTagType T0, LogTagType T1, LogTagType T2, LogTagType T3, LogTagType T4>
friend class LogTagSetMapping;
public:
static LogTagSet* first() {
return _list;
}
LogTagSet* next() {
return _next;
}
size_t ntags() const {
return _ntags;
}
bool contains(LogTagType tag) const {
for (size_t i = 0; _tag[i] != LogTag::__NO_TAG; i++) {
if (tag == _tag[i]) {
return true;
}
}
return false;
}
void set_output_level(LogOutput* output, LogLevelType level) {
_output_list.set_output_level(output, level);
}
// Refresh the decorators for this tagset to contain the decorators for all
// of its current outputs combined with the given decorators.
void update_decorators(const LogDecorators& decorator);
int label(char *buf, size_t len);
bool has_output(const LogOutput* output);
bool is_level(LogLevelType level);
void log(LogLevelType level, const char* msg);
};
template <LogTagType T0, LogTagType T1 = LogTag::__NO_TAG, LogTagType T2 = LogTag::__NO_TAG,
LogTagType T3 = LogTag::__NO_TAG, LogTagType T4 = LogTag::__NO_TAG>
class LogTagSetMapping : public AllStatic {
private:
static LogTagSet _tagset;
public:
static LogTagSet& tagset() {
return _tagset;
}
};
// Instantiate the static field _tagset for all tagsets that are used for logging somewhere.
// (This must be done here rather than the .cpp file because it's a template.)
// Each combination of tags used as template arguments to the Log class somewhere (via macro or not)
// will instantiate the LogTagSetMapping template, which in turn creates the static field for that
// tagset. This _tagset contains the configuration for those tags.
template <LogTagType T0, LogTagType T1, LogTagType T2, LogTagType T3, LogTagType T4>
LogTagSet LogTagSetMapping<T0, T1, T2, T3, T4>::_tagset(T0, T1, T2, T3, T4);
#endif // SHARE_VM_LOGGING_LOGTAGSET_HPP

@ -154,8 +154,9 @@ enum MemoryType {
mtChunk = 0x0C, // chunk that holds content of arenas
mtTest = 0x0D, // Test type for verifying NMT
mtTracing = 0x0E, // memory used for Tracing
mtNone = 0x0F, // undefined
mt_number_of_types = 0x10 // number of memory types (mtDontTrack
mtLogging = 0x0F, // memory for logging
mtNone = 0x10, // undefined
mt_number_of_types = 0x11 // number of memory types (mtDontTrack
// is not included as validate type)
};

@ -128,6 +128,7 @@
# include "interpreter/templateInterpreter.hpp"
# include "interpreter/templateTable.hpp"
# include "jvmtifiles/jvmti.h"
# include "logging/log.hpp"
# include "memory/allocation.hpp"
# include "memory/allocation.inline.hpp"
# include "memory/heap.hpp"

@ -32,6 +32,7 @@
#include "gc/shared/genCollectedHeap.hpp"
#include "gc/shared/referenceProcessor.hpp"
#include "gc/shared/taskqueue.hpp"
#include "logging/logConfiguration.hpp"
#include "memory/allocation.inline.hpp"
#include "memory/universe.inline.hpp"
#include "oops/oop.inline.hpp"
@ -3190,6 +3191,26 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args,
if (FLAG_SET_CMDLINE(bool, PrintGCTimeStamps, true) != Flag::SUCCESS) {
return JNI_EINVAL;
}
} else if (match_option(option, "-Xlog", &tail)) {
bool ret = false;
if (strcmp(tail, ":help") == 0) {
LogConfiguration::print_command_line_help(defaultStream::output_stream());
vm_exit(0);
} else if (strcmp(tail, ":disable") == 0) {
LogConfiguration::disable_logging();
ret = true;
} else if (*tail == '\0') {
ret = LogConfiguration::parse_command_line_arguments();
assert(ret, "-Xlog without arguments should never fail to parse");
} else if (*tail == ':') {
ret = LogConfiguration::parse_command_line_arguments(tail + 1);
}
if (ret == false) {
jio_fprintf(defaultStream::error_stream(),
"Invalid -Xlog option '-Xlog%s'\n",
tail);
return JNI_EINVAL;
}
// JNI hooks
} else if (match_option(option, "-Xcheck", &tail)) {
if (!strcmp(tail, ":jni")) {

@ -127,6 +127,7 @@ Monitor* GCTaskManager_lock = NULL;
Mutex* Management_lock = NULL;
Monitor* Service_lock = NULL;
Monitor* PeriodicTask_lock = NULL;
Mutex* LogConfiguration_lock = NULL;
#ifdef INCLUDE_TRACE
Mutex* JfrStacktrace_lock = NULL;
@ -282,6 +283,7 @@ void mutex_init() {
if (WhiteBoxAPI) {
def(Compilation_lock , Monitor, leaf, false, Monitor::_safepoint_check_never);
}
def(LogConfiguration_lock , Mutex, nonleaf, false, Monitor::_safepoint_check_always);
#ifdef INCLUDE_TRACE
def(JfrMsg_lock , Monitor, leaf, true, Monitor::_safepoint_check_always);

@ -126,6 +126,7 @@ extern Mutex* MMUTracker_lock; // protects the MMU
extern Mutex* Management_lock; // a lock used to serialize JVM management
extern Monitor* Service_lock; // a lock used for service thread operation
extern Monitor* PeriodicTask_lock; // protects the periodic task structure
extern Mutex* LogConfiguration_lock; // protects configuration of logging
#ifdef INCLUDE_TRACE
extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table

@ -37,6 +37,7 @@
#include "interpreter/linkResolver.hpp"
#include "interpreter/oopMapCache.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "logging/logConfiguration.hpp"
#include "memory/metaspaceShared.hpp"
#include "memory/oopFactory.hpp"
#include "memory/universe.inline.hpp"
@ -3306,6 +3307,10 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
// Initialize the os module before using TLS
os::init();
// Record VM creation timing statistics
TraceVmCreationTime create_vm_timer;
create_vm_timer.start();
// Initialize system properties.
Arguments::init_system_properties();
@ -3315,6 +3320,9 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
// Update/Initialize System properties after JDK version number is known
Arguments::init_version_specific_system_properties();
// Make sure to initialize log configuration *before* parsing arguments
LogConfiguration::initialize(create_vm_timer.begin_time());
// Parse arguments
jint parse_result = Arguments::parse(args);
if (parse_result != JNI_OK) return parse_result;
@ -3341,10 +3349,6 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
HOTSPOT_VM_INIT_BEGIN();
// Record VM creation timing statistics
TraceVmCreationTime create_vm_timer;
create_vm_timer.start();
// Timing (must come after argument parsing)
TraceTime timer("Create VM", TraceStartupTime);
@ -3492,6 +3496,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
// debug stuff, that does not work until all basic classes have been initialized.
set_init_completed();
LogConfiguration::post_initialize();
Metaspace::post_initialize();
HOTSPOT_VM_INIT_END();
@ -3966,6 +3971,8 @@ bool Threads::destroy_vm() {
// exit_globals() will delete tty
exit_globals();
LogConfiguration::finalize();
return true;
}

@ -118,6 +118,10 @@ public:
void start()
{ _timer.update_to(0); _begin_time = os::javaTimeMillis(); }
jlong begin_time() const {
return _begin_time;
}
/**
* Only call this if initialization completes successfully; it will
* crash if PerfMemory_exit() has already been called (usually by

@ -40,6 +40,7 @@ const char* NMTUtil::_memory_type_names[] = {
"Arena Chunk",
"Test",
"Tracing",
"Logging",
"Unknown"
};

@ -1440,3 +1440,14 @@ bool networkStream::connect(const char *ip, short port) {
}
#endif
void logStream::write(const char* s, size_t len) {
if (len > 0 && s[len - 1] == '\n') {
_current_line.write(s, len - 1);
_log_func(_current_line.as_string());
_current_line.reset();
} else {
_current_line.write(s, len);
update_position(s, len);
}
}

@ -235,6 +235,18 @@ class fdStream : public outputStream {
void flush() {};
};
class logStream : public outputStream {
private:
stringStream _current_line;
void (*_log_func)(const char* fmt, ...);
public:
void write(const char* s, size_t len);
logStream(void (*log_func)(const char* fmt, ...)) : _log_func(log_func) {}
~logStream() {
guarantee(_current_line.size() == 0, "Buffer not flushed. Missing call to print_cr()?");
}
};
class gcLogFileStream : public fileStream {
protected:
const char* _file_name;

@ -0,0 +1,43 @@
/*
* 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.
*/
/*
* @test TestBasicLogOutput
* @summary Ensure logging can be enabled and successfully prints to stdout.
* @library /testlibrary
*/
import jdk.test.lib.ProcessTools;
import jdk.test.lib.OutputAnalyzer;
public class TestBasicLogOutput {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder("-Xlog:all=trace", "-version");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldContain("[logging]"); // expected tag(s)
output.shouldContain("Log configuration fully initialized."); // expected message
output.shouldHaveExitValue(0);
}
}