// Copyright 2009 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/log-utils.h" #include "src/assert-scope.h" #include "src/base/platform/platform.h" #include "src/objects-inl.h" #include "src/string-stream.h" #include "src/utils.h" #include "src/version.h" namespace v8 { namespace internal { const char* const Log::kLogToTemporaryFile = "&"; const char* const Log::kLogToConsole = "-"; Log::Log(Logger* logger) : is_stopped_(false), output_handle_(NULL), message_buffer_(NULL), logger_(logger) { } void Log::Initialize(const char* log_file_name) { message_buffer_ = NewArray(kMessageBufferSize); // --log-all enables all the log flags. if (FLAG_log_all) { FLAG_log_api = true; FLAG_log_code = true; FLAG_log_gc = true; FLAG_log_suspect = true; FLAG_log_handles = true; FLAG_log_regexp = true; FLAG_log_internal_timer_events = true; } // --prof implies --log-code. if (FLAG_prof) FLAG_log_code = true; // If we're logging anything, we need to open the log file. if (Log::InitLogAtStart()) { if (strcmp(log_file_name, kLogToConsole) == 0) { OpenStdout(); } else if (strcmp(log_file_name, kLogToTemporaryFile) == 0) { OpenTemporaryFile(); } else { OpenFile(log_file_name); } if (output_handle_ != nullptr) { Log::MessageBuilder msg(this); msg.Append("v8-version,%d,%d,%d,%d,%d", Version::GetMajor(), Version::GetMinor(), Version::GetBuild(), Version::GetPatch(), Version::IsCandidate()); msg.WriteToLogFile(); } } } void Log::OpenStdout() { DCHECK(!IsEnabled()); output_handle_ = stdout; } void Log::OpenTemporaryFile() { DCHECK(!IsEnabled()); output_handle_ = base::OS::OpenTemporaryFile(); } void Log::OpenFile(const char* name) { DCHECK(!IsEnabled()); output_handle_ = base::OS::FOpen(name, base::OS::LogFileOpenMode); } FILE* Log::Close() { FILE* result = NULL; if (output_handle_ != NULL) { if (strcmp(FLAG_logfile, kLogToTemporaryFile) != 0) { fclose(output_handle_); } else { result = output_handle_; } } output_handle_ = NULL; DeleteArray(message_buffer_); message_buffer_ = NULL; is_stopped_ = false; return result; } Log::MessageBuilder::MessageBuilder(Log* log) : log_(log), lock_guard_(&log_->mutex_), pos_(0) { DCHECK(log_->message_buffer_ != NULL); } void Log::MessageBuilder::Append(const char* format, ...) { Vector buf(log_->message_buffer_ + pos_, Log::kMessageBufferSize - pos_); va_list args; va_start(args, format); AppendVA(format, args); va_end(args); DCHECK(pos_ <= Log::kMessageBufferSize); } void Log::MessageBuilder::AppendVA(const char* format, va_list args) { Vector buf(log_->message_buffer_ + pos_, Log::kMessageBufferSize - pos_); int result = v8::internal::VSNPrintF(buf, format, args); // Result is -1 if output was truncated. if (result >= 0) { pos_ += result; } else { pos_ = Log::kMessageBufferSize; } DCHECK(pos_ <= Log::kMessageBufferSize); } void Log::MessageBuilder::Append(const char c) { if (pos_ < Log::kMessageBufferSize) { log_->message_buffer_[pos_++] = c; } DCHECK(pos_ <= Log::kMessageBufferSize); } void Log::MessageBuilder::AppendDoubleQuotedString(const char* string) { Append('"'); for (const char* p = string; *p != '\0'; p++) { if (*p == '"') { Append('\\'); } Append(*p); } Append('"'); } void Log::MessageBuilder::Append(String* str) { DisallowHeapAllocation no_gc; // Ensure string stay valid. int length = str->length(); for (int i = 0; i < length; i++) { Append(static_cast(str->Get(i))); } } void Log::MessageBuilder::AppendAddress(Address addr) { Append("0x%" V8PRIxPTR, addr); } void Log::MessageBuilder::AppendSymbolName(Symbol* symbol) { DCHECK(symbol); Append("symbol("); if (!symbol->name()->IsUndefined()) { Append("\""); AppendDetailed(String::cast(symbol->name()), false); Append("\" "); } Append("hash %x)", symbol->Hash()); } void Log::MessageBuilder::AppendDetailed(String* str, bool show_impl_info) { if (str == NULL) return; DisallowHeapAllocation no_gc; // Ensure string stay valid. int len = str->length(); if (len > 0x1000) len = 0x1000; if (show_impl_info) { Append(str->IsOneByteRepresentation() ? 'a' : '2'); if (StringShape(str).IsExternal()) Append('e'); if (StringShape(str).IsInternalized()) Append('#'); Append(":%i:", str->length()); } for (int i = 0; i < len; i++) { uc32 c = str->Get(i); if (c > 0xff) { Append("\\u%04x", c); } else if (c < 32 || c > 126) { Append("\\x%02x", c); } else if (c == ',') { Append("\\,"); } else if (c == '\\') { Append("\\\\"); } else if (c == '\"') { Append("\"\""); } else { Append("%lc", c); } } } void Log::MessageBuilder::AppendStringPart(const char* str, int len) { if (pos_ + len > Log::kMessageBufferSize) { len = Log::kMessageBufferSize - pos_; DCHECK(len >= 0); if (len == 0) return; } Vector buf(log_->message_buffer_ + pos_, Log::kMessageBufferSize - pos_); StrNCpy(buf, str, len); pos_ += len; DCHECK(pos_ <= Log::kMessageBufferSize); } void Log::MessageBuilder::WriteToLogFile() { DCHECK(pos_ <= Log::kMessageBufferSize); // Assert that we do not already have a new line at the end. DCHECK(pos_ == 0 || log_->message_buffer_[pos_ - 1] != '\n'); if (pos_ == Log::kMessageBufferSize) pos_--; log_->message_buffer_[pos_++] = '\n'; const int written = log_->WriteToFile(log_->message_buffer_, pos_); if (written != pos_) { log_->stop(); log_->logger_->LogFailure(); } } } // namespace internal } // namespace v8