# HG changeset patch # User mlarsson # Date 1455274129 -3600 # Fri Feb 12 11:48:49 2016 +0100 # Node ID 88a626bfac868e1a83b81c98eb39532de614a1d2 # Parent fc2c277bce1477d744ac4ea5008663c7124996f0 imported patch 8145934 diff --git a/src/os/posix/vm/os_posix.cpp b/src/os/posix/vm/os_posix.cpp --- a/src/os/posix/vm/os_posix.cpp +++ b/src/os/posix/vm/os_posix.cpp @@ -319,6 +319,14 @@ return ::fdopen(fd, mode); } +void os::flockfile(FILE* fp) { + ::flockfile(fp); +} + +void os::funlockfile(FILE* fp) { + ::funlockfile(fp); +} + // Builds a platform dependent Agent_OnLoad_ function name // which is used to find statically linked in agents. // Parameters: diff --git a/src/os/windows/vm/os_windows.cpp b/src/os/windows/vm/os_windows.cpp --- a/src/os/windows/vm/os_windows.cpp +++ b/src/os/windows/vm/os_windows.cpp @@ -4631,6 +4631,14 @@ } } +void os::flockfile(FILE* fp) { + _lock_file(fp); +} + +void os::funlockfile(FILE* fp) { + _unlock_file(fp); +} + // This code is a copy of JDK's nonSeekAvailable // from src/windows/hpi/src/sys_api_md.c diff --git a/src/share/vm/logging/log.cpp b/src/share/vm/logging/log.cpp --- a/src/share/vm/logging/log.cpp +++ b/src/share/vm/logging/log.cpp @@ -30,9 +30,36 @@ #include "logging/log.hpp" #include "logging/logConfiguration.hpp" +#include "logging/logMessage.hpp" #include "logging/logOutput.hpp" #include "memory/resourceArea.hpp" +static bool file_contains_substrings_in_order(const char* filename, const char* substrs[]) { + FILE* fp = fopen(filename, "r"); + assert(fp, "File read error"); + char buffer[1024]; + for (int i = 0; substrs[i] != NULL; i++) { + assert(sizeof(buffer) > strlen(substrs[i]), "insufficient buffer"); + bool found = false; + while (fgets(buffer, sizeof(buffer), fp) != NULL) { + if (strstr(buffer, substrs[i]) != NULL) { + found = true; + break; + } + } + if (!found) { + return false; + } + } + fclose(fp); + return true; +} + +static bool file_contains_substring(const char* filename, const char* substr) { + const char* strs[] = {substr, NULL}; + return file_contains_substrings_in_order(filename, strs); +} + void Test_log_length() { remove("loglengthoutput.txt"); @@ -83,13 +110,7 @@ NULL, NULL, log.error_stream()); // Look for end of message in output file - FILE* fp = fopen("loglengthoutput.txt", "r"); - assert(fp, "File read error"); - char output[600]; - if (fgets(output, 600, fp) != NULL) { - assert(strstr(output, "37:1234567890-"), "logging print size error"); - } - fclose(fp); + assert(file_contains_substring("loglengthoutput.txt", "37:1234567890-"), "logging print size error"); remove("loglengthoutput.txt"); } @@ -136,4 +157,203 @@ LogConfiguration::parse_log_arguments("stdout", saved_config, NULL, NULL, log.error_stream()); os::free(saved_config); } + +class LogMessageTest { + private: + static LogHandle(logging) _log; + static const char* _level_filename[]; + + static void test_level_inclusion(); + static void test_long_message(); + static void test_message_with_many_lines(); + static void test_line_order(); + static void test_prefixing(); + + public: + static void test(); +}; + +const char* LogMessageTest::_level_filename[] = { + NULL, // LogLevel::Off +#define LOG_LEVEL(name, printname) "multiline-" #printname ".log", + LOG_LEVEL_LIST +#undef LOG_LEVEL +}; + +void Test_multiline_logging() { + LogMessageTest::test(); +} + +void LogMessageTest::test() { + ResourceMark rm; + + for (int i = 0; i < LogLevel::Count; i++) { + char buf[32]; + // Attempt to remove possibly pre-existing log files + remove(_level_filename[i]); + + jio_snprintf(buf, sizeof(buf), "logging=%s", LogLevel::name(static_cast(i))); + bool success = LogConfiguration::parse_log_arguments(_level_filename[i], buf, + NULL, NULL, _log.error_stream()); + assert(success, "unable to configure logging to file '%s'", _level_filename[i]); + } + + test_level_inclusion(); + test_line_order(); + test_long_message(); + test_message_with_many_lines(); + test_prefixing(); + + // Stop logging to the files and remove them. + for (int i = 0; i < LogLevel::Count; i++) { + LogConfiguration::parse_log_arguments(_level_filename[i], "all=off", NULL, NULL, _log.error_stream()); + remove(_level_filename[i]); + } +} + +// Verify that messages with multiple levels are written +// to outputs configured for all the corresponding levels +void LogMessageTest::test_level_inclusion() { + const size_t message_count = 10; + LogMessage msg[message_count]; + + struct { + int message_number; + LogLevelType level; + } lines[] = { + { 0, LogLevel::Error }, + { 1, LogLevel::Info }, + { 2, LogLevel::Info }, { 2, LogLevel::Debug }, + { 3, LogLevel::Info }, { 3, LogLevel::Warning }, + { 4, LogLevel::Debug }, { 4, LogLevel::Warning }, + { 5, LogLevel::Trace }, { 5, LogLevel::Debug }, + { 6, LogLevel::Warning }, { 6, LogLevel::Error }, + { 7, LogLevel::Trace }, { 7, LogLevel::Info }, { 7, LogLevel::Debug }, + { 8, LogLevel::Trace }, { 8, LogLevel::Debug }, { 8, LogLevel::Info }, + { 8, LogLevel::Warning }, { 8, LogLevel::Error}, + { 9, LogLevel::Trace } + }; + + // Fill in messages with the above lines + for (size_t i = 0; i < ARRAY_SIZE(lines); i++) { + switch (lines[i].level) { +#define LOG_LEVEL(name, printname) \ + case LogLevel::name: \ + msg[lines[i].message_number].printname("msg[%d]: " #printname, lines[i].message_number); \ + break; +LOG_LEVEL_LIST +#undef LOG_LEVEL + } + } + + for (size_t i = 0; i < message_count; i++) { + _log.write(msg[i]); + } + + // Verify that lines are written to the expected log files + for (size_t i = 0; i < ARRAY_SIZE(lines); i++) { + char expected[256]; + jio_snprintf(expected, sizeof(expected), "msg[%d]: %s", + lines[i].message_number, LogLevel::name(lines[i].level)); + for (int level = lines[i].level; level > 0; level--) { + assert(file_contains_substring(_level_filename[level], expected), + "line #" SIZE_FORMAT " missing from log file '%s'", i, _level_filename[level]); + } + for (int level = lines[i].level + 1; level < LogLevel::Count; level++) { + assert(!file_contains_substring(_level_filename[level], expected), + "line #" SIZE_FORMAT " erroneously included in log file '%s'", i, _level_filename[level]); + } + } +} + +// Verify that messages are logged in the order they are added to the log message +void LogMessageTest::test_line_order() { + LogMessage msg; + msg.info("info line").error("error line").trace("trace line") + .error("another error").warning("warning line").debug("debug line"); + _log.write(msg); + + const char* expected[] = { "info line", "error line", "trace line", + "another error", "warning line", "debug line", NULL }; + assert(file_contains_substrings_in_order(_level_filename[LogLevel::Trace], expected), + "output missing or in incorrect order"); +} + +void LogMessageTest::test_long_message() { + // Write 10K bytes worth of log data + LogMessage msg; + const size_t size = 10 * K; + const char* start_marker = "#start#"; + const char* end_marker = "#the end#"; + char* data = NEW_C_HEAP_ARRAY(char, size, mtLogging); + + // fill buffer with start_marker...some data...end_marker + sprintf(data, "%s", start_marker); + for (size_t i = strlen(start_marker); i < size; i++) { + data[i] = '0' + (i % 10); + } + sprintf(data + size - strlen(end_marker) - 1, "%s", end_marker); + + msg.trace("%s", data); // Adds a newline, making the message exactly 10K in length. + _log.write(msg); + + const char* expected[] = { start_marker, "0123456789", end_marker, NULL }; + assert(file_contains_substrings_in_order(_level_filename[LogLevel::Trace], expected), + "unable to print long line"); + FREE_C_HEAP_ARRAY(char, data); +} + +void LogMessageTest::test_message_with_many_lines() { + const size_t lines = 100; + const size_t line_length = 16; + + LogMessage msg; + for (size_t i = 0; i < lines; i++) { + msg.info("Line #" SIZE_FORMAT, i); + } + _log.write(msg); + + char expected_lines_data[lines][line_length]; + const char* expected_lines[lines + 1]; + for (size_t i = 0; i < lines; i++) { + jio_snprintf(&expected_lines_data[i][0], line_length, "Line #" SIZE_FORMAT, i); + expected_lines[i] = expected_lines_data[i]; + } + expected_lines[lines] = NULL; + + assert(file_contains_substrings_in_order(_level_filename[LogLevel::Trace], expected_lines), + "couldn't find all lines in multiline message"); +} + +static size_t dummy_prefixer(char* buf, size_t len) { + static int i = 0; + const char* prefix = "some prefix: "; + const size_t prefix_len = strlen(prefix); + if (len < prefix_len) { + return prefix_len; + } + jio_snprintf(buf, len, "%s", prefix); + return prefix_len; +} + +void LogMessageTest::test_prefixing() { + LogMessage msg; + msg.set_prefix(dummy_prefixer); + for (int i = 0; i < 3; i++) { + msg.info("test %d", i); + } + msg.set_prefix(NULL); + msg.info("test 3"); + _log.write(msg); + + const char* expected[] = { + "] some prefix: test 0", + "] some prefix: test 1", + "] some prefix: test 2", + "] test 3", + NULL + }; + assert(file_contains_substrings_in_order(_level_filename[LogLevel::Trace], expected),"error in prefixed output"); +} + #endif // PRODUCT diff --git a/src/share/vm/logging/log.hpp b/src/share/vm/logging/log.hpp --- a/src/share/vm/logging/log.hpp +++ b/src/share/vm/logging/log.hpp @@ -34,6 +34,8 @@ #include "utilities/debug.hpp" #include "utilities/ostream.hpp" +class LogMessage; + // // Logging macros // @@ -107,6 +109,10 @@ return LogTagSetMapping::tagset().is_level(level); } + static void write(const LogMessage& msg) { + LogTagSetMapping::tagset().log(msg); + }; + template ATTRIBUTE_PRINTF(1, 2) static void write(const char* fmt, ...) { @@ -137,6 +143,7 @@ } else { puts(buf); } + va_end(saved_args); } template diff --git a/src/share/vm/logging/logDecorations.cpp b/src/share/vm/logging/logDecorations.cpp --- a/src/share/vm/logging/logDecorations.cpp +++ b/src/share/vm/logging/logDecorations.cpp @@ -31,7 +31,7 @@ jlong LogDecorations::_vm_start_time_millis = 0; LogDecorations::LogDecorations(LogLevelType level, const LogTagSet &tagset, const LogDecorators &decorators) - : _level(level), _tagset(tagset), _millis(-1) { + : _level(level), _tagset(tagset), _millis(-1) { create_decorations(decorators); } @@ -101,8 +101,9 @@ } 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) + // Avoid generating the level decoration because it may change. + // The decoration() method has a special case for level decorations. + return pos; } char* LogDecorations::create_tags_decoration(char* pos) { diff --git a/src/share/vm/logging/logDecorations.hpp b/src/share/vm/logging/logDecorations.hpp --- a/src/share/vm/logging/logDecorations.hpp +++ b/src/share/vm/logging/logDecorations.hpp @@ -50,7 +50,18 @@ public: LogDecorations(LogLevelType level, const LogTagSet& tagset, const LogDecorators& decorators); + LogLevelType level() const { + return _level; + } + + void set_level(LogLevelType level) { + _level = level; + } + const char* decoration(LogDecorators::Decorator decorator) const { + if (decorator == LogDecorators::level_decorator) { + return LogLevel::name(_level); + } return _decoration_offset[decorator]; } diff --git a/src/share/vm/logging/logDecorators.hpp b/src/share/vm/logging/logDecorators.hpp --- a/src/share/vm/logging/logDecorators.hpp +++ b/src/share/vm/logging/logDecorators.hpp @@ -100,6 +100,10 @@ _decorators |= source._decorators; } + bool is_empty() const { + return _decorators == 0; + } + bool is_decorator(LogDecorators::Decorator decorator) const { return (_decorators & mask(decorator)) != 0; } diff --git a/src/share/vm/logging/logFileOutput.cpp b/src/share/vm/logging/logFileOutput.cpp --- a/src/share/vm/logging/logFileOutput.cpp +++ b/src/share/vm/logging/logFileOutput.cpp @@ -163,6 +163,24 @@ return written; } +int LogFileOutput::write(LogMessage::Iterator msg_iterator) { + if (_stream == NULL) { + // An error has occurred with this output, avoid writing to it. + return 0; + } + + _rotation_semaphore.wait(); + int written = LogFileStreamOutput::write(msg_iterator); + _current_size += written; + + if (should_rotate()) { + rotate(); + } + _rotation_semaphore.signal(); + + 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", diff --git a/src/share/vm/logging/logFileOutput.hpp b/src/share/vm/logging/logFileOutput.hpp --- a/src/share/vm/logging/logFileOutput.hpp +++ b/src/share/vm/logging/logFileOutput.hpp @@ -74,6 +74,7 @@ virtual ~LogFileOutput(); virtual bool initialize(const char* options); virtual int write(const LogDecorations& decorations, const char* msg); + virtual int write(LogMessage::Iterator msg_iterator); virtual void force_rotate(); virtual const char* name() const { diff --git a/src/share/vm/logging/logFileStreamOutput.cpp b/src/share/vm/logging/logFileStreamOutput.cpp --- a/src/share/vm/logging/logFileStreamOutput.cpp +++ b/src/share/vm/logging/logFileStreamOutput.cpp @@ -25,24 +25,26 @@ #include "logging/logDecorators.hpp" #include "logging/logDecorations.hpp" #include "logging/logFileStreamOutput.hpp" +#include "logging/logMessage.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 LogFileStreamOutput::write_decorations(char* buffer, + size_t buffer_length, + const LogDecorations& decorations) { int total_written = 0; + char* position = buffer; for (uint i = 0; i < LogDecorators::Count; i++) { LogDecorators::Decorator decorator = static_cast(i); if (!_decorators.is_decorator(decorator)) { continue; } - int written = jio_snprintf(position, sizeof(decoration_buf) - total_written, "[%-*s]", - _decorator_padding[decorator], - decorations.decoration(decorator)); + + int written = jio_snprintf(position, buffer_length - total_written, "[%-*s]", + _decorator_padding[decorator], decorations.decoration(decorator)); if (written <= 0) { return -1; } else if (static_cast(written - 2) > _decorator_padding[decorator]) { @@ -51,12 +53,47 @@ position += written; total_written += written; } + return total_written; +} - if (total_written == 0) { - total_written = jio_fprintf(_stream, "%s\n", msg); +int LogFileStreamOutput::write(const LogDecorations& decorations, const char* msg) { + const bool use_decorations = !_decorators.is_empty(); + + int written; + if (use_decorations) { + char decoration_buf[LogDecorations::DecorationsBufferSize]; + write_decorations(decoration_buf, sizeof(decoration_buf), decorations); + written = jio_fprintf(_stream, "%s %s\n", decoration_buf, msg); } else { - total_written = jio_fprintf(_stream, "%s %s\n", decoration_buf, msg); + written = jio_fprintf(_stream, "%s\n", msg); } fflush(_stream); - return total_written; + + return written; } + +int LogFileStreamOutput::write(LogMessage::Iterator msg_iterator) { + const bool use_decorations = !_decorators.is_empty(); + char decoration_buf[LogDecorations::DecorationsBufferSize]; + + int written = 0; + os::flockfile(_stream); + LogLevelType previous_level = LogLevel::Invalid; + for (; !msg_iterator.is_at_end(); msg_iterator++) { + if (use_decorations) { + const LogDecorations& decorations = msg_iterator.decorations(); + // Write decorators the first time, and re-write them if the level changed. + if (written == 0 || decorations.level() != previous_level) { + write_decorations(decoration_buf, sizeof(decoration_buf), decorations); + previous_level = decorations.level(); + } + written += jio_fprintf(_stream, "%s %s\n", decoration_buf, msg_iterator.message()); + } else { + written += jio_fprintf(_stream, "%s\n", msg_iterator.message()); + } + } + fflush(_stream); + os::funlockfile(_stream); + + return written; +} diff --git a/src/share/vm/logging/logFileStreamOutput.hpp b/src/share/vm/logging/logFileStreamOutput.hpp --- a/src/share/vm/logging/logFileStreamOutput.hpp +++ b/src/share/vm/logging/logFileStreamOutput.hpp @@ -42,8 +42,13 @@ } } + int write_decorations(char* buffer, + size_t buffer_length, + const LogDecorations& decorations); + public: - virtual int write(const LogDecorations &decorations, const char* msg); + virtual int write(const LogDecorations& decorations, const char* msg); + virtual int write(LogMessage::Iterator msg_iterator); }; class LogStdoutOutput : public LogFileStreamOutput { diff --git a/src/share/vm/logging/logMessage.hpp b/src/share/vm/logging/logMessage.hpp new file mode 100644 --- /dev/null +++ b/src/share/vm/logging/logMessage.hpp @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#ifndef SHARE_VM_LOGGING_LOGMESSAGE_HPP +#define SHARE_VM_LOGGING_LOGMESSAGE_HPP + +#include "logging/logDecorations.hpp" +#include "logging/logLevel.hpp" +#include "logging/logPrefix.hpp" +#include "logging/logTagSet.hpp" +#include "logging/logTag.hpp" +#include "memory/allocation.hpp" +#include "runtime/os.hpp" +#include "runtime/thread.hpp" +#include "utilities/debug.hpp" +#include "utilities/ostream.hpp" + +// The LogMessage class represents a multi-part/multi-line message +// that is guaranteed to be sent and written to the log outputs +// in a way that prevents interleaving by other log messages. +// +// The interface of LogMessage is very similar to the Log class, +// with printf functions for each level (trace(), debug(), etc). +// The difference is that these functions will append/write to the +// LogMessage, which only buffers the message-parts until the whole +// message is sent to a log (using Log::write). If TLS has been +// initialized, messages will be resource allocated, otherwise +// they will be C heap allocated. +// +// Example usage: +// +// LogHandle(logging) log; +// if (log.is_debug()) { +// ResourceMark rm; +// LogMessage msg; +// msg.debug("debug message"); +// msg.trace("additional trace information"); +// log.write(msg); +// } +// +// Log outputs on trace level will see both of the messages above, +// and the trace line will immediately follow the debug line. +// They will have identical decorations (apart from level). +// Log outputs on debug level will see the debug message, +// but not the trace message. +// +class LogMessage : public StackObj { + private: + struct LogLine VALUE_OBJ_CLASS_SPEC { + LogLevelType level; + size_t message_offset; + }; + static const size_t InitialLineCapacity = 10; + static const size_t InitialMessageBufferCapacity = 1024; + bool _c_heap_allocated; + + size_t _message_buffer_size; + size_t _message_buffer_capacity; + char* _message_buffer; + + size_t _line_count; + size_t _line_capacity; + LogLine* _lines; + + LogLevelType _least_detailed_level; + + size_t (*_prefix_fn)(char*, size_t); + + template + void grow(T*& buffer, size_t& capacity, size_t minimum_length = 0) { + size_t new_size = capacity * 2; + if (new_size < minimum_length) { + new_size = minimum_length; + } + if (_c_heap_allocated) { + buffer = REALLOC_C_HEAP_ARRAY(T, buffer, new_size, mtLogging); + } else { + buffer = REALLOC_RESOURCE_ARRAY(T, buffer, capacity, new_size); + } + capacity = new_size; + } + + // Forbid copy assignment and copy constructor. + void operator=(const LogMessage& ref) {} + LogMessage(const LogMessage& ref) {} + + public: + + class Iterator { + private: + const LogMessage& _message; + size_t _current_line_index; + LogLevelType _level; + LogDecorations &_decorations; + + void skip_messages_with_finer_level() { + for (; _current_line_index < _message._line_count; _current_line_index++) { + if (_message._lines[_current_line_index].level >= _level) { + break; + } + } + } + + public: + Iterator(const LogMessage& message, LogLevelType level, LogDecorations& decorations) + : _message(message), _level(level), _decorations(decorations), _current_line_index(0) { + skip_messages_with_finer_level(); + } + + void operator++(int) { + _current_line_index++; + skip_messages_with_finer_level(); + } + + bool is_at_end() { + return _current_line_index == _message._line_count; + } + + const char* message() const { + return _message._message_buffer + _message._lines[_current_line_index].message_offset; + } + + const LogDecorations& decorations() { + _decorations.set_level(_message._lines[_current_line_index].level); + return _decorations; + } + }; + + LogMessage() : _message_buffer_size(0), + _message_buffer_capacity(InitialMessageBufferCapacity), + _line_count(0), + _line_capacity(InitialLineCapacity), + _c_heap_allocated(Thread::current_or_null() == NULL), + _prefix_fn(NULL), + _least_detailed_level(LogLevel::Off) { + if (_c_heap_allocated) { + _lines = NEW_C_HEAP_ARRAY(LogLine, _message_buffer_capacity, mtLogging); + _message_buffer = NEW_C_HEAP_ARRAY(char, InitialMessageBufferCapacity, mtLogging); + } else { + _lines = NEW_RESOURCE_ARRAY(LogLine, _message_buffer_capacity); + _message_buffer = NEW_RESOURCE_ARRAY(char, InitialMessageBufferCapacity); + } + } + + ~LogMessage() { + if (_c_heap_allocated) { + FREE_C_HEAP_ARRAY(LogLine, _lines); + FREE_C_HEAP_ARRAY(char, _message_buffer); + } else { + FREE_RESOURCE_ARRAY(LogLine, _lines, _line_capacity); + FREE_RESOURCE_ARRAY(char, _message_buffer, _message_buffer_capacity); + } + } + + LogLevelType least_detailed_level() const { + return _least_detailed_level; + } + + Iterator iterator(LogLevelType level, LogDecorations& decorations) const { + return Iterator(*this, level, decorations); + } + + // LogMessages are not automatically prefixed based on tags like regular + // simple messages (see LogPrefix.hpp for more information about prefixes). + // It is, however, possible to specify a prefix per LogMessage, + // using set_prefix(). Lines added to the LogMessage after a prefix + // function has been set will be prefixed automatically. + // Setting this to NULL will disable prefixing. + void set_prefix(size_t (*prefix_fn)(char*, size_t)) { + _prefix_fn = prefix_fn; + } + + ATTRIBUTE_PRINTF(3, 4) + void write(LogLevelType level, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vwrite(level, fmt, args); + va_end(args); + }; + + ATTRIBUTE_PRINTF(3, 0) + void vwrite(LogLevelType level, const char* fmt, va_list args) { + + if (level > _least_detailed_level) { + _least_detailed_level = level; + } + + size_t written; + for (int attempts = 0; attempts < 2; attempts++) { + written = 0; + size_t remaining_buffer_length = _message_buffer_capacity - _message_buffer_size; + char* current_buffer_position = _message_buffer + _message_buffer_size; + if (_prefix_fn != NULL) { + written += _prefix_fn(current_buffer_position, remaining_buffer_length); + current_buffer_position += written; + if (remaining_buffer_length < written) { + remaining_buffer_length = 0; + } else { + remaining_buffer_length -= written; + } + } + va_list copy; + va_copy(copy, args); + written += (size_t)os::log_vsnprintf(current_buffer_position, remaining_buffer_length, fmt, copy) + 1; + va_end(copy); + if (written > _message_buffer_capacity - _message_buffer_size) { + assert(attempts == 0, "Second attempt should always have a sufficiently large buffer (resized to fit)."); + grow(_message_buffer, _message_buffer_capacity, _message_buffer_size + written); + continue; + } + break; + } + + if (_line_count == _line_capacity) { + grow(_lines, _line_capacity); + } + + _lines[_line_count].level = level; + _lines[_line_count].message_offset = _message_buffer_size; + _message_buffer_size += written; + _line_count++; + } + +#define LOG_LEVEL(level, name) ATTRIBUTE_PRINTF(2, 0) \ + LogMessage& v##name(const char* fmt, va_list args) { \ + vwrite(LogLevel::level, fmt, args); \ + return *this; \ + } \ + LogMessage& 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; \ + } + LOG_LEVEL_LIST +#undef LOG_LEVEL +}; + +#endif // SHARE_VM_LOGGING_LOGMESSAGE_HPP diff --git a/src/share/vm/logging/logOutput.hpp b/src/share/vm/logging/logOutput.hpp --- a/src/share/vm/logging/logOutput.hpp +++ b/src/share/vm/logging/logOutput.hpp @@ -26,10 +26,12 @@ #include "logging/logDecorators.hpp" #include "logging/logLevel.hpp" +#include "logging/logMessage.hpp" #include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" class LogDecorations; +class LogMessage; class LogTagSet; // The base class/interface for log outputs. @@ -83,7 +85,8 @@ virtual const char* name() const = 0; virtual bool initialize(const char* options) = 0; - virtual int write(const LogDecorations &decorations, const char* msg) = 0; + virtual int write(const LogDecorations& decorations, const char* msg) = 0; + virtual int write(LogMessage::Iterator msg_iterator) = 0; }; #endif // SHARE_VM_LOGGING_LOGOUTPUT_HPP diff --git a/src/share/vm/logging/logOutputList.hpp b/src/share/vm/logging/logOutputList.hpp --- a/src/share/vm/logging/logOutputList.hpp +++ b/src/share/vm/logging/logOutputList.hpp @@ -113,6 +113,10 @@ bool operator!=(const LogOutputNode *ref) const { return _current != ref; } + + LogLevelType level() const { + return _current->_level; + } }; Iterator iterator(LogLevelType level = LogLevel::Last) { diff --git a/src/share/vm/logging/logPrefix.hpp b/src/share/vm/logging/logPrefix.hpp --- a/src/share/vm/logging/logPrefix.hpp +++ b/src/share/vm/logging/logPrefix.hpp @@ -86,7 +86,11 @@ static size_t prefix(char* buf, size_t len) { \ DEBUG_ONLY(buf[0] = '\0';) \ size_t ret = fn(buf, len); \ - assert(ret == strlen(buf), "Length mismatch ret (" SIZE_FORMAT ") != buf length (" SIZE_FORMAT ")", ret, strlen(buf)); \ + /* Either prefix did fit (strlen(buf) == ret && ret < len) */ \ + /* or the prefix didn't fit in buffer (ret > len && strlen(buf) < len) */ \ + assert(strlen(buf) < len, "Buffer overrun by prefix function."); \ + assert(strlen(buf) == ret || ret >= len, "Prefix function should return length of prefix written," \ + " or the intended length of prefix if the buffer was too small."); \ return ret; \ } \ }; diff --git a/src/share/vm/logging/logTagSet.cpp b/src/share/vm/logging/logTagSet.cpp --- a/src/share/vm/logging/logTagSet.cpp +++ b/src/share/vm/logging/logTagSet.cpp @@ -24,6 +24,7 @@ #include "precompiled.hpp" #include "logging/logDecorations.hpp" #include "logging/logLevel.hpp" +#include "logging/logMessage.hpp" #include "logging/logOutput.hpp" #include "logging/logTag.hpp" #include "logging/logTagSet.hpp" @@ -77,6 +78,13 @@ } } +void LogTagSet::log(const LogMessage& msg) { + LogDecorations decorations(LogLevel::Invalid, *this, _decorators); + for (LogOutputList::Iterator it = _output_list.iterator(msg.least_detailed_level()); it != _output_list.end(); it++) { + (*it)->write(msg.iterator(it.level(), decorations)); + } +} + int LogTagSet::label(char* buf, size_t len, const char* separator) const { int tot_written = 0; for (size_t i = 0; i < _ntags; i++) { diff --git a/src/share/vm/logging/logTagSet.hpp b/src/share/vm/logging/logTagSet.hpp --- a/src/share/vm/logging/logTagSet.hpp +++ b/src/share/vm/logging/logTagSet.hpp @@ -30,6 +30,8 @@ #include "logging/logTag.hpp" #include "utilities/globalDefinitions.hpp" +class LogMessage; + // 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. @@ -93,6 +95,7 @@ bool has_output(const LogOutput* output); bool is_level(LogLevelType level) const; void log(LogLevelType level, const char* msg); + void log(const LogMessage& msg); }; template ::tagset().is_level(level); } - static void write(const LogMessage& msg) { + static void write(const LogMessageBuffer& msg) { LogTagSetMapping::tagset().log(msg); }; diff --git a/src/share/vm/logging/logFileOutput.cpp b/src/share/vm/logging/logFileOutput.cpp --- a/src/share/vm/logging/logFileOutput.cpp +++ b/src/share/vm/logging/logFileOutput.cpp @@ -163,7 +163,7 @@ return written; } -int LogFileOutput::write(LogMessage::Iterator msg_iterator) { +int LogFileOutput::write(LogMessageBuffer::Iterator msg_iterator) { if (_stream == NULL) { // An error has occurred with this output, avoid writing to it. return 0; diff --git a/src/share/vm/logging/logFileOutput.hpp b/src/share/vm/logging/logFileOutput.hpp --- a/src/share/vm/logging/logFileOutput.hpp +++ b/src/share/vm/logging/logFileOutput.hpp @@ -74,7 +74,7 @@ virtual ~LogFileOutput(); virtual bool initialize(const char* options); virtual int write(const LogDecorations& decorations, const char* msg); - virtual int write(LogMessage::Iterator msg_iterator); + virtual int write(LogMessageBuffer::Iterator msg_iterator); virtual void force_rotate(); virtual const char* name() const { diff --git a/src/share/vm/logging/logFileStreamOutput.cpp b/src/share/vm/logging/logFileStreamOutput.cpp --- a/src/share/vm/logging/logFileStreamOutput.cpp +++ b/src/share/vm/logging/logFileStreamOutput.cpp @@ -25,7 +25,7 @@ #include "logging/logDecorators.hpp" #include "logging/logDecorations.hpp" #include "logging/logFileStreamOutput.hpp" -#include "logging/logMessage.hpp" +#include "logging/logMessageBuffer.hpp" #include "memory/allocation.inline.hpp" LogStdoutOutput LogStdoutOutput::_instance; @@ -72,7 +72,7 @@ return written; } -int LogFileStreamOutput::write(LogMessage::Iterator msg_iterator) { +int LogFileStreamOutput::write(LogMessageBuffer::Iterator msg_iterator) { const bool use_decorations = !_decorators.is_empty(); char decoration_buf[LogDecorations::DecorationsBufferSize]; diff --git a/src/share/vm/logging/logFileStreamOutput.hpp b/src/share/vm/logging/logFileStreamOutput.hpp --- a/src/share/vm/logging/logFileStreamOutput.hpp +++ b/src/share/vm/logging/logFileStreamOutput.hpp @@ -48,7 +48,7 @@ public: virtual int write(const LogDecorations& decorations, const char* msg); - virtual int write(LogMessage::Iterator msg_iterator); + virtual int write(LogMessageBuffer::Iterator msg_iterator); }; class LogStdoutOutput : public LogFileStreamOutput { diff --git a/src/share/vm/logging/logMessage.hpp b/src/share/vm/logging/logMessage.hpp --- a/src/share/vm/logging/logMessage.hpp +++ b/src/share/vm/logging/logMessage.hpp @@ -24,16 +24,10 @@ #ifndef SHARE_VM_LOGGING_LOGMESSAGE_HPP #define SHARE_VM_LOGGING_LOGMESSAGE_HPP -#include "logging/logDecorations.hpp" -#include "logging/logLevel.hpp" +#include "logging/log.hpp" +#include "logging/logMessageBuffer.hpp" #include "logging/logPrefix.hpp" -#include "logging/logTagSet.hpp" #include "logging/logTag.hpp" -#include "memory/allocation.hpp" -#include "runtime/os.hpp" -#include "runtime/thread.hpp" -#include "utilities/debug.hpp" -#include "utilities/ostream.hpp" // The LogMessage class represents a multi-part/multi-line message // that is guaranteed to be sent and written to the log outputs @@ -49,13 +43,13 @@ // // Example usage: // -// LogHandle(logging) log; -// if (log.is_debug()) { -// ResourceMark rm; -// LogMessage msg; -// msg.debug("debug message"); -// msg.trace("additional trace information"); -// log.write(msg); +// { +// LogMessage(logging) msg; +// if (msg.is_debug()) { +// ResourceMark rm; +// msg.debug("debug message"); +// msg.trace("additional trace information"); +// } // } // // Log outputs on trace level will see both of the messages above, @@ -64,194 +58,27 @@ // Log outputs on debug level will see the debug message, // but not the trace message. // -class LogMessage : public StackObj { +#define LogMessage(...) ScopedLogMessage +template +class ScopedLogMessage : public LogMessageBuffer { private: - struct LogLine VALUE_OBJ_CLASS_SPEC { - LogLevelType level; - size_t message_offset; - }; - static const size_t InitialLineCapacity = 10; - static const size_t InitialMessageBufferCapacity = 1024; - bool _c_heap_allocated; + Log _log; - size_t _message_buffer_size; - size_t _message_buffer_capacity; - char* _message_buffer; - - size_t _line_count; - size_t _line_capacity; - LogLine* _lines; - - LogLevelType _least_detailed_level; - - size_t (*_prefix_fn)(char*, size_t); - - template - void grow(T*& buffer, size_t& capacity, size_t minimum_length = 0) { - size_t new_size = capacity * 2; - if (new_size < minimum_length) { - new_size = minimum_length; - } - if (_c_heap_allocated) { - buffer = REALLOC_C_HEAP_ARRAY(T, buffer, new_size, mtLogging); - } else { - buffer = REALLOC_RESOURCE_ARRAY(T, buffer, capacity, new_size); - } - capacity = new_size; + public: + ScopedLogMessage() { + set_prefix(LogPrefix::prefix); } - // Forbid copy assignment and copy constructor. - void operator=(const LogMessage& ref) {} - LogMessage(const LogMessage& ref) {} - - public: - - class Iterator { - private: - const LogMessage& _message; - size_t _current_line_index; - LogLevelType _level; - LogDecorations &_decorations; - - void skip_messages_with_finer_level() { - for (; _current_line_index < _message._line_count; _current_line_index++) { - if (_message._lines[_current_line_index].level >= _level) { - break; - } - } - } - - public: - Iterator(const LogMessage& message, LogLevelType level, LogDecorations& decorations) - : _message(message), _level(level), _decorations(decorations), _current_line_index(0) { - skip_messages_with_finer_level(); - } - - void operator++(int) { - _current_line_index++; - skip_messages_with_finer_level(); - } - - bool is_at_end() { - return _current_line_index == _message._line_count; - } - - const char* message() const { - return _message._message_buffer + _message._lines[_current_line_index].message_offset; - } - - const LogDecorations& decorations() { - _decorations.set_level(_message._lines[_current_line_index].level); - return _decorations; - } - }; - - LogMessage() : _message_buffer_size(0), - _message_buffer_capacity(InitialMessageBufferCapacity), - _line_count(0), - _line_capacity(InitialLineCapacity), - _c_heap_allocated(Thread::current_or_null() == NULL), - _prefix_fn(NULL), - _least_detailed_level(LogLevel::Off) { - if (_c_heap_allocated) { - _lines = NEW_C_HEAP_ARRAY(LogLine, _message_buffer_capacity, mtLogging); - _message_buffer = NEW_C_HEAP_ARRAY(char, InitialMessageBufferCapacity, mtLogging); - } else { - _lines = NEW_RESOURCE_ARRAY(LogLine, _message_buffer_capacity); - _message_buffer = NEW_RESOURCE_ARRAY(char, InitialMessageBufferCapacity); + ~ScopedLogMessage() { + if (_line_count > 0) { + _log.write(*this); } } - ~LogMessage() { - if (_c_heap_allocated) { - FREE_C_HEAP_ARRAY(LogLine, _lines); - FREE_C_HEAP_ARRAY(char, _message_buffer); - } else { - FREE_RESOURCE_ARRAY(LogLine, _lines, _line_capacity); - FREE_RESOURCE_ARRAY(char, _message_buffer, _message_buffer_capacity); - } - } - - LogLevelType least_detailed_level() const { - return _least_detailed_level; - } - - Iterator iterator(LogLevelType level, LogDecorations& decorations) const { - return Iterator(*this, level, decorations); - } - - // LogMessages are not automatically prefixed based on tags like regular - // simple messages (see LogPrefix.hpp for more information about prefixes). - // It is, however, possible to specify a prefix per LogMessage, - // using set_prefix(). Lines added to the LogMessage after a prefix - // function has been set will be prefixed automatically. - // Setting this to NULL will disable prefixing. - void set_prefix(size_t (*prefix_fn)(char*, size_t)) { - _prefix_fn = prefix_fn; - } - - ATTRIBUTE_PRINTF(3, 4) - void write(LogLevelType level, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - vwrite(level, fmt, args); - va_end(args); - }; - - ATTRIBUTE_PRINTF(3, 0) - void vwrite(LogLevelType level, const char* fmt, va_list args) { - - if (level > _least_detailed_level) { - _least_detailed_level = level; - } - - size_t written; - for (int attempts = 0; attempts < 2; attempts++) { - written = 0; - size_t remaining_buffer_length = _message_buffer_capacity - _message_buffer_size; - char* current_buffer_position = _message_buffer + _message_buffer_size; - if (_prefix_fn != NULL) { - written += _prefix_fn(current_buffer_position, remaining_buffer_length); - current_buffer_position += written; - if (remaining_buffer_length < written) { - remaining_buffer_length = 0; - } else { - remaining_buffer_length -= written; - } - } - va_list copy; - va_copy(copy, args); - written += (size_t)os::log_vsnprintf(current_buffer_position, remaining_buffer_length, fmt, copy) + 1; - va_end(copy); - if (written > _message_buffer_capacity - _message_buffer_size) { - assert(attempts == 0, "Second attempt should always have a sufficiently large buffer (resized to fit)."); - grow(_message_buffer, _message_buffer_capacity, _message_buffer_size + written); - continue; - } - break; - } - - if (_line_count == _line_capacity) { - grow(_lines, _line_capacity); - } - - _lines[_line_count].level = level; - _lines[_line_count].message_offset = _message_buffer_size; - _message_buffer_size += written; - _line_count++; - } - -#define LOG_LEVEL(level, name) ATTRIBUTE_PRINTF(2, 0) \ - LogMessage& v##name(const char* fmt, va_list args) { \ - vwrite(LogLevel::level, fmt, args); \ - return *this; \ - } \ - LogMessage& 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; \ +#define LOG_LEVEL(level, name) \ + bool is_##name() const { \ + return _log.is_level(LogLevel::level); \ } LOG_LEVEL_LIST #undef LOG_LEVEL diff --git a/src/share/vm/logging/logMessageBuffer.cpp b/src/share/vm/logging/logMessageBuffer.cpp new file mode 100644 --- /dev/null +++ b/src/share/vm/logging/logMessageBuffer.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#include "precompiled.hpp" +#include "logging/logMessageBuffer.hpp" +#include "memory/allocation.inline.hpp" +#include "runtime/thread.inline.hpp" + +template +static void grow(T*& buffer, size_t& capacity, bool c_heap, size_t minimum_length = 0) { + size_t new_size = capacity * 2; + if (new_size < minimum_length) { + new_size = minimum_length; + } + if (c_heap) { + buffer = REALLOC_C_HEAP_ARRAY(T, buffer, new_size, mtLogging); + } else { + buffer = REALLOC_RESOURCE_ARRAY(T, buffer, capacity, new_size); + } + capacity = new_size; +} + +LogMessageBuffer::LogMessageBuffer() : _message_buffer_size(0), + _message_buffer_capacity(0), + _message_buffer(NULL), + _line_count(0), + _line_capacity(0), + _lines(NULL), + _c_heap_allocated(Thread::current_or_null() == NULL), + _least_detailed_level(LogLevel::Off), + _prefix_fn(NULL) { +} + +LogMessageBuffer::~LogMessageBuffer() { + if (_c_heap_allocated) { + FREE_C_HEAP_ARRAY(char, _message_buffer); + FREE_C_HEAP_ARRAY(LogLine, _lines); + } else { + FREE_RESOURCE_ARRAY(char, _message_buffer, _message_buffer_capacity); + FREE_RESOURCE_ARRAY(LogLine, _lines, _line_capacity); + } +} + +void LogMessageBuffer::clear() { + _message_buffer_size = 0; + _line_count = 0; +} + +void LogMessageBuffer::initialize_buffers() { + if (_c_heap_allocated) { + _message_buffer = NEW_C_HEAP_ARRAY(char, InitialMessageBufferCapacity, mtLogging); + _lines = NEW_C_HEAP_ARRAY(LogLine, InitialLineCapacity, mtLogging); + } else { + _message_buffer = NEW_RESOURCE_ARRAY(char, InitialMessageBufferCapacity); + _lines = NEW_RESOURCE_ARRAY(LogLine, InitialLineCapacity); + } + _message_buffer_capacity = InitialMessageBufferCapacity; + _line_capacity = InitialLineCapacity; +} + +void LogMessageBuffer::Iterator::skip_messages_with_finer_level() { + for (; _current_line_index < _message._line_count; _current_line_index++) { + if (_message._lines[_current_line_index].level >= _level) { + break; + } + } +} + +void LogMessageBuffer::write(LogLevelType level, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + vwrite(level, fmt, args); + va_end(args); +}; + +void LogMessageBuffer::vwrite(LogLevelType level, const char* fmt, va_list args) { + if (_message_buffer_capacity == 0) { + initialize_buffers(); + } + + if (level > _least_detailed_level) { + _least_detailed_level = level; + } + + size_t written; + for (int attempts = 0; attempts < 2; attempts++) { + written = 0; + size_t remaining_buffer_length = _message_buffer_capacity - _message_buffer_size; + char* current_buffer_position = _message_buffer + _message_buffer_size; + + if (_prefix_fn != NULL) { + written += _prefix_fn(current_buffer_position, remaining_buffer_length); + current_buffer_position += written; + if (remaining_buffer_length < written) { + remaining_buffer_length = 0; + } else { + remaining_buffer_length -= written; + } + } + + va_list copy; + va_copy(copy, args); + written += (size_t)os::log_vsnprintf(current_buffer_position, remaining_buffer_length, fmt, copy) + 1; + va_end(copy); + if (written > _message_buffer_capacity - _message_buffer_size) { + assert(attempts == 0, "Second attempt should always have a sufficiently large buffer (resized to fit)."); + grow(_message_buffer, _message_buffer_capacity, _c_heap_allocated, _message_buffer_size + written); + continue; + } + break; + } + + if (_line_count == _line_capacity) { + grow(_lines, _line_capacity, _c_heap_allocated); + } + + _lines[_line_count].level = level; + _lines[_line_count].message_offset = _message_buffer_size; + _message_buffer_size += written; + _line_count++; +} + +#define LOG_LEVEL(level, name) \ +LogMessageBuffer& LogMessageBuffer::v##name(const char* fmt, va_list args) { \ + vwrite(LogLevel::level, fmt, args); \ + return *this; \ +} \ +LogMessageBuffer& LogMessageBuffer::name(const char* fmt, ...) { \ + va_list args; \ + va_start(args, fmt); \ + vwrite(LogLevel::level, fmt, args); \ + va_end(args); \ + return *this; \ +} +LOG_LEVEL_LIST +#undef LOG_LEVEL diff --git a/src/share/vm/logging/logMessageBuffer.hpp b/src/share/vm/logging/logMessageBuffer.hpp new file mode 100644 --- /dev/null +++ b/src/share/vm/logging/logMessageBuffer.hpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ +#ifndef SHARE_VM_LOGGING_LOGMESSAGEBUFFER_HPP +#define SHARE_VM_LOGGING_LOGMESSAGEBUFFER_HPP + +#include "logging/logDecorations.hpp" +#include "logging/logLevel.hpp" +#include "memory/allocation.hpp" + +class LogMessageBuffer : public StackObj { + friend class LogMessageTest; + protected: + struct LogLine VALUE_OBJ_CLASS_SPEC { + LogLevelType level; + size_t message_offset; + }; + static const size_t InitialLineCapacity = 10; + static const size_t InitialMessageBufferCapacity = 1024; + bool _c_heap_allocated; + + size_t _message_buffer_size; + size_t _message_buffer_capacity; + char* _message_buffer; + + size_t _line_count; + size_t _line_capacity; + LogLine* _lines; + + LogLevelType _least_detailed_level; + + size_t (*_prefix_fn)(char*, size_t); + + void initialize_buffers(); + + LogMessageBuffer(); + ~LogMessageBuffer(); + + private: + // Forbid copy assignment and copy constructor. + void operator=(const LogMessageBuffer& ref) {} + LogMessageBuffer(const LogMessageBuffer& ref) {} + + public: + + class Iterator { + private: + const LogMessageBuffer& _message; + size_t _current_line_index; + LogLevelType _level; + LogDecorations &_decorations; + + void skip_messages_with_finer_level(); + + public: + Iterator(const LogMessageBuffer& message, LogLevelType level, LogDecorations& decorations) + : _message(message), _level(level), _decorations(decorations), _current_line_index(0) { + skip_messages_with_finer_level(); + } + + void operator++(int) { + _current_line_index++; + skip_messages_with_finer_level(); + } + + bool is_at_end() { + return _current_line_index == _message._line_count; + } + + const char* message() const { + return _message._message_buffer + _message._lines[_current_line_index].message_offset; + } + + const LogDecorations& decorations() { + _decorations.set_level(_message._lines[_current_line_index].level); + return _decorations; + } + }; + + void clear(); + + LogLevelType least_detailed_level() const { + return _least_detailed_level; + } + + Iterator iterator(LogLevelType level, LogDecorations& decorations) const { + return Iterator(*this, level, decorations); + } + + // Lines in LogMessageBuffers are not automatically prefixed based on tags + // like regular simple messages (see LogPrefix.hpp for more about prefixes). + // It is, however, possible to specify a prefix per LogMessageBuffer, + // using set_prefix(). Lines added to the LogMessageBuffer after a prefix + // function has been set will be prefixed automatically. + // Setting this to NULL will disable prefixing. + void set_prefix(size_t (*prefix_fn)(char*, size_t)) { + _prefix_fn = prefix_fn; + } + + ATTRIBUTE_PRINTF(3, 4) + void write(LogLevelType level, const char* fmt, ...); + + ATTRIBUTE_PRINTF(3, 0) + void vwrite(LogLevelType level, const char* fmt, va_list args); + +#define LOG_LEVEL(level, name) \ + LogMessageBuffer& v##name(const char* fmt, va_list args) ATTRIBUTE_PRINTF(2, 0); \ + LogMessageBuffer& name(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3); + LOG_LEVEL_LIST +#undef LOG_LEVEL +}; + +#endif // SHARE_VM_LOGGING_LOGMESSAGEBUFFER_HPP diff --git a/src/share/vm/logging/logOutput.hpp b/src/share/vm/logging/logOutput.hpp --- a/src/share/vm/logging/logOutput.hpp +++ b/src/share/vm/logging/logOutput.hpp @@ -26,12 +26,12 @@ #include "logging/logDecorators.hpp" #include "logging/logLevel.hpp" -#include "logging/logMessage.hpp" +#include "logging/logMessageBuffer.hpp" #include "memory/allocation.hpp" #include "utilities/globalDefinitions.hpp" class LogDecorations; -class LogMessage; +class LogMessageBuffer; class LogTagSet; // The base class/interface for log outputs. @@ -86,7 +86,7 @@ virtual const char* name() const = 0; virtual bool initialize(const char* options) = 0; virtual int write(const LogDecorations& decorations, const char* msg) = 0; - virtual int write(LogMessage::Iterator msg_iterator) = 0; + virtual int write(LogMessageBuffer::Iterator msg_iterator) = 0; }; #endif // SHARE_VM_LOGGING_LOGOUTPUT_HPP diff --git a/src/share/vm/logging/logTagSet.cpp b/src/share/vm/logging/logTagSet.cpp --- a/src/share/vm/logging/logTagSet.cpp +++ b/src/share/vm/logging/logTagSet.cpp @@ -24,7 +24,7 @@ #include "precompiled.hpp" #include "logging/logDecorations.hpp" #include "logging/logLevel.hpp" -#include "logging/logMessage.hpp" +#include "logging/logMessageBuffer.hpp" #include "logging/logOutput.hpp" #include "logging/logTag.hpp" #include "logging/logTagSet.hpp" @@ -78,7 +78,7 @@ } } -void LogTagSet::log(const LogMessage& msg) { +void LogTagSet::log(const LogMessageBuffer& msg) { LogDecorations decorations(LogLevel::Invalid, *this, _decorators); for (LogOutputList::Iterator it = _output_list.iterator(msg.least_detailed_level()); it != _output_list.end(); it++) { (*it)->write(msg.iterator(it.level(), decorations)); diff --git a/src/share/vm/logging/logTagSet.hpp b/src/share/vm/logging/logTagSet.hpp --- a/src/share/vm/logging/logTagSet.hpp +++ b/src/share/vm/logging/logTagSet.hpp @@ -30,7 +30,7 @@ #include "logging/logTag.hpp" #include "utilities/globalDefinitions.hpp" -class LogMessage; +class LogMessageBuffer; // 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 @@ -95,7 +95,7 @@ bool has_output(const LogOutput* output); bool is_level(LogLevelType level) const; void log(LogLevelType level, const char* msg); - void log(const LogMessage& msg); + void log(const LogMessageBuffer& msg); }; template