Handle filling up of memory buffer to make log processing in DevTools Profiler easier.

When profiler's memory buffer is filled up, profiling is stopped and it is ensured that the last record in the buffer is "profiler,\"pause\"" thus making the end of profiling session explicit. Otherwise DevTools Profiler would need to guess whether the current profiling session has been stopped.

Tested with Chromium.

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


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2072 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
mikhail.naganov@gmail.com 2009-05-28 13:56:32 +00:00
parent 8ed81aca8a
commit 795ba99117
5 changed files with 127 additions and 18 deletions

View File

@ -34,11 +34,14 @@ namespace internal {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
LogDynamicBuffer::LogDynamicBuffer(int block_size, int max_size) LogDynamicBuffer::LogDynamicBuffer(
int block_size, int max_size, const char* seal, int seal_size)
: block_size_(block_size), : block_size_(block_size),
max_size_(max_size - (max_size % block_size_)), max_size_(max_size - (max_size % block_size_)),
seal_(seal),
seal_size_(seal_size),
blocks_(max_size_ / block_size_ + 1), blocks_(max_size_ / block_size_ + 1),
write_pos_(0), block_index_(0), block_write_pos_(0) { write_pos_(0), block_index_(0), block_write_pos_(0), is_sealed_(false) {
ASSERT(BlocksCount() > 0); ASSERT(BlocksCount() > 0);
AllocateBlock(0); AllocateBlock(0);
for (int i = 1; i < BlocksCount(); ++i) { for (int i = 1; i < BlocksCount(); ++i) {
@ -78,8 +81,26 @@ int LogDynamicBuffer::Read(int from_pos, char* dest_buf, int buf_size) {
} }
int LogDynamicBuffer::Seal() {
WriteInternal(seal_, seal_size_);
is_sealed_ = true;
return 0;
}
int LogDynamicBuffer::Write(const char* data, int data_size) { int LogDynamicBuffer::Write(const char* data, int data_size) {
if ((write_pos_ + data_size) > max_size_) return 0; if (is_sealed_) {
return 0;
}
if ((write_pos_ + data_size) <= (max_size_ - seal_size_)) {
return WriteInternal(data, data_size);
} else {
return Seal();
}
}
int LogDynamicBuffer::WriteInternal(const char* data, int data_size) {
int data_pos = 0; int data_pos = 0;
while (data_pos < data_size) { while (data_pos < data_size) {
const int write_size = const int write_size =
@ -98,9 +119,12 @@ int LogDynamicBuffer::Write(const char* data, int data_size) {
} }
bool Log::is_stopped_ = false;
Log::WritePtr Log::Write = NULL; Log::WritePtr Log::Write = NULL;
FILE* Log::output_handle_ = NULL; FILE* Log::output_handle_ = NULL;
LogDynamicBuffer* Log::output_buffer_ = NULL; LogDynamicBuffer* Log::output_buffer_ = NULL;
// Must be the same message as in Logger::PauseProfiler
const char* Log::kDynamicBufferSeal = "profiler,\"pause\"\n";
Mutex* Log::mutex_ = NULL; Mutex* Log::mutex_ = NULL;
char* Log::message_buffer_ = NULL; char* Log::message_buffer_ = NULL;
@ -130,7 +154,8 @@ void Log::OpenFile(const char* name) {
void Log::OpenMemoryBuffer() { void Log::OpenMemoryBuffer() {
ASSERT(!IsEnabled()); ASSERT(!IsEnabled());
output_buffer_ = new LogDynamicBuffer( output_buffer_ = new LogDynamicBuffer(
kDynamicBufferBlockSize, kMaxDynamicBufferSize); kDynamicBufferBlockSize, kMaxDynamicBufferSize,
kDynamicBufferSeal, strlen(kDynamicBufferSeal));
Write = WriteToMemory; Write = WriteToMemory;
Init(); Init();
} }
@ -150,6 +175,8 @@ void Log::Close() {
delete mutex_; delete mutex_;
mutex_ = NULL; mutex_ = NULL;
is_stopped_ = false;
} }
@ -171,6 +198,10 @@ int Log::GetLogLines(int from_pos, char* dest_buf, int max_size) {
} }
LogMessageBuilder::WriteFailureHandler
LogMessageBuilder::write_failure_handler = NULL;
LogMessageBuilder::LogMessageBuilder(): sl(Log::mutex_), pos_(0) { LogMessageBuilder::LogMessageBuilder(): sl(Log::mutex_), pos_(0) {
ASSERT(Log::message_buffer_ != NULL); ASSERT(Log::message_buffer_ != NULL);
} }
@ -251,13 +282,19 @@ void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
void LogMessageBuilder::WriteToLogFile() { void LogMessageBuilder::WriteToLogFile() {
ASSERT(pos_ <= Log::kMessageBufferSize); ASSERT(pos_ <= Log::kMessageBufferSize);
Log::Write(Log::message_buffer_, pos_); const int written = Log::Write(Log::message_buffer_, pos_);
if (written != pos_ && write_failure_handler != NULL) {
write_failure_handler();
}
} }
void LogMessageBuilder::WriteCStringToLogFile(const char* str) { void LogMessageBuilder::WriteCStringToLogFile(const char* str) {
int len = strlen(str); const int len = strlen(str);
Log::Write(str, len); const int written = Log::Write(str, len);
if (written != len && write_failure_handler != NULL) {
write_failure_handler();
}
} }
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING

View File

@ -35,12 +35,17 @@ namespace internal {
// A memory buffer that increments its size as you write in it. Size // A memory buffer that increments its size as you write in it. Size
// is incremented with 'block_size' steps, never exceeding 'max_size'. // is incremented with 'block_size' steps, never exceeding 'max_size'.
// During growth, memory contents are never copied. // During growth, memory contents are never copied. At the end of the
// buffer an amount of memory specified in 'seal_size' is reserved.
// When writing position reaches max_size - seal_size, buffer auto-seals
// itself with 'seal' and allows no further writes. Data pointed by
// 'seal' must be available during entire LogDynamicBuffer lifetime.
// //
// An instance of this class is created dynamically by Log. // An instance of this class is created dynamically by Log.
class LogDynamicBuffer { class LogDynamicBuffer {
public: public:
LogDynamicBuffer(int block_size, int max_size); LogDynamicBuffer(
int block_size, int max_size, const char* seal, int seal_size);
~LogDynamicBuffer(); ~LogDynamicBuffer();
@ -51,8 +56,9 @@ class LogDynamicBuffer {
// Writes 'data' to the buffer, making it larger if necessary. If // 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 // 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 // all. In that case, buffer auto-seals itself and stops to accept
// 0, if 'data' is too big). // any incoming writes. 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); int Write(const char* data, int data_size);
private: private:
@ -66,12 +72,19 @@ class LogDynamicBuffer {
int PosInBlock(int pos) const { return pos % block_size_; } int PosInBlock(int pos) const { return pos % block_size_; }
int Seal();
int WriteInternal(const char* data, int data_size);
const int block_size_; const int block_size_;
const int max_size_; const int max_size_;
const char* seal_;
const int seal_size_;
ScopedVector<char*> blocks_; ScopedVector<char*> blocks_;
int write_pos_; int write_pos_;
int block_index_; int block_index_;
int block_write_pos_; int block_write_pos_;
bool is_sealed_;
}; };
@ -87,6 +100,9 @@ class Log : public AllStatic {
// Opens memory buffer for logging. // Opens memory buffer for logging.
static void OpenMemoryBuffer(); static void OpenMemoryBuffer();
// Disables logging, but preserves acquired resources.
static void stop() { is_stopped_ = true; }
// Frees all resources acquired in Open... functions. // Frees all resources acquired in Open... functions.
static void Close(); static void Close();
@ -95,7 +111,7 @@ class Log : public AllStatic {
// Returns whether logging is enabled. // Returns whether logging is enabled.
static bool IsEnabled() { static bool IsEnabled() {
return output_handle_ != NULL || output_buffer_ != NULL; return !is_stopped_ && (output_handle_ != NULL || output_buffer_ != NULL);
} }
private: private:
@ -121,6 +137,9 @@ class Log : public AllStatic {
return output_buffer_->Write(msg, length); return output_buffer_->Write(msg, length);
} }
// Whether logging is stopped (e.g. due to insufficient resources).
static bool is_stopped_;
// When logging is active, either output_handle_ or output_buffer_ is used // 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 // to store a pointer to log destination. If logging was opened via OpenStdout
// or OpenFile, then output_handle_ is used. If logging was opened // or OpenFile, then output_handle_ is used. If logging was opened
@ -136,6 +155,9 @@ class Log : public AllStatic {
// Maximum size of dynamic buffer. // Maximum size of dynamic buffer.
static const int kMaxDynamicBufferSize = 50 * 1024 * 1024; static const int kMaxDynamicBufferSize = 50 * 1024 * 1024;
// Message to "seal" dynamic buffer with.
static const char* kDynamicBufferSeal;
// mutex_ is a Mutex used for enforcing exclusive // mutex_ is a Mutex used for enforcing exclusive
// access to the formatting buffer and the log file or log memory buffer. // access to the formatting buffer and the log file or log memory buffer.
static Mutex* mutex_; static Mutex* mutex_;
@ -180,7 +202,16 @@ class LogMessageBuilder BASE_EMBEDDED {
// Write a null-terminated string to to the log file currently opened. // Write a null-terminated string to to the log file currently opened.
void WriteCStringToLogFile(const char* str); void WriteCStringToLogFile(const char* str);
// A handler that is called when Log::Write fails.
typedef void (*WriteFailureHandler)();
static void set_write_failure_handler(WriteFailureHandler handler) {
write_failure_handler = handler;
}
private: private:
static WriteFailureHandler write_failure_handler;
ScopedLock sl; ScopedLock sl;
int pos_; int pos_;
}; };

View File

@ -813,12 +813,14 @@ void Logger::PauseProfiler() {
if (FLAG_prof_lazy) { if (FLAG_prof_lazy) {
if (!FLAG_sliding_state_window) ticker_->Stop(); if (!FLAG_sliding_state_window) ticker_->Stop();
FLAG_log_code = false; FLAG_log_code = false;
// Must be the same message as Log::kDynamicBufferSeal.
LOG(UncheckedStringEvent("profiler", "pause")); LOG(UncheckedStringEvent("profiler", "pause"));
} }
} }
void Logger::ResumeProfiler() { void Logger::ResumeProfiler() {
if (!Log::IsEnabled()) return;
if (FLAG_prof_lazy) { if (FLAG_prof_lazy) {
LOG(UncheckedStringEvent("profiler", "resume")); LOG(UncheckedStringEvent("profiler", "resume"));
FLAG_log_code = true; FLAG_log_code = true;
@ -829,6 +831,14 @@ void Logger::ResumeProfiler() {
} }
// This function can be called when Log's mutex is acquired,
// either from main or Profiler's thread.
void Logger::StopLoggingAndProfiling() {
Log::stop();
PauseProfiler();
}
bool Logger::IsProfilerSamplerActive() { bool Logger::IsProfilerSamplerActive() {
return ticker_->IsActive(); return ticker_->IsActive();
} }
@ -995,6 +1005,8 @@ bool Logger::Setup() {
profiler_->Engage(); profiler_->Engage();
} }
LogMessageBuilder::set_write_failure_handler(StopLoggingAndProfiling);
return true; return true;
#else #else
@ -1005,6 +1017,8 @@ bool Logger::Setup() {
void Logger::TearDown() { void Logger::TearDown() {
#ifdef ENABLE_LOGGING_AND_PROFILING #ifdef ENABLE_LOGGING_AND_PROFILING
LogMessageBuilder::set_write_failure_handler(NULL);
// Stop the profiler before closing the file. // Stop the profiler before closing the file.
if (profiler_ != NULL) { if (profiler_ != NULL) {
profiler_->Disengage(); profiler_->Disengage();

View File

@ -231,6 +231,9 @@ class Logger {
// Logs a StringEvent regardless of whether FLAG_log is true. // Logs a StringEvent regardless of whether FLAG_log is true.
static void UncheckedStringEvent(const char* name, const char* value); static void UncheckedStringEvent(const char* name, const char* value);
// Stops logging and profiling in case of insufficient resources.
static void StopLoggingAndProfiling();
// Returns whether profiler's sampler is active. // Returns whether profiler's sampler is active.
static bool IsProfilerSamplerActive(); static bool IsProfilerSamplerActive();

View File

@ -37,12 +37,14 @@ static int ReadData(
} }
// Helper function used by CHECK_EQ to compare Vectors. // Helper function used by CHECK_EQ to compare Vectors. Templatized to
// accept both "char" and "const char" vector contents.
template <typename E, typename V>
static inline void CheckEqualsHelper(const char* file, int line, static inline void CheckEqualsHelper(const char* file, int line,
const char* expected_source, const char* expected_source,
const Vector<char>& expected, const Vector<E>& expected,
const char* value_source, const char* value_source,
const Vector<char>& value) { const Vector<V>& value) {
if (expected.length() != value.length()) { if (expected.length() != value.length()) {
V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n" V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n"
"# Vectors lengths differ: %d expected, %d found", "# Vectors lengths differ: %d expected, %d found",
@ -62,7 +64,7 @@ static inline void CheckEqualsHelper(const char* file, int line,
TEST(DynaBufSingleBlock) { TEST(DynaBufSingleBlock) {
LogDynamicBuffer dynabuf(32, 32); LogDynamicBuffer dynabuf(32, 32, "", 0);
EmbeddedVector<char, 32> ref_buf; EmbeddedVector<char, 32> ref_buf;
WriteData(&dynabuf, &ref_buf); WriteData(&dynabuf, &ref_buf);
EmbeddedVector<char, 32> buf; EmbeddedVector<char, 32> buf;
@ -77,7 +79,7 @@ TEST(DynaBufSingleBlock) {
TEST(DynaBufCrossBlocks) { TEST(DynaBufCrossBlocks) {
LogDynamicBuffer dynabuf(32, 128); LogDynamicBuffer dynabuf(32, 128, "", 0);
EmbeddedVector<char, 48> ref_buf; EmbeddedVector<char, 48> ref_buf;
WriteData(&dynabuf, &ref_buf); WriteData(&dynabuf, &ref_buf);
CHECK_EQ(48, dynabuf.Write(ref_buf.start(), ref_buf.length())); CHECK_EQ(48, dynabuf.Write(ref_buf.start(), ref_buf.length()));
@ -93,7 +95,7 @@ TEST(DynaBufCrossBlocks) {
TEST(DynaBufReadTruncation) { TEST(DynaBufReadTruncation) {
LogDynamicBuffer dynabuf(32, 128); LogDynamicBuffer dynabuf(32, 128, "", 0);
EmbeddedVector<char, 128> ref_buf; EmbeddedVector<char, 128> ref_buf;
WriteData(&dynabuf, &ref_buf); WriteData(&dynabuf, &ref_buf);
EmbeddedVector<char, 128> buf; EmbeddedVector<char, 128> buf;
@ -105,4 +107,26 @@ TEST(DynaBufReadTruncation) {
CHECK_EQ(ref_buf.SubVector(128 - 32, 128), tail_buf.SubVector(0, 32)); CHECK_EQ(ref_buf.SubVector(128 - 32, 128), tail_buf.SubVector(0, 32));
} }
TEST(DynaBufSealing) {
const char* seal = "Sealed";
const int seal_size = strlen(seal);
LogDynamicBuffer dynabuf(32, 128, seal, seal_size);
EmbeddedVector<char, 100> ref_buf;
WriteData(&dynabuf, &ref_buf);
// Try to write data that will not fit in the buffer.
CHECK_EQ(0, dynabuf.Write(ref_buf.start(), 128 - 100 - seal_size + 1));
// Now the buffer is sealed, writing of any amount of data is forbidden.
CHECK_EQ(0, dynabuf.Write(ref_buf.start(), 1));
EmbeddedVector<char, 100> buf;
CHECK_EQ(100, ReadData(&dynabuf, 0, &buf));
CHECK_EQ(ref_buf, buf);
// Check the seal.
EmbeddedVector<char, 50> seal_buf;
CHECK_EQ(seal_size, ReadData(&dynabuf, 100, &seal_buf));
CHECK_EQ(v8::internal::CStrVector(seal), seal_buf.SubVector(0, seal_size));
// Verify that there's no data beyond the seal.
CHECK_EQ(0, ReadData(&dynabuf, 100 + seal_size, &buf));
}
#endif // ENABLE_LOGGING_AND_PROFILING #endif // ENABLE_LOGGING_AND_PROFILING