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
22 namespace v8 {
23 namespace internal {
24
25 const char* const Log::kLogToTemporaryFile = "+";
26 const char* const Log::kLogToConsole = "-";
27
28 // static
CreateOutputHandle(std::string file_name)29 FILE* 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
IsLoggingToConsole(std::string file_name)43 bool Log::IsLoggingToConsole(std::string file_name) {
44 return file_name.compare(Log::kLogToConsole) == 0;
45 }
46
47 // static
IsLoggingToTemporaryFile(std::string file_name)48 bool Log::IsLoggingToTemporaryFile(std::string file_name) {
49 return file_name.compare(Log::kLogToTemporaryFile) == 0;
50 }
51
Log(Logger * logger,std::string file_name)52 Log::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
WriteLogHeader()61 void 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
NewMessageBuilder()76 std::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
Close()92 FILE* 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
file_name() const103 std::string Log::file_name() const { return file_name_; }
104
MessageBuilder(Log * log)105 Log::MessageBuilder::MessageBuilder(Log* log)
106 : log_(log), lock_guard_(&log_->mutex_) {
107 }
108
AppendString(String str,base::Optional<int> length_limit)109 void 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
AppendString(base::Vector<const char> str)129 void Log::MessageBuilder::AppendString(base::Vector<const char> str) {
130 for (auto i = str.begin(); i < str.end(); i++) AppendCharacter(*i);
131 }
132
AppendString(const char * str)133 void Log::MessageBuilder::AppendString(const char* str) {
134 if (str == nullptr) return;
135 AppendString(str, strlen(str));
136 }
137
AppendString(const char * str,size_t length,bool is_one_byte)138 void 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
AppendFormatString(const char * format,...)154 void 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
AppendTwoByteCharacter(char c1,char c2)165 void 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 }
AppendCharacter(char c)173 void 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
AppendSymbolName(Symbol symbol)193 void 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
AppendSymbolNameDetails(String str,bool show_impl_info)205 void 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
FormatStringIntoBuffer(const char * format,va_list args)222 int 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
AppendRawFormatString(const char * format,...)233 void 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
AppendRawCharacter(char c)244 void Log::MessageBuilder::AppendRawCharacter(char c) { log_->os_ << c; }
245
WriteToLogFile()246 void Log::MessageBuilder::WriteToLogFile() {
247 log_->os_ << std::endl;
248 }
249
250 template <>
251 Log::MessageBuilder& Log::MessageBuilder::operator<<<const char*>(
252 const char* string) {
253 this->AppendString(string);
254 return *this;
255 }
256
257 template <>
258 Log::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
266 template <>
267 Log::MessageBuilder& Log::MessageBuilder::operator<<<char>(char c) {
268 this->AppendCharacter(c);
269 return *this;
270 }
271
272 template <>
273 Log::MessageBuilder& Log::MessageBuilder::operator<<<String>(String string) {
274 this->AppendString(string);
275 return *this;
276 }
277
278 template <>
279 Log::MessageBuilder& Log::MessageBuilder::operator<<<Symbol>(Symbol symbol) {
280 this->AppendSymbolName(symbol);
281 return *this;
282 }
283
284 template <>
285 Log::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
294 template <>
295 Log::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