#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),
max_size_(max_size - (max_size % block_size_)),
+ seal_(seal),
+ seal_size_(seal_size),
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);
AllocateBlock(0);
for (int i = 1; i < BlocksCount(); ++i) {
}
+int LogDynamicBuffer::Seal() {
+ WriteInternal(seal_, seal_size_);
+ is_sealed_ = true;
+ return 0;
+}
+
+
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;
while (data_pos < data_size) {
const int write_size =
}
+bool Log::is_stopped_ = false;
Log::WritePtr Log::Write = NULL;
FILE* Log::output_handle_ = 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;
char* Log::message_buffer_ = NULL;
void Log::OpenMemoryBuffer() {
ASSERT(!IsEnabled());
output_buffer_ = new LogDynamicBuffer(
- kDynamicBufferBlockSize, kMaxDynamicBufferSize);
+ kDynamicBufferBlockSize, kMaxDynamicBufferSize,
+ kDynamicBufferSeal, strlen(kDynamicBufferSeal));
Write = WriteToMemory;
Init();
}
delete mutex_;
mutex_ = NULL;
+
+ is_stopped_ = false;
}
}
+LogMessageBuilder::WriteFailureHandler
+ LogMessageBuilder::write_failure_handler = NULL;
+
+
LogMessageBuilder::LogMessageBuilder(): sl(Log::mutex_), pos_(0) {
ASSERT(Log::message_buffer_ != NULL);
}
void LogMessageBuilder::WriteToLogFile() {
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) {
- int len = strlen(str);
- Log::Write(str, len);
+ const int len = strlen(str);
+ const int written = Log::Write(str, len);
+ if (written != len && write_failure_handler != NULL) {
+ write_failure_handler();
+ }
}
#endif // 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.
+// 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.
class LogDynamicBuffer {
public:
- LogDynamicBuffer(int block_size, int max_size);
+ LogDynamicBuffer(
+ int block_size, int max_size, const char* seal, int seal_size);
~LogDynamicBuffer();
// 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).
+ // all. In that case, buffer auto-seals itself and stops to accept
+ // 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);
private:
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 max_size_;
+ const char* seal_;
+ const int seal_size_;
ScopedVector<char*> blocks_;
int write_pos_;
int block_index_;
int block_write_pos_;
+ bool is_sealed_;
};
// Opens memory buffer for logging.
static void OpenMemoryBuffer();
+ // Disables logging, but preserves acquired resources.
+ static void stop() { is_stopped_ = true; }
+
// Frees all resources acquired in Open... functions.
static void Close();
// Returns whether logging is enabled.
static bool IsEnabled() {
- return output_handle_ != NULL || output_buffer_ != NULL;
+ return !is_stopped_ && (output_handle_ != NULL || output_buffer_ != NULL);
}
private:
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
// to store a pointer to log destination. If logging was opened via OpenStdout
// or OpenFile, then output_handle_ is used. If logging was opened
// Maximum size of dynamic buffer.
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
// access to the formatting buffer and the log file or log memory buffer.
static Mutex* mutex_;
// Write a null-terminated string to to the log file currently opened.
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:
+ static WriteFailureHandler write_failure_handler;
+
ScopedLock sl;
int pos_;
};
if (FLAG_prof_lazy) {
if (!FLAG_sliding_state_window) ticker_->Stop();
FLAG_log_code = false;
+ // Must be the same message as Log::kDynamicBufferSeal.
LOG(UncheckedStringEvent("profiler", "pause"));
}
}
void Logger::ResumeProfiler() {
+ if (!Log::IsEnabled()) return;
if (FLAG_prof_lazy) {
LOG(UncheckedStringEvent("profiler", "resume"));
FLAG_log_code = true;
}
+// 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() {
return ticker_->IsActive();
}
profiler_->Engage();
}
+ LogMessageBuilder::set_write_failure_handler(StopLoggingAndProfiling);
+
return true;
#else
void Logger::TearDown() {
#ifdef ENABLE_LOGGING_AND_PROFILING
+ LogMessageBuilder::set_write_failure_handler(NULL);
+
// Stop the profiler before closing the file.
if (profiler_ != NULL) {
profiler_->Disengage();
// Logs a StringEvent regardless of whether FLAG_log is true.
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.
static bool IsProfilerSamplerActive();
}
-// 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,
const char* expected_source,
- const Vector<char>& expected,
+ const Vector<E>& expected,
const char* value_source,
- const Vector<char>& value) {
+ const Vector<V>& value) {
if (expected.length() != value.length()) {
V8_Fatal(file, line, "CHECK_EQ(%s, %s) failed\n"
"# Vectors lengths differ: %d expected, %d found",
TEST(DynaBufSingleBlock) {
- LogDynamicBuffer dynabuf(32, 32);
+ LogDynamicBuffer dynabuf(32, 32, "", 0);
EmbeddedVector<char, 32> ref_buf;
WriteData(&dynabuf, &ref_buf);
EmbeddedVector<char, 32> buf;
TEST(DynaBufCrossBlocks) {
- LogDynamicBuffer dynabuf(32, 128);
+ LogDynamicBuffer dynabuf(32, 128, "", 0);
EmbeddedVector<char, 48> ref_buf;
WriteData(&dynabuf, &ref_buf);
CHECK_EQ(48, dynabuf.Write(ref_buf.start(), ref_buf.length()));
TEST(DynaBufReadTruncation) {
- LogDynamicBuffer dynabuf(32, 128);
+ LogDynamicBuffer dynabuf(32, 128, "", 0);
EmbeddedVector<char, 128> ref_buf;
WriteData(&dynabuf, &ref_buf);
EmbeddedVector<char, 128> buf;
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