Implement a dynamically growing memory log buffer with an upper limit.

The goal of this change is to allow longer profiling sessions and preserve memory when profiler isn't started. The buffer starts with 64K and grows until it reaches the upper limit, which is currently set to 50MB --- according to my evaluations, this is enough for at least 20 minutes of GMail profiling. As we're planning to introduce compression for the profiler log, this time boundary will be significantly increased soon.

To make possible unit testing of the new component, I've factored out Logger's utility classes into a separate source file: log-utils.h/cc. Log and LogMessageBuilder are moved there from log.cc without any semantical changes.

Review URL: http://codereview.chromium.org/115814


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2067 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 4014f642
......@@ -43,14 +43,14 @@ SOURCES = {
'flags.cc', 'frames.cc', 'func-name-inferrer.cc',
'global-handles.cc', 'handles.cc', 'hashmap.cc',
'heap.cc', 'ic.cc', 'interpreter-irregexp.cc', 'jsregexp.cc',
'jump-target.cc', 'log.cc', 'mark-compact.cc', 'messages.cc', 'objects.cc',
'oprofile-agent.cc', 'parser.cc', 'property.cc', 'regexp-macro-assembler.cc',
'regexp-macro-assembler-irregexp.cc', 'regexp-stack.cc',
'register-allocator.cc', 'rewriter.cc', 'runtime.cc', 'scanner.cc',
'scopeinfo.cc', 'scopes.cc', 'serialize.cc', 'snapshot-common.cc',
'spaces.cc', 'string-stream.cc', 'stub-cache.cc', 'token.cc', 'top.cc',
'unicode.cc', 'usage-analyzer.cc', 'utils.cc', 'v8-counters.cc',
'v8.cc', 'v8threads.cc', 'variables.cc', 'version.cc',
'jump-target.cc', 'log.cc', 'log-utils.cc', 'mark-compact.cc', 'messages.cc',
'objects.cc', 'oprofile-agent.cc', 'parser.cc', 'property.cc',
'regexp-macro-assembler.cc', 'regexp-macro-assembler-irregexp.cc',
'regexp-stack.cc', 'register-allocator.cc', 'rewriter.cc', 'runtime.cc',
'scanner.cc', 'scopeinfo.cc', 'scopes.cc', 'serialize.cc',
'snapshot-common.cc', 'spaces.cc', 'string-stream.cc', 'stub-cache.cc',
'token.cc', 'top.cc', 'unicode.cc', 'usage-analyzer.cc', 'utils.cc',
'v8-counters.cc', 'v8.cc', 'v8threads.cc', 'variables.cc', 'version.cc',
'virtual-frame.cc', 'zone.cc'
],
'arch:arm': [
......
// 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);
AllocateBlock(0);
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;
AllocateBlock(++block_index_);
}
}
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
// Copyright 2006-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.
#ifndef V8_LOG_UTILS_H_
#define V8_LOG_UTILS_H_
namespace v8 {
namespace internal {
#ifdef ENABLE_LOGGING_AND_PROFILING
// A memory buffer that increments its size as you write in it. Size
// is incremented with 'block_size' steps, never exceeding 'max_size'.
// During growth, memory contents are never copied.
//
// An instance of this class is created dynamically by Log.
class LogDynamicBuffer {
public:
LogDynamicBuffer(int block_size, int max_size);
~LogDynamicBuffer();
// Reads contents of the buffer starting from 'from_pos'. Upon
// return, 'dest_buf' is filled with the data. Actual amount of data
// filled is returned, it is <= 'buf_size'.
int Read(int from_pos, char* dest_buf, int buf_size);
// Writes 'data' to the buffer, making it larger if necessary. If
// data is too big to fit in the buffer, it doesn't get written at
// all. Returns amount of data written (it is either 'data_size', or
// 0, if 'data' is too big).
int Write(const char* data, int data_size);
private:
void AllocateBlock(int index) {
blocks_[index] = NewArray<char>(block_size_);
}
int BlockIndex(int pos) const { return pos / block_size_; }
int BlocksCount() const { return BlockIndex(max_size_) + 1; }
int PosInBlock(int pos) const { return pos % block_size_; }
const int block_size_;
const int max_size_;
ScopedVector<char*> blocks_;
int write_pos_;
int block_index_;
int block_write_pos_;
};
// Functions and data for performing output of log messages.
class Log : public AllStatic {
public:
// Opens stdout for logging.
static void OpenStdout();
// Opens file for logging.
static void OpenFile(const char* name);
// Opens memory buffer for logging.
static void OpenMemoryBuffer();
// Frees all resources acquired in Open... functions.
static void Close();
// See description in include/v8.h.
static int GetLogLines(int from_pos, char* dest_buf, int max_size);
// Returns whether logging is enabled.
static bool IsEnabled() {
return output_handle_ != NULL || output_buffer_ != NULL;
}
private:
typedef int (*WritePtr)(const char* msg, int length);
// Initialization function called from Open... functions.
static void Init();
// Write functions assume that mutex_ is acquired by the caller.
static WritePtr Write;
// Implementation of writing to a log file.
static int WriteToFile(const char* msg, int length) {
ASSERT(output_handle_ != NULL);
int rv = fwrite(msg, 1, length, output_handle_);
ASSERT(length == rv);
return rv;
}
// Implementation of writing to a memory buffer.
static int WriteToMemory(const char* msg, int length) {
ASSERT(output_buffer_ != NULL);
return output_buffer_->Write(msg, length);
}
// When logging is active, either output_handle_ or output_buffer_ is used
// to store a pointer to log destination. If logging was opened via OpenStdout
// or OpenFile, then output_handle_ is used. If logging was opened
// via OpenMemoryBuffer, then output_buffer_ is used.
// mutex_ should be acquired before using output_handle_ or output_buffer_.
static FILE* output_handle_;
static LogDynamicBuffer* output_buffer_;
// Size of dynamic buffer block (and dynamic buffer initial size).
static const int kDynamicBufferBlockSize = 65536;
// Maximum size of dynamic buffer.
static const int kMaxDynamicBufferSize = 50 * 1024 * 1024;
// mutex_ is a Mutex used for enforcing exclusive
// access to the formatting buffer and the log file or log memory buffer.
static Mutex* mutex_;
// Size of buffer used for formatting log messages.
static const int kMessageBufferSize = 2048;
// Buffer used for formatting log messages. This is a singleton buffer and
// mutex_ should be acquired before using it.
static char* message_buffer_;
friend class LogMessageBuilder;
};
// Utility class for formatting log messages. It fills the message into the
// static buffer in Log.
class LogMessageBuilder BASE_EMBEDDED {
public:
// Create a message builder starting from position 0. This acquires the mutex
// in the log as well.
explicit LogMessageBuilder();
~LogMessageBuilder() { }
// Append string data to the log message.
void Append(const char* format, ...);
// Append string data to the log message.
void Append(const char* format, va_list args);
// Append a character to the log message.
void Append(const char c);
// Append a heap string.
void Append(String* str);
void AppendDetailed(String* str, bool show_impl_info);
// Write the log message to the log file currently opened.
void WriteToLogFile();
// Write a null-terminated string to to the log file currently opened.
void WriteCStringToLogFile(const char* str);
private:
ScopedLock sl;
int pos_;
};
#endif // ENABLE_LOGGING_AND_PROFILING
} } // namespace v8::internal
#endif // V8_LOG_UTILS_H_
......@@ -31,6 +31,7 @@
#include "bootstrapper.h"
#include "log.h"
#include "log-utils.h"
#include "macro-assembler.h"
#include "platform.h"
#include "serialize.h"
......@@ -287,304 +288,6 @@ void Profiler::Run() {
}
#ifdef ENABLE_LOGGING_AND_PROFILING
// Functions and data for performing output of log messages.
class Log : public AllStatic {
public:
// Opens stdout for logging.
static void OpenStdout();
// Opens file for logging.
static void OpenFile(const char* name);
// Opens memory buffer for logging.
static void OpenMemoryBuffer();
// Frees all resources acquired in Open... functions.
static void Close();
// See description in include/v8.h.
static int GetLogLines(int from_pos, char* dest_buf, int max_size);
// Returns whether logging is enabled.
static bool IsEnabled() {
return output_handle_ != NULL || output_buffer_ != NULL;
}
private:
typedef int (*WritePtr)(const char* msg, int length);
// Initialization function called from Open... functions.
static void Init();
// Write functions assume that mutex_ is acquired by the caller.
static WritePtr Write;
// Implementation of writing to a log file.
static int WriteToFile(const char* msg, int length) {
ASSERT(output_handle_ != NULL);
int rv = fwrite(msg, 1, length, output_handle_);
ASSERT(length == rv);
return rv;
}
// Implementation of writing to a memory buffer.
static int WriteToMemory(const char* msg, int length) {
ASSERT(output_buffer_ != NULL);
ASSERT(output_buffer_write_pos_ >= output_buffer_);
if (output_buffer_write_pos_ + length
<= output_buffer_ + kOutputBufferSize) {
memcpy(output_buffer_write_pos_, msg, length);
output_buffer_write_pos_ += length;
return length;
} else {
// Memory buffer is full, ignore write.
return 0;
}
}
// When logging is active, either output_handle_ or output_buffer_ is used
// to store a pointer to log destination. If logging was opened via OpenStdout
// or OpenFile, then output_handle_ is used. If logging was opened
// via OpenMemoryBuffer, then output_buffer_ is used.
// mutex_ should be acquired before using output_handle_ or output_buffer_.
static FILE* output_handle_;
static char* output_buffer_;
// mutex_ is a Mutex used for enforcing exclusive
// access to the formatting buffer and the log file or log memory buffer.
static Mutex* mutex_;
// Size of buffer used for memory logging.
static const int kOutputBufferSize = 2 * 1024 * 1024;
// Writing position in a memory buffer.
static char* output_buffer_write_pos_;
// Size of buffer used for formatting log messages.
static const int kMessageBufferSize = 2048;
// Buffer used for formatting log messages. This is a singleton buffer and
// mutex_ should be acquired before using it.
static char* message_buffer_;
friend class LogMessageBuilder;
};
Log::WritePtr Log::Write = NULL;
FILE* Log::output_handle_ = NULL;
char* Log::output_buffer_ = NULL;
Mutex* Log::mutex_ = NULL;
char* Log::output_buffer_write_pos_ = 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_ = NewArray<char>(kOutputBufferSize);
output_buffer_write_pos_ = output_buffer_;
Write = WriteToMemory;
Init();
}
void Log::Close() {
if (Write == WriteToFile) {
fclose(output_handle_);
output_handle_ = NULL;
} else if (Write == WriteToMemory) {
DeleteArray(output_buffer_);
output_buffer_ = NULL;
} else {
ASSERT(Write == NULL);
}
Write = NULL;
delete mutex_;
mutex_ = NULL;
DeleteArray(message_buffer_);
message_buffer_ = NULL;
}
int Log::GetLogLines(int from_pos, char* dest_buf, int max_size) {
if (Write != WriteToMemory) return 0;
ASSERT(output_buffer_ != NULL);
ASSERT(output_buffer_write_pos_ >= output_buffer_);
ASSERT(from_pos >= 0);
ASSERT(max_size >= 0);
int actual_size = max_size;
char* buffer_read_pos = output_buffer_ + from_pos;
ScopedLock sl(mutex_);
if (actual_size == 0
|| output_buffer_write_pos_ == output_buffer_
|| buffer_read_pos >= output_buffer_write_pos_) {
// No data requested or can be returned.
return 0;
}
if (buffer_read_pos + actual_size > output_buffer_write_pos_) {
// Requested size overlaps with current writing position and
// needs to be truncated.
actual_size = output_buffer_write_pos_ - buffer_read_pos;
ASSERT(actual_size == 0 || buffer_read_pos[actual_size - 1] == '\n');
} else {
// Find previous log line boundary.
char* end_pos = buffer_read_pos + actual_size - 1;
while (end_pos >= buffer_read_pos && *end_pos != '\n') --end_pos;
actual_size = end_pos - buffer_read_pos + 1;
}
ASSERT(actual_size <= max_size);
if (actual_size > 0) {
memcpy(dest_buf, buffer_read_pos, actual_size);
}
return actual_size;
}
// Utility class for formatting log messages. It fills the message into the
// static buffer in Log.
class LogMessageBuilder BASE_EMBEDDED {
public:
explicit LogMessageBuilder();
~LogMessageBuilder() { }
void Append(const char* format, ...);
void Append(const char* format, va_list args);
void Append(const char c);
void Append(String* str);
void AppendDetailed(String* str, bool show_impl_info);
void WriteToLogFile();
void WriteCStringToLogFile(const char* str);
private:
ScopedLock sl;
int pos_;
};
// Create a message builder starting from position 0. This acquires the mutex
// in the logger as well.
LogMessageBuilder::LogMessageBuilder(): sl(Log::mutex_), pos_(0) {
ASSERT(Log::message_buffer_ != NULL);
}
// Append string data to the log message.
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);
}
// Append string data to the log message.
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);
}
// Append a character to the log message.
void LogMessageBuilder::Append(const char c) {
if (pos_ < Log::kMessageBufferSize) {
Log::message_buffer_[pos_++] = c;
}
ASSERT(pos_ <= Log::kMessageBufferSize);
}
// Append a heap string.
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);
}
}
}
// Write the log message to the log file currently opened.
void LogMessageBuilder::WriteToLogFile() {
ASSERT(pos_ <= Log::kMessageBufferSize);
Log::Write(Log::message_buffer_, pos_);
}
// Write a null-terminated string to to the log file currently opened.
void LogMessageBuilder::WriteCStringToLogFile(const char* str) {
int len = strlen(str);
Log::Write(str, len);
}
#endif
//
// Logger class implementation.
//
......
......@@ -48,6 +48,7 @@ SOURCES = {
'test-list.cc',
'test-lock.cc',
'test-log.cc',
'test-log-utils.cc',
'test-mark-compact.cc',
'test-regexp.cc',
'test-serialize.cc',
......
// Copyright 2006-2009 the V8 project authors. All rights reserved.
//
// Tests of logging utilities from log-utils.h
#ifdef ENABLE_LOGGING_AND_PROFILING
#include "v8.h"
#include "log-utils.h"
#include "cctest.h"
using v8::internal::EmbeddedVector;
using v8::internal::LogDynamicBuffer;
using v8::internal::Vector;
// Fills 'ref_buffer' with test data: a sequence of two-digit
// hex numbers: '0001020304...'. Then writes 'ref_buffer' contents to 'dynabuf'.
static void WriteData(LogDynamicBuffer* dynabuf, Vector<char>* ref_buffer) {
static const char kHex[] = "0123456789ABCDEF";
CHECK_GT(ref_buffer->length(), 0);
CHECK_GT(513, ref_buffer->length());
for (int i = 0, half_len = ref_buffer->length() >> 1; i < half_len; ++i) {
(*ref_buffer)[i << 1] = kHex[i >> 4];
(*ref_buffer)[(i << 1) + 1] = kHex[i & 15];
}
if (ref_buffer->length() & 1) {
ref_buffer->last() = kHex[ref_buffer->length() >> 5];
}
CHECK_EQ(ref_buffer->length(),
dynabuf->Write(ref_buffer->start(), ref_buffer->length()));
}
static int ReadData(
LogDynamicBuffer* dynabuf, int start_pos, i::Vector<char>* buffer) {
return dynabuf->Read(start_pos, buffer->start(), buffer->length());
}
// Helper function used by CHECK_EQ to compare Vectors.
static inline void CheckEqualsHelper(const char* file, int line,
const char* expected_source,
const Vector<char>& expected,
const char* value_source,
const Vector<char>& value) {
if (expected.length() != value.length()) {
V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n"
"# Vectors lengths differ: %d expected, %d found",
expected_source, value_source,
expected.length(), value.length());
}
if (strncmp(expected.start(), value.start(), expected.length()) != 0) {
V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n"
"# Vectors contents differ:\n"
"# Expected: %.*s\n"
"# Found: %.*s",
expected_source, value_source,
expected.length(), expected.start(),
value.length(), value.start());
}
}
TEST(DynaBufSingleBlock) {
LogDynamicBuffer dynabuf(32, 32);
EmbeddedVector<char, 32> ref_buf;
WriteData(&dynabuf, &ref_buf);
EmbeddedVector<char, 32> buf;
CHECK_EQ(32, dynabuf.Read(0, buf.start(), buf.length()));
CHECK_EQ(32, ReadData(&dynabuf, 0, &buf));
CHECK_EQ(ref_buf, buf);
// Verify that we can't read and write past the end.
CHECK_EQ(0, dynabuf.Read(32, buf.start(), buf.length()));
CHECK_EQ(0, dynabuf.Write(buf.start(), buf.length()));
}
TEST(DynaBufCrossBlocks) {
LogDynamicBuffer dynabuf(32, 128);
EmbeddedVector<char, 48> ref_buf;
WriteData(&dynabuf, &ref_buf);
CHECK_EQ(48, dynabuf.Write(ref_buf.start(), ref_buf.length()));
// Verify that we can't write data when remaining buffer space isn't enough.
CHECK_EQ(0, dynabuf.Write(ref_buf.start(), ref_buf.length()));
EmbeddedVector<char, 48> buf;
CHECK_EQ(48, ReadData(&dynabuf, 0, &buf));
CHECK_EQ(ref_buf, buf);
CHECK_EQ(48, ReadData(&dynabuf, 48, &buf));
CHECK_EQ(ref_buf, buf);
CHECK_EQ(0, ReadData(&dynabuf, 48 * 2, &buf));
}
TEST(DynaBufReadTruncation) {
LogDynamicBuffer dynabuf(32, 128);
EmbeddedVector<char, 128> ref_buf;
WriteData(&dynabuf, &ref_buf);
EmbeddedVector<char, 128> buf;
CHECK_EQ(128, ReadData(&dynabuf, 0, &buf));
CHECK_EQ(ref_buf, buf);
// Try to read near the end with a buffer larger than remaining data size.
EmbeddedVector<char, 48> tail_buf;
CHECK_EQ(32, ReadData(&dynabuf, 128 - 32, &tail_buf));
CHECK_EQ(ref_buf.SubVector(128 - 32, 128), tail_buf.SubVector(0, 32));
}
#endif // ENABLE_LOGGING_AND_PROFILING
......@@ -57,6 +57,7 @@ TEST(GetMessages) {
memset(log_lines, 0, sizeof(log_lines));
// A bit more than the first line length.
CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len + 3));
log_lines[line_1_len] = '\0';
CHECK_EQ(line_1, log_lines);
memset(log_lines, 0, sizeof(log_lines));
const char* line_2 = "cccc,\"dddd\"\n";
......
......@@ -2,7 +2,7 @@
# 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
......@@ -12,7 +12,7 @@
# * 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
......@@ -165,6 +165,8 @@
'../../src/list.h',
'../../src/log.cc',
'../../src/log.h',
'../../src/log-utils.cc',
'../../src/log-utils.h',
'../../src/macro-assembler.h',
'../../src/mark-compact.cc',
'../../src/mark-compact.h',
......
......@@ -205,6 +205,8 @@
89F23C9F0E78D604006B2466 /* simulator-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF17D0E719B8F00D62E90 /* simulator-arm.cc */; };
89F23CA00E78D609006B2466 /* stub-cache-arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = 897FF18A0E719B8F00D62E90 /* stub-cache-arm.cc */; };
89FB0E3A0F8E533F00B04B3C /* d8-posix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 89FB0E360F8E531900B04B3C /* d8-posix.cc */; };
9F4B7B890FCC877A00DC4117 /* log-utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F4B7B870FCC877A00DC4117 /* log-utils.cc */; };
9F4B7B8A0FCC877A00DC4117 /* log-utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F4B7B870FCC877A00DC4117 /* log-utils.cc */; };
9F92FAA90F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; };
9F92FAAA0F8F28AD0089F02C /* func-name-inferrer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */; };
9FC86ABD0F5FEDAC00F22668 /* oprofile-agent.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */; };
......@@ -527,6 +529,8 @@
89F23C950E78D5B6006B2466 /* v8_shell-arm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "v8_shell-arm"; sourceTree = BUILT_PRODUCTS_DIR; };
89FB0E360F8E531900B04B3C /* d8-posix.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-posix.cc"; path = "../src/d8-posix.cc"; sourceTree = "<group>"; };
89FB0E370F8E531900B04B3C /* d8-windows.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "d8-windows.cc"; path = "../src/d8-windows.cc"; sourceTree = "<group>"; };
9F4B7B870FCC877A00DC4117 /* log-utils.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "log-utils.cc"; sourceTree = "<group>"; };
9F4B7B880FCC877A00DC4117 /* log-utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "log-utils.h"; sourceTree = "<group>"; };
9F92FAA70F8F28AD0089F02C /* func-name-inferrer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "func-name-inferrer.cc"; sourceTree = "<group>"; };
9F92FAA80F8F28AD0089F02C /* func-name-inferrer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "func-name-inferrer.h"; sourceTree = "<group>"; };
9FC86ABB0F5FEDAC00F22668 /* oprofile-agent.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "oprofile-agent.cc"; sourceTree = "<group>"; };
......@@ -732,6 +736,8 @@
897FF1510E719B8F00D62E90 /* list.h */,
897FF1520E719B8F00D62E90 /* log.cc */,
897FF1530E719B8F00D62E90 /* log.h */,
9F4B7B870FCC877A00DC4117 /* log-utils.cc */,
9F4B7B880FCC877A00DC4117 /* log-utils.h */,
897FF1540E719B8F00D62E90 /* macro-assembler-arm.cc */,
897FF1550E719B8F00D62E90 /* macro-assembler-arm.h */,
897FF1560E719B8F00D62E90 /* macro-assembler-ia32.cc */,
......@@ -1187,6 +1193,7 @@
58950D660F5551C200F3E8BA /* virtual-frame.cc in Sources */,
58950D670F5551C400F3E8BA /* virtual-frame-ia32.cc in Sources */,
89A88E2E0E71A6D60043BA31 /* zone.cc in Sources */,
9F4B7B890FCC877A00DC4117 /* log-utils.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -1290,6 +1297,7 @@
58950D680F5551CB00F3E8BA /* virtual-frame.cc in Sources */,
58950D690F5551CE00F3E8BA /* virtual-frame-arm.cc in Sources */,
89F23C820E78D5B2006B2466 /* zone.cc in Sources */,
9F4B7B8A0FCC877A00DC4117 /* log-utils.cc in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -544,6 +544,14 @@
RelativePath="..\..\src\log.h"
>
</File>
<File
RelativePath="..\..\src\log-utils.cc"
>
</File>
<File
RelativePath="..\..\src\log-utils.h"
>
</File>
<File
RelativePath="..\..\src\ia32\macro-assembler-ia32.cc"
>
......
......@@ -540,6 +540,14 @@
RelativePath="..\..\src\log.h"
>
</File>
<File
RelativePath="..\..\src\log-utils.cc"
>
</File>
<File
RelativePath="..\..\src\log-utils.h"
>
</File>
<File
RelativePath="..\..\src\arm\macro-assembler-arm.cc"
>
......
......@@ -205,6 +205,10 @@
RelativePath="..\..\test\cctest\test-log.cc"
>
</File>
<File
RelativePath="..\..\test\cctest\test-log-utils.cc"
>
</File>
<File
RelativePath="..\..\test\cctest\test-log-ia32.cc"
>
......
......@@ -199,6 +199,10 @@
RelativePath="..\..\test\cctest\test-log.cc"
>
</File>
<File
RelativePath="..\..\test\cctest\test-log-utils.cc"
>
</File>
<File
RelativePath="..\..\test\cctest\test-mark-compact.cc"
>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment