• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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