• 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/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