Index: src/log-utils.cc |
diff --git a/src/log-utils.cc b/src/log-utils.cc |
index 43610497ef6382e72b3b73b9d68f8fa5781fa061..b0d7159df082d9a92944c2dec39c4b7ecd3d0702 100644 |
--- a/src/log-utils.cc |
+++ b/src/log-utils.cc |
@@ -127,11 +127,15 @@ LogDynamicBuffer* Log::output_buffer_ = NULL; |
const char* Log::kDynamicBufferSeal = "profiler,\"pause\"\n"; |
Mutex* Log::mutex_ = NULL; |
char* Log::message_buffer_ = NULL; |
+LogRecordCompressor* Log::record_compressor_ = NULL; |
void Log::Init() { |
mutex_ = OS::CreateMutex(); |
message_buffer_ = NewArray<char>(kMessageBufferSize); |
+ if (FLAG_compress_log) { |
+ record_compressor_ = new LogRecordCompressor(kRecordCompressorWindow); |
+ } |
} |
@@ -173,6 +177,12 @@ void Log::Close() { |
} |
Write = NULL; |
+ delete record_compressor_; |
+ record_compressor_ = NULL; |
+ |
+ DeleteArray(message_buffer_); |
+ message_buffer_ = NULL; |
+ |
delete mutex_; |
mutex_ = NULL; |
@@ -280,6 +290,29 @@ void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) { |
} |
+bool LogMessageBuilder::StoreInCompressor() { |
+ if (!FLAG_compress_log) return true; |
+ ASSERT(Log::record_compressor_ != NULL); |
+ return Log::record_compressor_->Store( |
+ Vector<const char>(Log::message_buffer_, pos_)); |
+} |
+ |
+ |
+bool LogMessageBuilder::RetrieveCompressedPrevious(const char* prefix) { |
+ if (!FLAG_compress_log) return true; |
+ ASSERT(Log::record_compressor_ != NULL); |
+ pos_ = 0; |
+ if (prefix[0] != '\0') Append(prefix); |
+ Vector<char> prev_record(Log::message_buffer_ + pos_, |
+ Log::kMessageBufferSize - pos_); |
+ const bool has_previous = |
+ Log::record_compressor_->RetrievePreviousCompressed(&prev_record); |
+ if (!has_previous) return false; |
+ pos_ += prev_record.length(); |
+ return true; |
+} |
+ |
+ |
void LogMessageBuilder::WriteToLogFile() { |
ASSERT(pos_ <= Log::kMessageBufferSize); |
const int written = Log::Write(Log::message_buffer_, pos_); |
@@ -297,6 +330,119 @@ void LogMessageBuilder::WriteCStringToLogFile(const char* str) { |
} |
} |
+ |
+// Formatting string for back references. E.g. "#2:4" means |
+// "the second line above, start from the 4th field (0-based)". |
+const char* LogRecordCompressor::kBackwardReferenceFormat = "#%d:%d"; |
+ |
+ |
+LogRecordCompressor::~LogRecordCompressor() { |
+ for (int i = 0; i < buffer_.length(); ++i) { |
+ buffer_[i].Dispose(); |
+ } |
+} |
+ |
+ |
+bool LogRecordCompressor::Store(const Vector<const char>& record) { |
+ // Check if the record is the same as the last stored one. |
+ if (curr_ != -1) { |
+ Vector<const char>& curr = buffer_[curr_]; |
+ if (record.length() == curr.length() |
+ && strncmp(record.start(), curr.start(), record.length()) == 0) { |
+ return false; |
+ } |
+ } |
+ prev_ = curr_++; |
Søren Thygesen Gjesse
2009/06/11 11:13:56
Maybe add a comment here saying that we keep a cir
Mikhail Naganov
2009/06/11 14:07:12
Done.
|
+ curr_ %= buffer_.length(); |
+ Vector<char> record_copy = Vector<char>::New(record.length()); |
+ memcpy(record_copy.start(), record.start(), record.length()); |
+ buffer_[curr_].Dispose(); |
+ buffer_[curr_] = |
+ Vector<const char>(record_copy.start(), record_copy.length()); |
+ return true; |
+} |
+ |
+ |
+bool LogRecordCompressor::RetrievePreviousCompressed( |
+ Vector<char>* prev_record) { |
+ if (prev_ == -1) return false; |
+ |
+ int index = prev_; |
+ // Distance from prev_. |
+ int distance = 0; |
+ // Best compression result among records in the buffer. |
+ struct { |
+ intptr_t truncated_len; |
+ int distance; |
+ int field_num; |
+ } best = {-1, 0, 0}; |
+ Vector<const char>& prev = buffer_[prev_]; |
+ const char* const prev_end = prev.start() + prev.length(); |
+ do { |
+ // We're moving backwards until we reach the current record. |
+ if (--index == -1) index = buffer_.length() - 1; |
+ ++distance; |
+ if (index == curr_) break; |
+ |
+ Vector<const char>& data = buffer_[index]; |
+ if (data.start() == NULL) break; |
+ const char* const data_end = data.start() + data.length(); |
+ const char* prev_ptr = prev_end; |
+ const char* data_ptr = data_end; |
+ // Compare strings backwards, stop on the last matching character. |
Søren Thygesen Gjesse
2009/06/11 11:13:56
I am not sure how expensive this is, but perhaps c
Mikhail Naganov
2009/06/11 14:07:12
I'm aware of this optimization, but don't want to
|
+ while (prev_ptr != prev.start() && data_ptr != data.start() |
+ && *(prev_ptr - 1) == *(data_ptr - 1)) { |
+ --prev_ptr; |
+ --data_ptr; |
+ } |
+ if (prev_end - prev_ptr < kUncompressibleBound) continue; |
+ |
+ // Align to the field boundary. |
+ do { |
+ ++prev_ptr; |
+ ++data_ptr; |
+ } while (prev_ptr != prev_end |
+ && (prev_ptr != prev.start() && *(prev_ptr - 1) != kFieldSeparator |
Søren Thygesen Gjesse
2009/06/11 11:13:56
Will prev_ptr ever become prev.start() as the do l
Mikhail Naganov
2009/06/11 14:07:12
I've removed field alignment, so this code has gon
|
+ || data_ptr != data.start() |
+ && *(data_ptr - 1) != kFieldSeparator)); |
+ // Check if the record is still compressible. |
+ const intptr_t truncated_len = prev_end - prev_ptr; |
+ if (truncated_len < kUncompressibleBound) continue; |
+ |
+ // Determine reference field number. |
+ int field_num = 0; |
+ for (const char* fld_ptr = data.start(); |
+ fld_ptr != data_ptr; ++fld_ptr) { |
+ if (*fld_ptr == kFieldSeparator) field_num++; |
+ } |
+ // Record compression results. |
+ if (truncated_len > best.truncated_len) { |
+ best.truncated_len = truncated_len; |
+ best.distance = distance; |
+ best.field_num = field_num; |
+ } |
+ } while (true); |
+ |
+ if (best.distance == 0) { |
+ // Can't compress the previous record. Return as is. |
+ ASSERT(prev_record->length() >= prev.length()); |
+ memcpy(prev_record->start(), prev.start(), prev.length()); |
+ prev_record->Truncate(prev.length()); |
+ } else { |
+ // Copy the uncompressible part unchanged. |
+ const intptr_t unchanged_len = prev.length() - best.truncated_len; |
+ ASSERT(prev_record->length() >= unchanged_len + kUncompressibleBound); |
+ memcpy(prev_record->start(), prev.start(), unchanged_len); |
+ // Append the backward reference. |
+ Vector<char> patch(prev_record->start() + unchanged_len, |
+ kUncompressibleBound); |
+ OS::SNPrintF(patch, kBackwardReferenceFormat, |
+ best.distance, best.field_num); |
+ prev_record->Truncate(unchanged_len + strlen(patch.start())); |
+ } |
+ return true; |
+} |
+ |
#endif // ENABLE_LOGGING_AND_PROFILING |
} } // namespace v8::internal |