blob: 1bba7cd54eb3f463452ffe7072737e7a56ce6be8 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
29
30#include "log-utils.h"
Steve Block44f0eee2011-05-26 01:26:41 +010031#include "string-stream.h"
Steve Blocka7e24c12009-10-30 11:49:00 +000032
33namespace v8 {
34namespace internal {
35
36#ifdef ENABLE_LOGGING_AND_PROFILING
37
38LogDynamicBuffer::LogDynamicBuffer(
39 int block_size, int max_size, const char* seal, int seal_size)
40 : block_size_(block_size),
41 max_size_(max_size - (max_size % block_size_)),
42 seal_(seal),
43 seal_size_(seal_size),
44 blocks_(max_size_ / block_size_ + 1),
45 write_pos_(0), block_index_(0), block_write_pos_(0), is_sealed_(false) {
46 ASSERT(BlocksCount() > 0);
47 AllocateBlock(0);
48 for (int i = 1; i < BlocksCount(); ++i) {
49 blocks_[i] = NULL;
50 }
51}
52
53
54LogDynamicBuffer::~LogDynamicBuffer() {
55 for (int i = 0; i < BlocksCount(); ++i) {
56 DeleteArray(blocks_[i]);
57 }
58}
59
60
61int LogDynamicBuffer::Read(int from_pos, char* dest_buf, int buf_size) {
62 if (buf_size == 0) return 0;
63 int read_pos = from_pos;
64 int block_read_index = BlockIndex(from_pos);
65 int block_read_pos = PosInBlock(from_pos);
66 int dest_buf_pos = 0;
67 // Read until dest_buf is filled, or write_pos_ encountered.
68 while (read_pos < write_pos_ && dest_buf_pos < buf_size) {
69 const int read_size = Min(write_pos_ - read_pos,
70 Min(buf_size - dest_buf_pos, block_size_ - block_read_pos));
71 memcpy(dest_buf + dest_buf_pos,
72 blocks_[block_read_index] + block_read_pos, read_size);
73 block_read_pos += read_size;
74 dest_buf_pos += read_size;
75 read_pos += read_size;
76 if (block_read_pos == block_size_) {
77 block_read_pos = 0;
78 ++block_read_index;
79 }
80 }
81 return dest_buf_pos;
82}
83
84
85int LogDynamicBuffer::Seal() {
86 WriteInternal(seal_, seal_size_);
87 is_sealed_ = true;
88 return 0;
89}
90
91
92int LogDynamicBuffer::Write(const char* data, int data_size) {
93 if (is_sealed_) {
94 return 0;
95 }
96 if ((write_pos_ + data_size) <= (max_size_ - seal_size_)) {
97 return WriteInternal(data, data_size);
98 } else {
99 return Seal();
100 }
101}
102
103
104int LogDynamicBuffer::WriteInternal(const char* data, int data_size) {
105 int data_pos = 0;
106 while (data_pos < data_size) {
107 const int write_size =
108 Min(data_size - data_pos, block_size_ - block_write_pos_);
109 memcpy(blocks_[block_index_] + block_write_pos_, data + data_pos,
110 write_size);
111 block_write_pos_ += write_size;
112 data_pos += write_size;
113 if (block_write_pos_ == block_size_) {
114 block_write_pos_ = 0;
115 AllocateBlock(++block_index_);
116 }
117 }
118 write_pos_ += data_size;
119 return data_size;
120}
121
Steve Blocka7e24c12009-10-30 11:49:00 +0000122// Must be the same message as in Logger::PauseProfiler.
Steve Block44f0eee2011-05-26 01:26:41 +0100123const char* const Log::kDynamicBufferSeal = "profiler,\"pause\"\n";
124
125Log::Log(Logger* logger)
126 : write_to_file_(false),
127 is_stopped_(false),
128 output_handle_(NULL),
Ben Murdoch257744e2011-11-30 15:57:28 +0000129 ll_output_handle_(NULL),
Steve Block44f0eee2011-05-26 01:26:41 +0100130 output_buffer_(NULL),
131 mutex_(NULL),
132 message_buffer_(NULL),
133 logger_(logger) {
134}
Steve Blocka7e24c12009-10-30 11:49:00 +0000135
136
Steve Block44f0eee2011-05-26 01:26:41 +0100137static void AddIsolateIdIfNeeded(StringStream* stream) {
138 Isolate* isolate = Isolate::Current();
139 if (isolate->IsDefaultIsolate()) return;
140 stream->Add("isolate-%p-", isolate);
141}
142
143
144void Log::Initialize() {
145#ifdef ENABLE_LOGGING_AND_PROFILING
Steve Blocka7e24c12009-10-30 11:49:00 +0000146 mutex_ = OS::CreateMutex();
147 message_buffer_ = NewArray<char>(kMessageBufferSize);
Steve Block44f0eee2011-05-26 01:26:41 +0100148
149 // --log-all enables all the log flags.
150 if (FLAG_log_all) {
151 FLAG_log_runtime = true;
152 FLAG_log_api = true;
153 FLAG_log_code = true;
154 FLAG_log_gc = true;
155 FLAG_log_suspect = true;
156 FLAG_log_handles = true;
157 FLAG_log_regexp = true;
158 }
159
160 // --prof implies --log-code.
161 if (FLAG_prof) FLAG_log_code = true;
162
163 // --prof_lazy controls --log-code, implies --noprof_auto.
164 if (FLAG_prof_lazy) {
165 FLAG_log_code = false;
166 FLAG_prof_auto = false;
167 }
168
169 bool start_logging = FLAG_log || FLAG_log_runtime || FLAG_log_api
170 || FLAG_log_code || FLAG_log_gc || FLAG_log_handles || FLAG_log_suspect
Ben Murdoch257744e2011-11-30 15:57:28 +0000171 || FLAG_log_regexp || FLAG_log_state_changes || FLAG_ll_prof;
Steve Block44f0eee2011-05-26 01:26:41 +0100172
173 bool open_log_file = start_logging || FLAG_prof_lazy;
174
175 // If we're logging anything, we need to open the log file.
176 if (open_log_file) {
177 if (strcmp(FLAG_logfile, "-") == 0) {
178 OpenStdout();
179 } else if (strcmp(FLAG_logfile, "*") == 0) {
180 OpenMemoryBuffer();
181 } else {
182 if (strchr(FLAG_logfile, '%') != NULL ||
183 !Isolate::Current()->IsDefaultIsolate()) {
184 // If there's a '%' in the log file name we have to expand
185 // placeholders.
186 HeapStringAllocator allocator;
187 StringStream stream(&allocator);
188 AddIsolateIdIfNeeded(&stream);
189 for (const char* p = FLAG_logfile; *p; p++) {
190 if (*p == '%') {
191 p++;
192 switch (*p) {
193 case '\0':
194 // If there's a % at the end of the string we back up
195 // one character so we can escape the loop properly.
196 p--;
197 break;
198 case 't': {
199 // %t expands to the current time in milliseconds.
200 double time = OS::TimeCurrentMillis();
201 stream.Add("%.0f", FmtElm(time));
202 break;
203 }
204 case '%':
205 // %% expands (contracts really) to %.
206 stream.Put('%');
207 break;
208 default:
209 // All other %'s expand to themselves.
210 stream.Put('%');
211 stream.Put(*p);
212 break;
213 }
214 } else {
215 stream.Put(*p);
216 }
217 }
218 SmartPointer<const char> expanded = stream.ToCString();
219 OpenFile(*expanded);
220 } else {
221 OpenFile(FLAG_logfile);
222 }
223 }
224 }
225#endif
Steve Blocka7e24c12009-10-30 11:49:00 +0000226}
227
228
229void Log::OpenStdout() {
230 ASSERT(!IsEnabled());
231 output_handle_ = stdout;
Steve Block44f0eee2011-05-26 01:26:41 +0100232 write_to_file_ = true;
Steve Blocka7e24c12009-10-30 11:49:00 +0000233}
234
235
Ben Murdoch257744e2011-11-30 15:57:28 +0000236// Extension added to V8 log file name to get the low-level log name.
237static const char kLowLevelLogExt[] = ".ll";
238
239// File buffer size of the low-level log. We don't use the default to
240// minimize the associated overhead.
241static const int kLowLevelLogBufferSize = 2 * MB;
Ben Murdochf87a2032010-10-22 12:50:53 +0100242
243
Steve Blocka7e24c12009-10-30 11:49:00 +0000244void Log::OpenFile(const char* name) {
245 ASSERT(!IsEnabled());
246 output_handle_ = OS::FOpen(name, OS::LogFileOpenMode);
Steve Block44f0eee2011-05-26 01:26:41 +0100247 write_to_file_ = true;
Ben Murdochf87a2032010-10-22 12:50:53 +0100248 if (FLAG_ll_prof) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000249 // Open the low-level log file.
250 size_t len = strlen(name);
251 ScopedVector<char> ll_name(static_cast<int>(len + sizeof(kLowLevelLogExt)));
252 memcpy(ll_name.start(), name, len);
253 memcpy(ll_name.start() + len, kLowLevelLogExt, sizeof(kLowLevelLogExt));
254 ll_output_handle_ = OS::FOpen(ll_name.start(), OS::LogFileOpenMode);
255 setvbuf(ll_output_handle_, NULL, _IOFBF, kLowLevelLogBufferSize);
Ben Murdochf87a2032010-10-22 12:50:53 +0100256 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000257}
258
259
260void Log::OpenMemoryBuffer() {
261 ASSERT(!IsEnabled());
262 output_buffer_ = new LogDynamicBuffer(
263 kDynamicBufferBlockSize, kMaxDynamicBufferSize,
Steve Blockd0582a62009-12-15 09:54:21 +0000264 kDynamicBufferSeal, StrLength(kDynamicBufferSeal));
Steve Block44f0eee2011-05-26 01:26:41 +0100265 write_to_file_ = false;
Steve Blocka7e24c12009-10-30 11:49:00 +0000266}
267
268
269void Log::Close() {
Steve Block44f0eee2011-05-26 01:26:41 +0100270 if (write_to_file_) {
Steve Blocka7e24c12009-10-30 11:49:00 +0000271 if (output_handle_ != NULL) fclose(output_handle_);
272 output_handle_ = NULL;
Ben Murdoch257744e2011-11-30 15:57:28 +0000273 if (ll_output_handle_ != NULL) fclose(ll_output_handle_);
274 ll_output_handle_ = NULL;
Steve Block44f0eee2011-05-26 01:26:41 +0100275 } else {
Steve Blocka7e24c12009-10-30 11:49:00 +0000276 delete output_buffer_;
277 output_buffer_ = NULL;
Steve Blocka7e24c12009-10-30 11:49:00 +0000278 }
Steve Blocka7e24c12009-10-30 11:49:00 +0000279
280 DeleteArray(message_buffer_);
281 message_buffer_ = NULL;
282
283 delete mutex_;
284 mutex_ = NULL;
285
286 is_stopped_ = false;
287}
288
289
290int Log::GetLogLines(int from_pos, char* dest_buf, int max_size) {
Steve Block44f0eee2011-05-26 01:26:41 +0100291 if (write_to_file_) return 0;
Steve Blocka7e24c12009-10-30 11:49:00 +0000292 ASSERT(output_buffer_ != NULL);
293 ASSERT(from_pos >= 0);
294 ASSERT(max_size >= 0);
295 int actual_size = output_buffer_->Read(from_pos, dest_buf, max_size);
296 ASSERT(actual_size <= max_size);
297 if (actual_size == 0) return 0;
298
299 // Find previous log line boundary.
300 char* end_pos = dest_buf + actual_size - 1;
301 while (end_pos >= dest_buf && *end_pos != '\n') --end_pos;
Steve Blockd0582a62009-12-15 09:54:21 +0000302 actual_size = static_cast<int>(end_pos - dest_buf + 1);
Steve Block6ded16b2010-05-10 14:33:55 +0100303 // If the assertion below is hit, it means that there was no line end
304 // found --- something wrong has happened.
305 ASSERT(actual_size > 0);
Steve Blocka7e24c12009-10-30 11:49:00 +0000306 ASSERT(actual_size <= max_size);
307 return actual_size;
308}
309
310
Steve Block44f0eee2011-05-26 01:26:41 +0100311LogMessageBuilder::LogMessageBuilder(Logger* logger)
312 : log_(logger->log_),
313 sl(log_->mutex_),
314 pos_(0) {
315 ASSERT(log_->message_buffer_ != NULL);
Steve Blocka7e24c12009-10-30 11:49:00 +0000316}
317
318
319void LogMessageBuilder::Append(const char* format, ...) {
Steve Block44f0eee2011-05-26 01:26:41 +0100320 Vector<char> buf(log_->message_buffer_ + pos_,
Steve Blocka7e24c12009-10-30 11:49:00 +0000321 Log::kMessageBufferSize - pos_);
322 va_list args;
323 va_start(args, format);
324 AppendVA(format, args);
325 va_end(args);
326 ASSERT(pos_ <= Log::kMessageBufferSize);
327}
328
329
330void LogMessageBuilder::AppendVA(const char* format, va_list args) {
Steve Block44f0eee2011-05-26 01:26:41 +0100331 Vector<char> buf(log_->message_buffer_ + pos_,
Steve Blocka7e24c12009-10-30 11:49:00 +0000332 Log::kMessageBufferSize - pos_);
333 int result = v8::internal::OS::VSNPrintF(buf, format, args);
334
335 // Result is -1 if output was truncated.
336 if (result >= 0) {
337 pos_ += result;
338 } else {
339 pos_ = Log::kMessageBufferSize;
340 }
341 ASSERT(pos_ <= Log::kMessageBufferSize);
342}
343
344
345void LogMessageBuilder::Append(const char c) {
346 if (pos_ < Log::kMessageBufferSize) {
Steve Block44f0eee2011-05-26 01:26:41 +0100347 log_->message_buffer_[pos_++] = c;
Steve Blocka7e24c12009-10-30 11:49:00 +0000348 }
349 ASSERT(pos_ <= Log::kMessageBufferSize);
350}
351
352
353void LogMessageBuilder::Append(String* str) {
354 AssertNoAllocation no_heap_allocation; // Ensure string stay valid.
355 int length = str->length();
356 for (int i = 0; i < length; i++) {
357 Append(static_cast<char>(str->Get(i)));
358 }
359}
360
361
362void LogMessageBuilder::AppendAddress(Address addr) {
Ben Murdochb0fe1622011-05-05 13:52:32 +0100363 Append("0x%" V8PRIxPTR, addr);
Steve Blocka7e24c12009-10-30 11:49:00 +0000364}
365
366
367void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
Ben Murdoch257744e2011-11-30 15:57:28 +0000368 if (str == NULL) return;
Steve Blocka7e24c12009-10-30 11:49:00 +0000369 AssertNoAllocation no_heap_allocation; // Ensure string stay valid.
370 int len = str->length();
371 if (len > 0x1000)
372 len = 0x1000;
373 if (show_impl_info) {
374 Append(str->IsAsciiRepresentation() ? 'a' : '2');
375 if (StringShape(str).IsExternal())
376 Append('e');
377 if (StringShape(str).IsSymbol())
378 Append('#');
379 Append(":%i:", str->length());
380 }
381 for (int i = 0; i < len; i++) {
382 uc32 c = str->Get(i);
383 if (c > 0xff) {
384 Append("\\u%04x", c);
385 } else if (c < 32 || c > 126) {
386 Append("\\x%02x", c);
387 } else if (c == ',') {
388 Append("\\,");
389 } else if (c == '\\') {
390 Append("\\\\");
Ben Murdoche0cee9b2011-05-25 10:26:03 +0100391 } else if (c == '\"') {
392 Append("\"\"");
Steve Blocka7e24c12009-10-30 11:49:00 +0000393 } else {
394 Append("%lc", c);
395 }
396 }
397}
398
399
400void LogMessageBuilder::AppendStringPart(const char* str, int len) {
401 if (pos_ + len > Log::kMessageBufferSize) {
402 len = Log::kMessageBufferSize - pos_;
403 ASSERT(len >= 0);
404 if (len == 0) return;
405 }
Steve Block44f0eee2011-05-26 01:26:41 +0100406 Vector<char> buf(log_->message_buffer_ + pos_,
Steve Blocka7e24c12009-10-30 11:49:00 +0000407 Log::kMessageBufferSize - pos_);
408 OS::StrNCpy(buf, str, len);
409 pos_ += len;
410 ASSERT(pos_ <= Log::kMessageBufferSize);
411}
412
413
Steve Blocka7e24c12009-10-30 11:49:00 +0000414void LogMessageBuilder::WriteToLogFile() {
415 ASSERT(pos_ <= Log::kMessageBufferSize);
Steve Block44f0eee2011-05-26 01:26:41 +0100416 const int written = log_->write_to_file_ ?
417 log_->WriteToFile(log_->message_buffer_, pos_) :
418 log_->WriteToMemory(log_->message_buffer_, pos_);
419 if (written != pos_) {
420 log_->stop();
421 log_->logger_->LogFailure();
Steve Blocka7e24c12009-10-30 11:49:00 +0000422 }
423}
424
Steve Block44f0eee2011-05-26 01:26:41 +0100425
Steve Blocka7e24c12009-10-30 11:49:00 +0000426#endif // ENABLE_LOGGING_AND_PROFILING
427
428} } // namespace v8::internal