1// Copyright 2009 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "src/logging/log-utils.h" 6 7#include <atomic> 8#include <memory> 9 10#include "src/base/platform/mutex.h" 11#include "src/base/platform/platform.h" 12#include "src/base/strings.h" 13#include "src/base/vector.h" 14#include "src/common/assert-scope.h" 15#include "src/common/globals.h" 16#include "src/execution/isolate-utils.h" 17#include "src/objects/objects-inl.h" 18#include "src/objects/string-inl.h" 19#include "src/strings/string-stream.h" 20#include "src/utils/version.h" 21 22namespace v8 { 23namespace internal { 24 25const char* const Log::kLogToTemporaryFile = "+"; 26const char* const Log::kLogToConsole = "-"; 27 28// static 29FILE* Log::CreateOutputHandle(std::string file_name) { 30 // If we're logging anything, we need to open the log file. 31 if (!FLAG_log) { 32 return nullptr; 33 } else if (Log::IsLoggingToConsole(file_name)) { 34 return stdout; 35 } else if (Log::IsLoggingToTemporaryFile(file_name)) { 36 return base::OS::OpenTemporaryFile(); 37 } else { 38 return base::OS::FOpen(file_name.c_str(), base::OS::LogFileOpenMode); 39 } 40} 41 42// static 43bool Log::IsLoggingToConsole(std::string file_name) { 44 return file_name.compare(Log::kLogToConsole) == 0; 45} 46 47// static 48bool Log::IsLoggingToTemporaryFile(std::string file_name) { 49 return file_name.compare(Log::kLogToTemporaryFile) == 0; 50} 51 52Log::Log(Logger* logger, std::string file_name) 53 : logger_(logger), 54 file_name_(file_name), 55 output_handle_(Log::CreateOutputHandle(file_name)), 56 os_(output_handle_ == nullptr ? stdout : output_handle_), 57 format_buffer_(NewArray<char>(kMessageBufferSize)) { 58 if (output_handle_) WriteLogHeader(); 59} 60 61void Log::WriteLogHeader() { 62 Log::MessageBuilder msg(this); 63 LogSeparator kNext = LogSeparator::kSeparator; 64 msg << "v8-version" << kNext << Version::GetMajor() << kNext 65 << Version::GetMinor() << kNext << Version::GetBuild() << kNext 66 << Version::GetPatch(); 67 if (strlen(Version::GetEmbedder()) != 0) { 68 msg << kNext << Version::GetEmbedder(); 69 } 70 msg << kNext << Version::IsCandidate(); 71 msg.WriteToLogFile(); 72 msg << "v8-platform" << kNext << V8_OS_STRING << kNext << V8_TARGET_OS_STRING; 73 msg.WriteToLogFile(); 74} 75 76std::unique_ptr<Log::MessageBuilder> Log::NewMessageBuilder() { 77 // Fast check of is_logging() without taking the lock. Bail out immediately if 78 // logging isn't enabled. 79 if (!logger_->is_logging()) return {}; 80 81 std::unique_ptr<Log::MessageBuilder> result(new Log::MessageBuilder(this)); 82 83 // The first invocation of is_logging() might still read an old value. It is 84 // fine if a background thread starts logging a bit later, but we want to 85 // avoid background threads continue logging after logging was already closed. 86 if (!logger_->is_logging()) return {}; 87 DCHECK_NOT_NULL(format_buffer_.get()); 88 89 return result; 90} 91 92FILE* Log::Close() { 93 FILE* result = nullptr; 94 if (output_handle_ != nullptr) { 95 fflush(output_handle_); 96 result = output_handle_; 97 } 98 output_handle_ = nullptr; 99 format_buffer_.reset(); 100 return result; 101} 102 103std::string Log::file_name() const { return file_name_; } 104 105Log::MessageBuilder::MessageBuilder(Log* log) 106 : log_(log), lock_guard_(&log_->mutex_) { 107} 108 109void Log::MessageBuilder::AppendString(String str, 110 base::Optional<int> length_limit) { 111 if (str.is_null()) return; 112 113 DisallowGarbageCollection no_gc; // Ensure string stays valid. 114 PtrComprCageBase cage_base = GetPtrComprCageBase(str); 115 SharedStringAccessGuardIfNeeded access_guard(str); 116 int length = str.length(); 117 if (length_limit) length = std::min(length, *length_limit); 118 for (int i = 0; i < length; i++) { 119 uint16_t c = str.Get(i, cage_base, access_guard); 120 if (c <= 0xFF) { 121 AppendCharacter(static_cast<char>(c)); 122 } else { 123 // Escape non-ascii characters. 124 AppendRawFormatString("\\u%04x", c & 0xFFFF); 125 } 126 } 127} 128 129void Log::MessageBuilder::AppendString(base::Vector<const char> str) { 130 for (auto i = str.begin(); i < str.end(); i++) AppendCharacter(*i); 131} 132 133void Log::MessageBuilder::AppendString(const char* str) { 134 if (str == nullptr) return; 135 AppendString(str, strlen(str)); 136} 137 138void Log::MessageBuilder::AppendString(const char* str, size_t length, 139 bool is_one_byte) { 140 if (str == nullptr) return; 141 if (is_one_byte) { 142 for (size_t i = 0; i < length; i++) { 143 DCHECK_IMPLIES(is_one_byte, str[i] != '\0'); 144 AppendCharacter(str[i]); 145 } 146 } else { 147 DCHECK_EQ(length % 2, 0); 148 for (size_t i = 0; i + 1 < length; i += 2) { 149 AppendTwoByteCharacter(str[i], str[i + 1]); 150 } 151 } 152} 153 154void Log::MessageBuilder::AppendFormatString(const char* format, ...) { 155 va_list args; 156 va_start(args, format); 157 const int length = FormatStringIntoBuffer(format, args); 158 va_end(args); 159 for (int i = 0; i < length; i++) { 160 DCHECK_NE(log_->format_buffer_[i], '\0'); 161 AppendCharacter(log_->format_buffer_[i]); 162 } 163} 164 165void Log::MessageBuilder::AppendTwoByteCharacter(char c1, char c2) { 166 if (c2 == 0) { 167 AppendCharacter(c1); 168 } else { 169 // Escape non-printable characters. 170 AppendRawFormatString("\\u%02x%02x", c1 & 0xFF, c2 & 0xFF); 171 } 172} 173void Log::MessageBuilder::AppendCharacter(char c) { 174 if (c >= 32 && c <= 126) { 175 if (c == ',') { 176 // Escape commas to avoid adding column separators. 177 AppendRawFormatString("\\x2C"); 178 } else if (c == '\\') { 179 AppendRawFormatString("\\\\"); 180 } else { 181 // Safe, printable ascii character. 182 AppendRawCharacter(c); 183 } 184 } else if (c == '\n') { 185 // Escape newlines to avoid adding row separators. 186 AppendRawFormatString("\\n"); 187 } else { 188 // Escape non-printable characters. 189 AppendRawFormatString("\\x%02x", c & 0xFF); 190 } 191} 192 193void Log::MessageBuilder::AppendSymbolName(Symbol symbol) { 194 DCHECK(!symbol.is_null()); 195 OFStream& os = log_->os_; 196 os << "symbol("; 197 if (!symbol.description().IsUndefined()) { 198 os << "\""; 199 AppendSymbolNameDetails(String::cast(symbol.description()), false); 200 os << "\" "; 201 } 202 os << "hash " << std::hex << symbol.hash() << std::dec << ")"; 203} 204 205void Log::MessageBuilder::AppendSymbolNameDetails(String str, 206 bool show_impl_info) { 207 if (str.is_null()) return; 208 209 DisallowGarbageCollection no_gc; // Ensure string stays valid. 210 OFStream& os = log_->os_; 211 int limit = str.length(); 212 if (limit > 0x1000) limit = 0x1000; 213 if (show_impl_info) { 214 os << (str.IsOneByteRepresentation() ? 'a' : '2'); 215 if (StringShape(str).IsExternal()) os << 'e'; 216 if (StringShape(str).IsInternalized()) os << '#'; 217 os << ':' << str.length() << ':'; 218 } 219 AppendString(str, limit); 220} 221 222int Log::MessageBuilder::FormatStringIntoBuffer(const char* format, 223 va_list args) { 224 base::Vector<char> buf(log_->format_buffer_.get(), Log::kMessageBufferSize); 225 int length = base::VSNPrintF(buf, format, args); 226 // |length| is -1 if output was truncated. 227 if (length == -1) length = Log::kMessageBufferSize; 228 DCHECK_LE(length, Log::kMessageBufferSize); 229 DCHECK_GE(length, 0); 230 return length; 231} 232 233void Log::MessageBuilder::AppendRawFormatString(const char* format, ...) { 234 va_list args; 235 va_start(args, format); 236 const int length = FormatStringIntoBuffer(format, args); 237 va_end(args); 238 for (int i = 0; i < length; i++) { 239 DCHECK_NE(log_->format_buffer_[i], '\0'); 240 AppendRawCharacter(log_->format_buffer_[i]); 241 } 242} 243 244void Log::MessageBuilder::AppendRawCharacter(char c) { log_->os_ << c; } 245 246void Log::MessageBuilder::WriteToLogFile() { 247 log_->os_ << std::endl; 248} 249 250template <> 251Log::MessageBuilder& Log::MessageBuilder::operator<<<const char*>( 252 const char* string) { 253 this->AppendString(string); 254 return *this; 255} 256 257template <> 258Log::MessageBuilder& Log::MessageBuilder::operator<<<void*>(void* pointer) { 259 OFStream& os = log_->os_; 260 // Manually format the pointer since on Windows we do not consistently 261 // get a "0x" prefix. 262 os << "0x" << std::hex << reinterpret_cast<intptr_t>(pointer) << std::dec; 263 return *this; 264} 265 266template <> 267Log::MessageBuilder& Log::MessageBuilder::operator<<<char>(char c) { 268 this->AppendCharacter(c); 269 return *this; 270} 271 272template <> 273Log::MessageBuilder& Log::MessageBuilder::operator<<<String>(String string) { 274 this->AppendString(string); 275 return *this; 276} 277 278template <> 279Log::MessageBuilder& Log::MessageBuilder::operator<<<Symbol>(Symbol symbol) { 280 this->AppendSymbolName(symbol); 281 return *this; 282} 283 284template <> 285Log::MessageBuilder& Log::MessageBuilder::operator<<<Name>(Name name) { 286 if (name.IsString()) { 287 this->AppendString(String::cast(name)); 288 } else { 289 this->AppendSymbolName(Symbol::cast(name)); 290 } 291 return *this; 292} 293 294template <> 295Log::MessageBuilder& Log::MessageBuilder::operator<<<LogSeparator>( 296 LogSeparator separator) { 297 // Skip escaping to create a new column. 298 this->AppendRawCharacter(','); 299 return *this; 300} 301 302} // namespace internal 303} // namespace v8 304