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
15 namespace v8 {
16 namespace internal {
17
18
19 const char* const Log::kLogToTemporaryFile = "&";
20 const char* const Log::kLogToConsole = "-";
21
22 // static
CreateOutputHandle(const char * file_name)23 FILE* 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
Log(Logger * logger,const char * file_name)36 Log::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
Close()68 FILE* 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
MessageBuilder(Log * log)86 Log::MessageBuilder::MessageBuilder(Log* log)
87 : log_(log), lock_guard_(&log_->mutex_) {
88 DCHECK_NOT_NULL(log_->format_buffer_);
89 }
90
AppendString(String * str,base::Optional<int> length_limit)91 void Log::MessageBuilder::AppendString(String* str,
92 base::Optional<int> length_limit) {
93 if (str == nullptr) 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
AppendString(Vector<const char> str)109 void Log::MessageBuilder::AppendString(Vector<const char> str) {
110 for (auto i = str.begin(); i < str.end(); i++) AppendCharacter(*i);
111 }
112
AppendString(const char * str)113 void Log::MessageBuilder::AppendString(const char* str) {
114 if (str == nullptr) return;
115 AppendString(str, strlen(str));
116 }
117
AppendString(const char * str,size_t length)118 void 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
AppendFormatString(const char * format,...)127 void 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
AppendCharacter(char c)138 void 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
AppendSymbolName(Symbol * symbol)158 void Log::MessageBuilder::AppendSymbolName(Symbol* symbol) {
159 DCHECK(symbol);
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
AppendSymbolNameDetails(String * str,bool show_impl_info)170 void Log::MessageBuilder::AppendSymbolNameDetails(String* str,
171 bool show_impl_info) {
172 if (str == nullptr) 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
FormatStringIntoBuffer(const char * format,va_list args)187 int 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
AppendRawFormatString(const char * format,...)198 void 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
AppendRawCharacter(char c)209 void Log::MessageBuilder::AppendRawCharacter(char c) { log_->os_ << c; }
210
WriteToLogFile()211 void Log::MessageBuilder::WriteToLogFile() { log_->os_ << std::endl; }
212
213 template <>
214 Log::MessageBuilder& Log::MessageBuilder::operator<<<const char*>(
215 const char* string) {
216 this->AppendString(string);
217 return *this;
218 }
219
220 template <>
221 Log::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
229 template <>
230 Log::MessageBuilder& Log::MessageBuilder::operator<<<char>(char c) {
231 this->AppendCharacter(c);
232 return *this;
233 }
234
235 template <>
236 Log::MessageBuilder& Log::MessageBuilder::operator<<<String*>(String* string) {
237 this->AppendString(string);
238 return *this;
239 }
240
241 template <>
242 Log::MessageBuilder& Log::MessageBuilder::operator<<<Symbol*>(Symbol* symbol) {
243 this->AppendSymbolName(symbol);
244 return *this;
245 }
246
247 template <>
248 Log::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
257 template <>
258 Log::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