#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 v8.h.
+ static int GetLogLines(int from_pos, char* dest_buf, int max_size);
+
+ static bool is_enabled() { return output_.handle != NULL; }
+
+ typedef int (*WritePtr)(const char* msg, int length);
+ private:
+ static void Init();
+
+ // Write functions assume that mutex_ is acquired by the caller.
+ static WritePtr Write;
+
+ 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;
+ }
+
+ 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, output_ refers the file or memory buffer
+ // events are written to.
+ // mutex_ should be acquired before using output_.
+ union Output {
+ FILE* handle;
+ char* buffer;
+ };
+ static Output output_;
+
+ // 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;
+Log::Output Log::output_ = {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(output_.handle == NULL);
+ output_.handle = stdout;
+ Write = WriteToFile;
+ Init();
+}
+
+
+void Log::OpenFile(const char* name) {
+ ASSERT(output_.handle == NULL);
+ output_.handle = OS::FOpen(name, OS::LogFileOpenMode);
+ Write = WriteToFile;
+ Init();
+}
+
+
+void Log::OpenMemoryBuffer() {
+ ASSERT(output_.buffer == NULL);
+ 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) {
+ 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 Logger.
+// static buffer in Log.
class LogMessageBuilder BASE_EMBEDDED {
public:
explicit LogMessageBuilder();
// Create a message builder starting from position 0. This acquires the mutex
// in the logger as well.
-LogMessageBuilder::LogMessageBuilder(): sl(Logger::mutex_), pos_(0) {
- ASSERT(Logger::message_buffer_ != NULL);
+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(Logger::message_buffer_ + pos_,
- Logger::kMessageBufferSize - pos_);
+ 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_ <= Logger::kMessageBufferSize);
+ ASSERT(pos_ <= Log::kMessageBufferSize);
}
// Append string data to the log message.
void LogMessageBuilder::Append(const char* format, va_list args) {
- Vector<char> buf(Logger::message_buffer_ + pos_,
- Logger::kMessageBufferSize - pos_);
+ 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_ = Logger::kMessageBufferSize;
+ pos_ = Log::kMessageBufferSize;
}
- ASSERT(pos_ <= Logger::kMessageBufferSize);
+ ASSERT(pos_ <= Log::kMessageBufferSize);
}
// Append a character to the log message.
void LogMessageBuilder::Append(const char c) {
- if (pos_ < Logger::kMessageBufferSize) {
- Logger::message_buffer_[pos_++] = c;
+ if (pos_ < Log::kMessageBufferSize) {
+ Log::message_buffer_[pos_++] = c;
}
- ASSERT(pos_ <= Logger::kMessageBufferSize);
+ ASSERT(pos_ <= Log::kMessageBufferSize);
}
// Write the log message to the log file currently opened.
void LogMessageBuilder::WriteToLogFile() {
- ASSERT(pos_ <= Logger::kMessageBufferSize);
- size_t rv = fwrite(Logger::message_buffer_, 1, pos_, Logger::logfile_);
- ASSERT(rv == static_cast<size_t>(pos_));
- USE(rv);
+ 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) {
- size_t len = strlen(str);
- size_t rv = fwrite(str, 1, len, Logger::logfile_);
- ASSERT(rv == len);
- USE(rv);
+ int len = strlen(str);
+ Log::Write(str, len);
}
#endif
// Logger class implementation.
//
Ticker* Logger::ticker_ = NULL;
-char* Logger::message_buffer_ = NULL;
-FILE* Logger::logfile_ = NULL;
Profiler* Logger::profiler_ = NULL;
-Mutex* Logger::mutex_ = NULL;
VMState* Logger::current_state_ = NULL;
VMState Logger::bottom_state_(EXTERNAL);
SlidingStateWindow* Logger::sliding_state_window_ = NULL;
+
+bool Logger::is_enabled() {
+ return Log::is_enabled();
+}
+
#endif // ENABLE_LOGGING_AND_PROFILING
void Logger::Preamble(const char* content) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.WriteCStringToLogFile(content);
#endif
#ifdef ENABLE_LOGGING_AND_PROFILING
void Logger::UncheckedStringEvent(const char* name, const char* value) {
- if (logfile_ == NULL) return;
+ if (!Log::is_enabled()) return;
LogMessageBuilder msg;
msg.Append("%s,\"%s\"\n", name, value);
msg.WriteToLogFile();
void Logger::IntEvent(const char* name, int value) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log) return;
+ if (!Log::is_enabled() || !FLAG_log) return;
LogMessageBuilder msg;
msg.Append("%s,%d\n", name, value);
msg.WriteToLogFile();
void Logger::HandleEvent(const char* name, Object** location) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_handles) return;
+ if (!Log::is_enabled() || !FLAG_log_handles) return;
LogMessageBuilder msg;
msg.Append("%s,0x%x\n", name,
reinterpret_cast<unsigned int>(location));
#ifdef ENABLE_LOGGING_AND_PROFILING
// ApiEvent is private so all the calls come from the Logger class. It is the
-// caller's responsibility to ensure that logfile_ is not NULL and that
+// caller's responsibility to ensure that log is enabled and that
// FLAG_log_api is true.
void Logger::ApiEvent(const char* format, ...) {
- ASSERT(logfile_ != NULL && FLAG_log_api);
+ ASSERT(Log::is_enabled() && FLAG_log_api);
LogMessageBuilder msg;
va_list ap;
va_start(ap, format);
void Logger::ApiNamedSecurityCheck(Object* key) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_api) return;
+ if (!Log::is_enabled() || !FLAG_log_api) return;
if (key->IsString()) {
SmartPointer<char> str =
String::cast(key)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
unsigned start,
unsigned end) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_prof) return;
+ if (!Log::is_enabled() || !FLAG_prof) return;
LogMessageBuilder msg;
msg.Append("shared-library,\"%s\",0x%08x,0x%08x\n", library_path,
start, end);
unsigned start,
unsigned end) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_prof) return;
+ if (!Log::is_enabled() || !FLAG_prof) return;
LogMessageBuilder msg;
msg.Append("shared-library,\"%ls\",0x%08x,0x%08x\n", library_path,
start, end);
void Logger::RegExpCompileEvent(Handle<JSRegExp> regexp, bool in_cache) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_regexp) return;
+ if (!Log::is_enabled() || !FLAG_log_regexp) return;
LogMessageBuilder msg;
msg.Append("regexp-compile,");
LogRegExpSource(regexp);
void Logger::LogRuntime(Vector<const char> format, JSArray* args) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_runtime) return;
+ if (!Log::is_enabled() || !FLAG_log_runtime) return;
HandleScope scope;
LogMessageBuilder msg;
for (int i = 0; i < format.length(); i++) {
void Logger::ApiIndexedSecurityCheck(uint32_t index) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_api) return;
+ if (!Log::is_enabled() || !FLAG_log_api) return;
ApiEvent("api,check-security,%u\n", index);
#endif
}
Object* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
ASSERT(name->IsString());
- if (logfile_ == NULL || !FLAG_log_api) return;
+ if (!Log::is_enabled() || !FLAG_log_api) return;
String* class_name_obj = holder->class_name();
SmartPointer<char> class_name =
class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
JSObject* holder,
uint32_t index) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_api) return;
+ if (!Log::is_enabled() || !FLAG_log_api) return;
String* class_name_obj = holder->class_name();
SmartPointer<char> class_name =
class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
void Logger::ApiObjectAccess(const char* tag, JSObject* object) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_api) return;
+ if (!Log::is_enabled() || !FLAG_log_api) return;
String* class_name_obj = object->class_name();
SmartPointer<char> class_name =
class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
void Logger::ApiEntryCall(const char* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_api) return;
+ if (!Log::is_enabled() || !FLAG_log_api) return;
Logger::ApiEvent("api,%s\n", name);
#endif
}
void Logger::NewEvent(const char* name, void* object, size_t size) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log) return;
+ if (!Log::is_enabled() || !FLAG_log) return;
LogMessageBuilder msg;
msg.Append("new,%s,0x%x,%u\n", name,
reinterpret_cast<unsigned int>(object),
void Logger::DeleteEvent(const char* name, void* object) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log) return;
+ if (!Log::is_enabled() || !FLAG_log) return;
LogMessageBuilder msg;
msg.Append("delete,%s,0x%x\n", name,
reinterpret_cast<unsigned int>(object));
void Logger::CodeCreateEvent(const char* tag, Code* code, const char* comment) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.Append("code-creation,%s,0x%x,%d,\"", tag,
reinterpret_cast<unsigned int>(code->address()),
void Logger::CodeCreateEvent(const char* tag, Code* code, String* name) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
SmartPointer<char> str =
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
void Logger::CodeCreateEvent(const char* tag, Code* code, String* name,
String* source, int line) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
SmartPointer<char> str =
name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL);
void Logger::CodeCreateEvent(const char* tag, Code* code, int args_count) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.Append("code-creation,%s,0x%x,%d,\"args_count: %d\"\n", tag,
reinterpret_cast<unsigned int>(code->address()),
void Logger::RegExpCodeCreateEvent(Code* code, String* source) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.Append("code-creation,%s,0x%x,%d,\"", "RegExp",
reinterpret_cast<unsigned int>(code->address()),
void Logger::CodeAllocateEvent(Code* code, Assembler* assem) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.Append("code-allocate,0x%x,0x%x\n",
reinterpret_cast<unsigned int>(code->address()),
void Logger::CodeMoveEvent(Address from, Address to) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.Append("code-move,0x%x,0x%x\n",
reinterpret_cast<unsigned int>(from),
void Logger::CodeDeleteEvent(Address from) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
+ if (!Log::is_enabled() || !FLAG_log_code) return;
LogMessageBuilder msg;
msg.Append("code-delete,0x%x\n", reinterpret_cast<unsigned int>(from));
msg.WriteToLogFile();
void Logger::ResourceEvent(const char* name, const char* tag) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log) return;
+ if (!Log::is_enabled() || !FLAG_log) return;
LogMessageBuilder msg;
msg.Append("%s,%s,", name, tag);
void Logger::SuspectReadEvent(String* name, Object* obj) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_suspect) return;
+ if (!Log::is_enabled() || !FLAG_log_suspect) return;
LogMessageBuilder msg;
String* class_name = obj->IsJSObject()
? JSObject::cast(obj)->class_name()
: Heap::empty_string();
- ScopedLock sl(mutex_);
msg.Append("suspect-read,");
msg.Append(class_name);
msg.Append(',');
void Logger::HeapSampleBeginEvent(const char* space, const char* kind) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_gc) return;
+ if (!Log::is_enabled() || !FLAG_log_gc) return;
LogMessageBuilder msg;
msg.Append("heap-sample-begin,\"%s\",\"%s\"\n", space, kind);
msg.WriteToLogFile();
void Logger::HeapSampleEndEvent(const char* space, const char* kind) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_gc) return;
+ if (!Log::is_enabled() || !FLAG_log_gc) return;
LogMessageBuilder msg;
msg.Append("heap-sample-end,\"%s\",\"%s\"\n", space, kind);
msg.WriteToLogFile();
void Logger::HeapSampleItemEvent(const char* type, int number, int bytes) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_gc) return;
+ if (!Log::is_enabled() || !FLAG_log_gc) return;
LogMessageBuilder msg;
msg.Append("heap-sample-item,%s,%d,%d\n", type, number, bytes);
msg.WriteToLogFile();
void Logger::DebugTag(const char* call_site_tag) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log) return;
+ if (!Log::is_enabled() || !FLAG_log) return;
LogMessageBuilder msg;
msg.Append("debug-tag,%s\n", call_site_tag);
msg.WriteToLogFile();
void Logger::DebugEvent(const char* event_type, Vector<uint16_t> parameter) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log) return;
+ if (!Log::is_enabled() || !FLAG_log) return;
StringBuilder s(parameter.length() + 1);
for (int i = 0; i < parameter.length(); ++i) {
s.AddCharacter(static_cast<char>(parameter[i]));
#ifdef ENABLE_LOGGING_AND_PROFILING
void Logger::TickEvent(TickSample* sample, bool overflow) {
- if (logfile_ == NULL || !FLAG_prof) return;
+ if (!Log::is_enabled() || !FLAG_prof) return;
LogMessageBuilder msg;
msg.Append("tick,0x%x,0x%x,%d", sample->pc, sample->sp,
static_cast<int>(sample->state));
void Logger::ResumeProfiler() {
profiler_->resume();
}
+
+
+int Logger::GetLogLines(int from_pos, char* dest_buf, int max_size) {
+ return Log::GetLogLines(from_pos, dest_buf, max_size);
+}
+
#endif
// If we're logging anything, we need to open the log file.
if (open_log_file) {
if (strcmp(FLAG_logfile, "-") == 0) {
- logfile_ = stdout;
+ Log::OpenStdout();
+ } else if (strcmp(FLAG_logfile, "*") == 0) {
+ Log::OpenMemoryBuffer();
} else if (strchr(FLAG_logfile, '%') != NULL) {
// If there's a '%' in the log file name we have to expand
// placeholders.
}
}
SmartPointer<const char> expanded = stream.ToCString();
- logfile_ = OS::FOpen(*expanded, OS::LogFileOpenMode);
+ Log::OpenFile(*expanded);
} else {
- logfile_ = OS::FOpen(FLAG_logfile, OS::LogFileOpenMode);
+ Log::OpenFile(FLAG_logfile);
}
- message_buffer_ = NewArray<char>(kMessageBufferSize);
- mutex_ = OS::CreateMutex();
}
current_state_ = &bottom_state_;
delete ticker_;
- if (logfile_ != NULL) {
- fclose(logfile_);
- logfile_ = NULL;
- delete mutex_;
- mutex_ = NULL;
- DeleteArray(message_buffer_);
- }
+ Log::Close();
#endif
}
--- /dev/null
+// Copyright 2006-2009 the V8 project authors. All rights reserved.
+//
+// Tests of logging functions from log.h
+
+#ifdef ENABLE_LOGGING_AND_PROFILING
+
+#include "v8.h"
+
+#include "log.h"
+
+#include "cctest.h"
+
+using v8::internal::Logger;
+
+static void SetUp() {
+ // Log to memory buffer.
+ v8::internal::FLAG_logfile = "*";
+ v8::internal::FLAG_log = true;
+ Logger::Setup();
+}
+
+static void TearDown() {
+ Logger::TearDown();
+}
+
+TEST(EmptyLog) {
+ SetUp();
+ CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
+ CHECK_EQ(0, Logger::GetLogLines(100, NULL, 0));
+ CHECK_EQ(0, Logger::GetLogLines(0, NULL, 100));
+ CHECK_EQ(0, Logger::GetLogLines(100, NULL, 100));
+ TearDown();
+}
+
+
+TEST(GetMessages) {
+ SetUp();
+ Logger::StringEvent("aaa", "bbb");
+ Logger::StringEvent("cccc", "dddd");
+ CHECK_EQ(0, Logger::GetLogLines(0, NULL, 0));
+ char log_lines[100];
+ memset(log_lines, 0, sizeof(log_lines));
+ // Requesting data size which is smaller than first log message length.
+ CHECK_EQ(0, Logger::GetLogLines(0, log_lines, 3));
+ // See Logger::StringEvent.
+ const char* line_1 = "aaa,\"bbb\"\n";
+ const int line_1_len = strlen(line_1);
+ // Still smaller than log message length.
+ CHECK_EQ(0, Logger::GetLogLines(0, log_lines, line_1_len - 1));
+ // The exact size.
+ CHECK_EQ(line_1_len, Logger::GetLogLines(0, log_lines, line_1_len));
+ CHECK_EQ(line_1, log_lines);
+ 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));
+ CHECK_EQ(line_1, log_lines);
+ memset(log_lines, 0, sizeof(log_lines));
+ const char* line_2 = "cccc,\"dddd\"\n";
+ const int line_2_len = strlen(line_2);
+ // Now start with line_2 beginning.
+ CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 0));
+ CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, 3));
+ CHECK_EQ(0, Logger::GetLogLines(line_1_len, log_lines, line_2_len - 1));
+ CHECK_EQ(line_2_len, Logger::GetLogLines(line_1_len, log_lines, line_2_len));
+ CHECK_EQ(line_2, log_lines);
+ memset(log_lines, 0, sizeof(log_lines));
+ CHECK_EQ(line_2_len,
+ Logger::GetLogLines(line_1_len, log_lines, line_2_len + 3));
+ CHECK_EQ(line_2, log_lines);
+ memset(log_lines, 0, sizeof(log_lines));
+ // Now get entire buffer contents.
+ const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
+ const int all_lines_len = strlen(all_lines);
+ CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len));
+ CHECK_EQ(all_lines, log_lines);
+ memset(log_lines, 0, sizeof(log_lines));
+ CHECK_EQ(all_lines_len, Logger::GetLogLines(0, log_lines, all_lines_len + 3));
+ CHECK_EQ(all_lines, log_lines);
+ memset(log_lines, 0, sizeof(log_lines));
+ TearDown();
+}
+
+
+TEST(BeyondWritePosition) {
+ SetUp();
+ Logger::StringEvent("aaa", "bbb");
+ Logger::StringEvent("cccc", "dddd");
+ // See Logger::StringEvent.
+ const char* all_lines = "aaa,\"bbb\"\ncccc,\"dddd\"\n";
+ const int all_lines_len = strlen(all_lines);
+ CHECK_EQ(0, Logger::GetLogLines(all_lines_len, NULL, 1));
+ CHECK_EQ(0, Logger::GetLogLines(all_lines_len, NULL, 100));
+ CHECK_EQ(0, Logger::GetLogLines(all_lines_len + 1, NULL, 1));
+ CHECK_EQ(0, Logger::GetLogLines(all_lines_len + 1, NULL, 100));
+ CHECK_EQ(0, Logger::GetLogLines(all_lines_len + 100, NULL, 1));
+ CHECK_EQ(0, Logger::GetLogLines(all_lines_len + 100, NULL, 100));
+ CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, NULL, 1));
+ CHECK_EQ(0, Logger::GetLogLines(10 * 1024 * 1024, NULL, 100));
+ TearDown();
+}
+
+#endif // ENABLE_LOGGING_AND_PROFILING