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/version.h"
13
14 namespace v8 {
15 namespace internal {
16
17
18 const char* const Log::kLogToTemporaryFile = "&";
19 const char* const Log::kLogToConsole = "-";
20
21
Log(Logger * logger)22 Log::Log(Logger* logger)
23 : is_stopped_(false),
24 output_handle_(NULL),
25 message_buffer_(NULL),
26 logger_(logger) {
27 }
28
29
Initialize(const char * log_file_name)30 void Log::Initialize(const char* log_file_name) {
31 message_buffer_ = NewArray<char>(kMessageBufferSize);
32
33 // --log-all enables all the log flags.
34 if (FLAG_log_all) {
35 FLAG_log_api = true;
36 FLAG_log_code = true;
37 FLAG_log_gc = true;
38 FLAG_log_suspect = true;
39 FLAG_log_handles = true;
40 FLAG_log_internal_timer_events = true;
41 }
42
43 // --prof implies --log-code.
44 if (FLAG_prof) FLAG_log_code = true;
45
46 // If we're logging anything, we need to open the log file.
47 if (Log::InitLogAtStart()) {
48 if (strcmp(log_file_name, kLogToConsole) == 0) {
49 OpenStdout();
50 } else if (strcmp(log_file_name, kLogToTemporaryFile) == 0) {
51 OpenTemporaryFile();
52 } else {
53 OpenFile(log_file_name);
54 }
55
56 if (output_handle_ != nullptr) {
57 Log::MessageBuilder msg(this);
58 msg.Append("v8-version,%d,%d,%d,%d,%d", Version::GetMajor(),
59 Version::GetMinor(), Version::GetBuild(), Version::GetPatch(),
60 Version::IsCandidate());
61 msg.WriteToLogFile();
62 }
63 }
64 }
65
66
OpenStdout()67 void Log::OpenStdout() {
68 DCHECK(!IsEnabled());
69 output_handle_ = stdout;
70 }
71
72
OpenTemporaryFile()73 void Log::OpenTemporaryFile() {
74 DCHECK(!IsEnabled());
75 output_handle_ = base::OS::OpenTemporaryFile();
76 }
77
78
OpenFile(const char * name)79 void Log::OpenFile(const char* name) {
80 DCHECK(!IsEnabled());
81 output_handle_ = base::OS::FOpen(name, base::OS::LogFileOpenMode);
82 }
83
84
Close()85 FILE* Log::Close() {
86 FILE* result = NULL;
87 if (output_handle_ != NULL) {
88 if (strcmp(FLAG_logfile, kLogToTemporaryFile) != 0) {
89 fclose(output_handle_);
90 } else {
91 result = output_handle_;
92 }
93 }
94 output_handle_ = NULL;
95
96 DeleteArray(message_buffer_);
97 message_buffer_ = NULL;
98
99 is_stopped_ = false;
100 return result;
101 }
102
103
MessageBuilder(Log * log)104 Log::MessageBuilder::MessageBuilder(Log* log)
105 : log_(log),
106 lock_guard_(&log_->mutex_),
107 pos_(0) {
108 DCHECK(log_->message_buffer_ != NULL);
109 }
110
111
Append(const char * format,...)112 void Log::MessageBuilder::Append(const char* format, ...) {
113 Vector<char> buf(log_->message_buffer_ + pos_,
114 Log::kMessageBufferSize - pos_);
115 va_list args;
116 va_start(args, format);
117 AppendVA(format, args);
118 va_end(args);
119 DCHECK(pos_ <= Log::kMessageBufferSize);
120 }
121
122
AppendVA(const char * format,va_list args)123 void Log::MessageBuilder::AppendVA(const char* format, va_list args) {
124 Vector<char> buf(log_->message_buffer_ + pos_,
125 Log::kMessageBufferSize - pos_);
126 int result = v8::internal::VSNPrintF(buf, format, args);
127
128 // Result is -1 if output was truncated.
129 if (result >= 0) {
130 pos_ += result;
131 } else {
132 pos_ = Log::kMessageBufferSize;
133 }
134 DCHECK(pos_ <= Log::kMessageBufferSize);
135 }
136
137
Append(const char c)138 void Log::MessageBuilder::Append(const char c) {
139 if (pos_ < Log::kMessageBufferSize) {
140 log_->message_buffer_[pos_++] = c;
141 }
142 DCHECK(pos_ <= Log::kMessageBufferSize);
143 }
144
145
AppendDoubleQuotedString(const char * string)146 void Log::MessageBuilder::AppendDoubleQuotedString(const char* string) {
147 Append('"');
148 for (const char* p = string; *p != '\0'; p++) {
149 if (*p == '"') {
150 Append('\\');
151 }
152 Append(*p);
153 }
154 Append('"');
155 }
156
157
Append(String * str)158 void Log::MessageBuilder::Append(String* str) {
159 DisallowHeapAllocation no_gc; // Ensure string stay valid.
160 int length = str->length();
161 for (int i = 0; i < length; i++) {
162 Append(static_cast<char>(str->Get(i)));
163 }
164 }
165
AppendAddress(Address addr)166 void Log::MessageBuilder::AppendAddress(Address addr) {
167 Append("%p", static_cast<void*>(addr));
168 }
169
AppendSymbolName(Symbol * symbol)170 void Log::MessageBuilder::AppendSymbolName(Symbol* symbol) {
171 DCHECK(symbol);
172 Append("symbol(");
173 if (!symbol->name()->IsUndefined(symbol->GetIsolate())) {
174 Append("\"");
175 AppendDetailed(String::cast(symbol->name()), false);
176 Append("\" ");
177 }
178 Append("hash %x)", symbol->Hash());
179 }
180
181
AppendDetailed(String * str,bool show_impl_info)182 void Log::MessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
183 if (str == NULL) return;
184 DisallowHeapAllocation no_gc; // Ensure string stay valid.
185 int len = str->length();
186 if (len > 0x1000)
187 len = 0x1000;
188 if (show_impl_info) {
189 Append(str->IsOneByteRepresentation() ? 'a' : '2');
190 if (StringShape(str).IsExternal())
191 Append('e');
192 if (StringShape(str).IsInternalized())
193 Append('#');
194 Append(":%i:", str->length());
195 }
196 for (int i = 0; i < len; i++) {
197 uc32 c = str->Get(i);
198 if (c > 0xff) {
199 Append("\\u%04x", c);
200 } else if (c < 32 || c > 126) {
201 Append("\\x%02x", c);
202 } else if (c == ',') {
203 Append("\\,");
204 } else if (c == '\\') {
205 Append("\\\\");
206 } else if (c == '\"') {
207 Append("\"\"");
208 } else {
209 Append("%lc", c);
210 }
211 }
212 }
213
214
AppendStringPart(const char * str,int len)215 void Log::MessageBuilder::AppendStringPart(const char* str, int len) {
216 if (pos_ + len > Log::kMessageBufferSize) {
217 len = Log::kMessageBufferSize - pos_;
218 DCHECK(len >= 0);
219 if (len == 0) return;
220 }
221 Vector<char> buf(log_->message_buffer_ + pos_,
222 Log::kMessageBufferSize - pos_);
223 StrNCpy(buf, str, len);
224 pos_ += len;
225 DCHECK(pos_ <= Log::kMessageBufferSize);
226 }
227
228
WriteToLogFile()229 void Log::MessageBuilder::WriteToLogFile() {
230 DCHECK(pos_ <= Log::kMessageBufferSize);
231 // Assert that we do not already have a new line at the end.
232 DCHECK(pos_ == 0 || log_->message_buffer_[pos_ - 1] != '\n');
233 if (pos_ == Log::kMessageBufferSize) pos_--;
234 log_->message_buffer_[pos_++] = '\n';
235 const int written = log_->WriteToFile(log_->message_buffer_, pos_);
236 if (written != pos_) {
237 log_->stop();
238 log_->logger_->LogFailure();
239 }
240 }
241
242
243 } // namespace internal
244 } // namespace v8
245