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