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