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
14namespace v8 {
15namespace internal {
16
17
18const char* const Log::kLogToTemporaryFile = "&";
19const char* const Log::kLogToConsole = "-";
20
21
22Log::Log(Logger* logger)
23  : is_stopped_(false),
24    output_handle_(NULL),
25    message_buffer_(NULL),
26    logger_(logger) {
27}
28
29
30void 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
67void Log::OpenStdout() {
68  DCHECK(!IsEnabled());
69  output_handle_ = stdout;
70}
71
72
73void Log::OpenTemporaryFile() {
74  DCHECK(!IsEnabled());
75  output_handle_ = base::OS::OpenTemporaryFile();
76}
77
78
79void Log::OpenFile(const char* name) {
80  DCHECK(!IsEnabled());
81  output_handle_ = base::OS::FOpen(name, base::OS::LogFileOpenMode);
82}
83
84
85FILE* 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
104Log::MessageBuilder::MessageBuilder(Log* log)
105  : log_(log),
106    lock_guard_(&log_->mutex_),
107    pos_(0) {
108  DCHECK(log_->message_buffer_ != NULL);
109}
110
111
112void 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
123void 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
138void 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
146void 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
158void 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
166void Log::MessageBuilder::AppendAddress(Address addr) {
167  Append("%p", static_cast<void*>(addr));
168}
169
170void 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
182void 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
215void 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
229void 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