 Chromium Code Reviews
 Chromium Code Reviews Issue 115814:
  Implement a dynamically growing memory log buffer with an upper limit.  (Closed)
    
  
    Issue 115814:
  Implement a dynamically growing memory log buffer with an upper limit.  (Closed) 
  | Index: src/log-utils.cc | 
| diff --git a/src/log-utils.cc b/src/log-utils.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..0ccd4f2d6efcc0c552b0d05420c801836f9891c6 | 
| --- /dev/null | 
| +++ b/src/log-utils.cc | 
| @@ -0,0 +1,265 @@ | 
| +// Copyright 2009 the V8 project authors. All rights reserved. | 
| +// Redistribution and use in source and binary forms, with or without | 
| +// modification, are permitted provided that the following conditions are | 
| +// met: | 
| +// | 
| +// * Redistributions of source code must retain the above copyright | 
| +// notice, this list of conditions and the following disclaimer. | 
| +// * Redistributions in binary form must reproduce the above | 
| +// copyright notice, this list of conditions and the following | 
| +// disclaimer in the documentation and/or other materials provided | 
| +// with the distribution. | 
| +// * Neither the name of Google Inc. nor the names of its | 
| +// contributors may be used to endorse or promote products derived | 
| +// from this software without specific prior written permission. | 
| +// | 
| +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
| +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
| +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
| +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
| +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
| +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
| +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| + | 
| +#include "v8.h" | 
| + | 
| +#include "log-utils.h" | 
| + | 
| +namespace v8 { | 
| +namespace internal { | 
| + | 
| +#ifdef ENABLE_LOGGING_AND_PROFILING | 
| + | 
| +LogDynamicBuffer::LogDynamicBuffer(int block_size, int max_size) | 
| + : block_size_(block_size), | 
| + max_size_(max_size - (max_size % block_size_)), | 
| + blocks_(max_size_ / block_size_ + 1), | 
| + write_pos_(0), block_index_(0), block_write_pos_(0) { | 
| + ASSERT(BlocksCount() > 0); | 
| + blocks_[0] = NewArray<char>(block_size_); | 
| 
Søren Thygesen Gjesse
2009/05/27 12:00:40
Maybe add a private method AllocateBlock.
 
Mikhail Naganov
2009/05/28 07:05:35
Done.
 | 
| + for (int i = 1; i < BlocksCount(); ++i) { | 
| + blocks_[i] = NULL; | 
| + } | 
| +} | 
| + | 
| + | 
| +LogDynamicBuffer::~LogDynamicBuffer() { | 
| + for (int i = 0; i < BlocksCount(); ++i) { | 
| + DeleteArray(blocks_[i]); | 
| + } | 
| +} | 
| + | 
| + | 
| +int LogDynamicBuffer::Read(int from_pos, char* dest_buf, int buf_size) { | 
| + if (buf_size == 0) return 0; | 
| + int read_pos = from_pos; | 
| + int block_read_index = BlockIndex(from_pos); | 
| + int block_read_pos = PosInBlock(from_pos); | 
| + int dest_buf_pos = 0; | 
| + // Read until dest_buf is filled, or write_pos_ encountered. | 
| + while (read_pos < write_pos_ && dest_buf_pos < buf_size) { | 
| + const int read_size = Min(write_pos_ - read_pos, | 
| + Min(buf_size - dest_buf_pos, block_size_ - block_read_pos)); | 
| + memcpy(dest_buf + dest_buf_pos, | 
| + blocks_[block_read_index] + block_read_pos, read_size); | 
| + block_read_pos += read_size; | 
| + dest_buf_pos += read_size; | 
| + read_pos += read_size; | 
| + if (block_read_pos == block_size_) { | 
| + block_read_pos = 0; | 
| + ++block_read_index; | 
| + } | 
| + } | 
| + return dest_buf_pos; | 
| +} | 
| + | 
| + | 
| +int LogDynamicBuffer::Write(const char* data, int data_size) { | 
| + if ((write_pos_ + data_size) > max_size_) return 0; | 
| + int data_pos = 0; | 
| + while (data_pos < data_size) { | 
| + const int write_size = | 
| + Min(data_size - data_pos, block_size_ - block_write_pos_); | 
| + memcpy(blocks_[block_index_] + block_write_pos_, data + data_pos, | 
| + write_size); | 
| + block_write_pos_ += write_size; | 
| + data_pos += write_size; | 
| + if (block_write_pos_ == block_size_) { | 
| + block_write_pos_ = 0; | 
| + blocks_[++block_index_] = NewArray<char>(block_size_); | 
| 
Søren Thygesen Gjesse
2009/05/27 12:00:40
Ditto.
 
Mikhail Naganov
2009/05/28 07:05:35
Done.
 | 
| + } | 
| + } | 
| + write_pos_ += data_size; | 
| + return data_size; | 
| +} | 
| + | 
| + | 
| +Log::WritePtr Log::Write = NULL; | 
| +FILE* Log::output_handle_ = NULL; | 
| +LogDynamicBuffer* Log::output_buffer_ = NULL; | 
| +Mutex* Log::mutex_ = NULL; | 
| +char* Log::message_buffer_ = NULL; | 
| + | 
| + | 
| +void Log::Init() { | 
| + mutex_ = OS::CreateMutex(); | 
| + message_buffer_ = NewArray<char>(kMessageBufferSize); | 
| +} | 
| + | 
| + | 
| +void Log::OpenStdout() { | 
| + ASSERT(!IsEnabled()); | 
| + output_handle_ = stdout; | 
| + Write = WriteToFile; | 
| + Init(); | 
| +} | 
| + | 
| + | 
| +void Log::OpenFile(const char* name) { | 
| + ASSERT(!IsEnabled()); | 
| + output_handle_ = OS::FOpen(name, OS::LogFileOpenMode); | 
| + Write = WriteToFile; | 
| + Init(); | 
| +} | 
| + | 
| + | 
| +void Log::OpenMemoryBuffer() { | 
| + ASSERT(!IsEnabled()); | 
| + output_buffer_ = new LogDynamicBuffer( | 
| + kDynamicBufferBlockSize, kMaxDynamicBufferSize); | 
| + Write = WriteToMemory; | 
| + Init(); | 
| +} | 
| + | 
| + | 
| +void Log::Close() { | 
| + if (Write == WriteToFile) { | 
| + fclose(output_handle_); | 
| + output_handle_ = NULL; | 
| + } else if (Write == WriteToMemory) { | 
| + delete output_buffer_; | 
| + output_buffer_ = NULL; | 
| + } else { | 
| + ASSERT(Write == NULL); | 
| + } | 
| + Write = NULL; | 
| + | 
| + delete mutex_; | 
| + mutex_ = NULL; | 
| +} | 
| + | 
| + | 
| +int Log::GetLogLines(int from_pos, char* dest_buf, int max_size) { | 
| + if (Write != WriteToMemory) return 0; | 
| + ASSERT(output_buffer_ != NULL); | 
| + ASSERT(from_pos >= 0); | 
| + ASSERT(max_size >= 0); | 
| + int actual_size = output_buffer_->Read(from_pos, dest_buf, max_size); | 
| + ASSERT(actual_size <= max_size); | 
| + if (actual_size == 0) return 0; | 
| + | 
| + // Find previous log line boundary. | 
| + char* end_pos = dest_buf + actual_size - 1; | 
| + while (end_pos >= dest_buf && *end_pos != '\n') --end_pos; | 
| + actual_size = end_pos - dest_buf + 1; | 
| + ASSERT(actual_size <= max_size); | 
| + return actual_size; | 
| +} | 
| + | 
| + | 
| +LogMessageBuilder::LogMessageBuilder(): sl(Log::mutex_), pos_(0) { | 
| + ASSERT(Log::message_buffer_ != NULL); | 
| +} | 
| + | 
| + | 
| +void LogMessageBuilder::Append(const char* format, ...) { | 
| + Vector<char> buf(Log::message_buffer_ + pos_, | 
| + Log::kMessageBufferSize - pos_); | 
| + va_list args; | 
| + va_start(args, format); | 
| + Append(format, args); | 
| + va_end(args); | 
| + ASSERT(pos_ <= Log::kMessageBufferSize); | 
| +} | 
| + | 
| + | 
| +void LogMessageBuilder::Append(const char* format, va_list args) { | 
| + Vector<char> buf(Log::message_buffer_ + pos_, | 
| + Log::kMessageBufferSize - pos_); | 
| + int result = v8::internal::OS::VSNPrintF(buf, format, args); | 
| + | 
| + // Result is -1 if output was truncated. | 
| + if (result >= 0) { | 
| + pos_ += result; | 
| + } else { | 
| + pos_ = Log::kMessageBufferSize; | 
| + } | 
| + ASSERT(pos_ <= Log::kMessageBufferSize); | 
| +} | 
| + | 
| + | 
| +void LogMessageBuilder::Append(const char c) { | 
| + if (pos_ < Log::kMessageBufferSize) { | 
| + Log::message_buffer_[pos_++] = c; | 
| + } | 
| + ASSERT(pos_ <= Log::kMessageBufferSize); | 
| +} | 
| + | 
| + | 
| +void LogMessageBuilder::Append(String* str) { | 
| + AssertNoAllocation no_heap_allocation; // Ensure string stay valid. | 
| + int length = str->length(); | 
| + for (int i = 0; i < length; i++) { | 
| + Append(static_cast<char>(str->Get(i))); | 
| + } | 
| +} | 
| + | 
| + | 
| +void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) { | 
| + AssertNoAllocation no_heap_allocation; // Ensure string stay valid. | 
| + int len = str->length(); | 
| + if (len > 0x1000) | 
| + len = 0x1000; | 
| + if (show_impl_info) { | 
| + Append(str->IsAsciiRepresentation() ? 'a' : '2'); | 
| + if (StringShape(str).IsExternal()) | 
| + Append('e'); | 
| + if (StringShape(str).IsSymbol()) | 
| + Append('#'); | 
| + Append(":%i:", str->length()); | 
| + } | 
| + for (int i = 0; i < len; i++) { | 
| + uc32 c = str->Get(i); | 
| + if (c > 0xff) { | 
| + Append("\\u%04x", c); | 
| + } else if (c < 32 || c > 126) { | 
| + Append("\\x%02x", c); | 
| + } else if (c == ',') { | 
| + Append("\\,"); | 
| + } else if (c == '\\') { | 
| + Append("\\\\"); | 
| + } else { | 
| + Append("%lc", c); | 
| + } | 
| + } | 
| +} | 
| + | 
| + | 
| +void LogMessageBuilder::WriteToLogFile() { | 
| + ASSERT(pos_ <= Log::kMessageBufferSize); | 
| + Log::Write(Log::message_buffer_, pos_); | 
| +} | 
| + | 
| + | 
| +void LogMessageBuilder::WriteCStringToLogFile(const char* str) { | 
| + int len = strlen(str); | 
| + Log::Write(str, len); | 
| +} | 
| + | 
| +#endif // ENABLE_LOGGING_AND_PROFILING | 
| + | 
| +} } // namespace v8::internal |