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