Chromium Code Reviews| 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 |