1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 #ifndef SHARE_VM_LOGGING_LOGMESSAGE_HPP
  25 #define SHARE_VM_LOGGING_LOGMESSAGE_HPP
  26 
  27 #include "logging/logDecorations.hpp"
  28 #include "logging/logLevel.hpp"
  29 #include "logging/logPrefix.hpp"
  30 #include "logging/logTagSet.hpp"
  31 #include "logging/logTag.hpp"
  32 #include "memory/allocation.hpp"
  33 #include "runtime/os.hpp"
  34 #include "runtime/thread.hpp"
  35 #include "utilities/debug.hpp"
  36 #include "utilities/ostream.hpp"
  37 
  38 // The LogMessage class represents a multi-part/multi-line message
  39 // that is guaranteed to be sent and written to the log outputs
  40 // in a way that prevents interleaving by other log messages.
  41 //
  42 // The interface of LogMessage is very similar to the Log class,
  43 // with printf functions for each level (trace(), debug(), etc).
  44 // The difference is that these functions will append/write to the
  45 // LogMessage, which only buffers the message-parts until the whole
  46 // message is sent to a log (using Log::write). If TLS has been
  47 // initialized, messages will be resource allocated, otherwise
  48 // they will be C heap allocated.
  49 //
  50 // Example usage:
  51 //
  52 // LogHandle(logging) log;
  53 // if (log.is_debug()) {
  54 //   ResourceMark rm;
  55 //   LogMessage msg;
  56 //   msg.debug("debug message");
  57 //   msg.trace("additional trace information");
  58 //   log.write(msg);
  59 // }
  60 //
  61 // Log outputs on trace level will see both of the messages above,
  62 // and the trace line will immediately follow the debug line.
  63 // They will have identical decorations (apart from level).
  64 // Log outputs on debug level will see the debug message,
  65 // but not the trace message.
  66 //
  67 class LogMessage : public StackObj {
  68  private:
  69   struct LogLine VALUE_OBJ_CLASS_SPEC {
  70     LogLevelType level;
  71     size_t message_offset;
  72   };
  73   static const size_t InitialLineCapacity = 10;
  74   static const size_t InitialMessageBufferCapacity = 1024;
  75   bool _c_heap_allocated;
  76 
  77   size_t _message_buffer_size;
  78   size_t _message_buffer_capacity;
  79   char* _message_buffer;
  80 
  81   size_t _line_count;
  82   size_t _line_capacity;
  83   LogLine* _lines;
  84 
  85   LogLevelType _least_detailed_level;
  86 
  87   size_t (*_prefix_fn)(char*, size_t);
  88 
  89   template <typename T>
  90   void grow(T*& buffer, size_t& capacity, size_t minimum_length = 0) {
  91     size_t new_size = capacity * 2;
  92     if (new_size < minimum_length) {
  93       new_size = minimum_length;
  94     }
  95     if (_c_heap_allocated) {
  96       buffer = REALLOC_C_HEAP_ARRAY(T, buffer, new_size, mtLogging);
  97     } else {
  98       buffer = REALLOC_RESOURCE_ARRAY(T, buffer, capacity, new_size);
  99     }
 100     capacity = new_size;
 101   }
 102 
 103   // Forbid copy assignment and copy constructor.
 104   void operator=(const LogMessage& ref) {}
 105   LogMessage(const LogMessage& ref) {}
 106 
 107  public:
 108 
 109   class Iterator {
 110    private:
 111     const LogMessage& _message;
 112     size_t _current_line_index;
 113     LogLevelType _level;
 114     LogDecorations &_decorations;
 115 
 116     void skip_messages_with_finer_level() {
 117       for (; _current_line_index < _message._line_count; _current_line_index++) {
 118         if (_message._lines[_current_line_index].level >= _level) {
 119           break;
 120         }
 121       }
 122     }
 123 
 124    public:
 125     Iterator(const LogMessage& message, LogLevelType level, LogDecorations& decorations)
 126         : _message(message), _level(level), _decorations(decorations), _current_line_index(0) {
 127       skip_messages_with_finer_level();
 128     }
 129 
 130     void operator++(int) {
 131       _current_line_index++;
 132       skip_messages_with_finer_level();
 133     }
 134 
 135     bool is_at_end() {
 136       return _current_line_index == _message._line_count;
 137     }
 138 
 139     const char* message() const {
 140       return _message._message_buffer + _message._lines[_current_line_index].message_offset;
 141     }
 142 
 143     const LogDecorations& decorations() {
 144       _decorations.set_level(_message._lines[_current_line_index].level);
 145       return _decorations;
 146     }
 147   };
 148 
 149   LogMessage() : _message_buffer_size(0),
 150                  _message_buffer_capacity(InitialMessageBufferCapacity),
 151                  _line_count(0),
 152                  _line_capacity(InitialLineCapacity),
 153                  _c_heap_allocated(Thread::current_or_null() == NULL),
 154                  _prefix_fn(NULL),
 155                  _least_detailed_level(LogLevel::Off) {
 156     if (_c_heap_allocated) {
 157       _lines = NEW_C_HEAP_ARRAY(LogLine, _message_buffer_capacity, mtLogging);
 158       _message_buffer = NEW_C_HEAP_ARRAY(char, InitialMessageBufferCapacity, mtLogging);
 159     } else {
 160       _lines = NEW_RESOURCE_ARRAY(LogLine, _message_buffer_capacity);
 161       _message_buffer = NEW_RESOURCE_ARRAY(char, InitialMessageBufferCapacity);
 162     }
 163   }
 164 
 165   ~LogMessage() {
 166     if (_c_heap_allocated) {
 167       FREE_C_HEAP_ARRAY(LogLine, _lines);
 168       FREE_C_HEAP_ARRAY(char, _message_buffer);
 169     } else {
 170       FREE_RESOURCE_ARRAY(LogLine, _lines, _line_capacity);
 171       FREE_RESOURCE_ARRAY(char, _message_buffer, _message_buffer_capacity);
 172     }
 173   }
 174 
 175   LogLevelType least_detailed_level() const {
 176     return _least_detailed_level;
 177   }
 178 
 179   Iterator iterator(LogLevelType level, LogDecorations& decorations) const {
 180     return Iterator(*this, level, decorations);
 181   }
 182 
 183   // LogMessages are not automatically prefixed based on tags like regular
 184   // simple messages (see LogPrefix.hpp for more information about prefixes).
 185   // It is, however, possible to specify a prefix per LogMessage,
 186   // using set_prefix(). Lines added to the LogMessage after a prefix
 187   // function has been set will be prefixed automatically.
 188   // Setting this to NULL will disable prefixing.
 189   void set_prefix(size_t (*prefix_fn)(char*, size_t)) {
 190     _prefix_fn = prefix_fn;
 191   }
 192 
 193   ATTRIBUTE_PRINTF(3, 4)
 194   void write(LogLevelType level, const char* fmt, ...) {
 195     va_list args;
 196     va_start(args, fmt);
 197     vwrite(level, fmt, args);
 198     va_end(args);
 199   };
 200 
 201   ATTRIBUTE_PRINTF(3, 0)
 202   void vwrite(LogLevelType level, const char* fmt, va_list args) {
 203 
 204     if (level > _least_detailed_level) {
 205       _least_detailed_level = level;
 206     }
 207 
 208     size_t written;
 209     for (int attempts = 0; attempts < 2; attempts++) {
 210       written = 0;
 211       size_t remaining_buffer_length = _message_buffer_capacity - _message_buffer_size;
 212       char* current_buffer_position = _message_buffer + _message_buffer_size;
 213       if (_prefix_fn != NULL) {
 214         written += _prefix_fn(current_buffer_position, remaining_buffer_length);
 215         current_buffer_position += written;
 216         if (remaining_buffer_length < written) {
 217           remaining_buffer_length = 0;
 218         } else {
 219           remaining_buffer_length -= written;
 220         }
 221       }
 222       va_list copy;
 223       va_copy(copy, args);
 224       written += (size_t)os::log_vsnprintf(current_buffer_position, remaining_buffer_length, fmt, copy) + 1;
 225       va_end(copy);
 226       if (written > _message_buffer_capacity - _message_buffer_size) {
 227         assert(attempts == 0, "Second attempt should always have a sufficiently large buffer (resized to fit).");
 228         grow(_message_buffer, _message_buffer_capacity, _message_buffer_size + written);
 229         continue;
 230       }
 231       break;
 232     }
 233 
 234     if (_line_count == _line_capacity) {
 235       grow(_lines, _line_capacity);
 236     }
 237 
 238     _lines[_line_count].level = level;
 239     _lines[_line_count].message_offset = _message_buffer_size;
 240     _message_buffer_size += written;
 241     _line_count++;
 242   }
 243 
 244 #define LOG_LEVEL(level, name) ATTRIBUTE_PRINTF(2, 0) \
 245   LogMessage& v##name(const char* fmt, va_list args) { \
 246     vwrite(LogLevel::level, fmt, args); \
 247     return *this; \
 248   } \
 249   LogMessage& name(const char* fmt, ...) ATTRIBUTE_PRINTF(2, 3) { \
 250     va_list args; \
 251     va_start(args, fmt); \
 252     vwrite(LogLevel::level, fmt, args); \
 253     va_end(args); \
 254     return *this; \
 255   }
 256   LOG_LEVEL_LIST
 257 #undef LOG_LEVEL
 258 };
 259 
 260 #endif // SHARE_VM_LOGGING_LOGMESSAGE_HPP