log-utils.cc revision a7e24c173cf37484693b9abb38e494fa7bd7baeb
1// 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"
31
32namespace v8 {
33namespace internal {
34
35#ifdef ENABLE_LOGGING_AND_PROFILING
36
37LogDynamicBuffer::LogDynamicBuffer(
38    int block_size, int max_size, const char* seal, int seal_size)
39    : block_size_(block_size),
40      max_size_(max_size - (max_size % block_size_)),
41      seal_(seal),
42      seal_size_(seal_size),
43      blocks_(max_size_ / block_size_ + 1),
44      write_pos_(0), block_index_(0), block_write_pos_(0), is_sealed_(false) {
45  ASSERT(BlocksCount() > 0);
46  AllocateBlock(0);
47  for (int i = 1; i < BlocksCount(); ++i) {
48    blocks_[i] = NULL;
49  }
50}
51
52
53LogDynamicBuffer::~LogDynamicBuffer() {
54  for (int i = 0; i < BlocksCount(); ++i) {
55    DeleteArray(blocks_[i]);
56  }
57}
58
59
60int LogDynamicBuffer::Read(int from_pos, char* dest_buf, int buf_size) {
61  if (buf_size == 0) return 0;
62  int read_pos = from_pos;
63  int block_read_index = BlockIndex(from_pos);
64  int block_read_pos = PosInBlock(from_pos);
65  int dest_buf_pos = 0;
66  // Read until dest_buf is filled, or write_pos_ encountered.
67  while (read_pos < write_pos_ && dest_buf_pos < buf_size) {
68    const int read_size = Min(write_pos_ - read_pos,
69        Min(buf_size - dest_buf_pos, block_size_ - block_read_pos));
70    memcpy(dest_buf + dest_buf_pos,
71           blocks_[block_read_index] + block_read_pos, read_size);
72    block_read_pos += read_size;
73    dest_buf_pos += read_size;
74    read_pos += read_size;
75    if (block_read_pos == block_size_) {
76      block_read_pos = 0;
77      ++block_read_index;
78    }
79  }
80  return dest_buf_pos;
81}
82
83
84int LogDynamicBuffer::Seal() {
85  WriteInternal(seal_, seal_size_);
86  is_sealed_ = true;
87  return 0;
88}
89
90
91int LogDynamicBuffer::Write(const char* data, int data_size) {
92  if (is_sealed_) {
93    return 0;
94  }
95  if ((write_pos_ + data_size) <= (max_size_ - seal_size_)) {
96    return WriteInternal(data, data_size);
97  } else {
98    return Seal();
99  }
100}
101
102
103int LogDynamicBuffer::WriteInternal(const char* data, int data_size) {
104  int data_pos = 0;
105  while (data_pos < data_size) {
106    const int write_size =
107        Min(data_size - data_pos, block_size_ - block_write_pos_);
108    memcpy(blocks_[block_index_] + block_write_pos_, data + data_pos,
109           write_size);
110    block_write_pos_ += write_size;
111    data_pos += write_size;
112    if (block_write_pos_ == block_size_) {
113      block_write_pos_ = 0;
114      AllocateBlock(++block_index_);
115    }
116  }
117  write_pos_ += data_size;
118  return data_size;
119}
120
121
122bool Log::is_stopped_ = false;
123Log::WritePtr Log::Write = NULL;
124FILE* Log::output_handle_ = NULL;
125LogDynamicBuffer* Log::output_buffer_ = NULL;
126// Must be the same message as in Logger::PauseProfiler.
127const char* Log::kDynamicBufferSeal = "profiler,\"pause\"\n";
128Mutex* Log::mutex_ = NULL;
129char* Log::message_buffer_ = NULL;
130
131
132void Log::Init() {
133  mutex_ = OS::CreateMutex();
134  message_buffer_ = NewArray<char>(kMessageBufferSize);
135}
136
137
138void Log::OpenStdout() {
139  ASSERT(!IsEnabled());
140  output_handle_ = stdout;
141  Write = WriteToFile;
142  Init();
143}
144
145
146void Log::OpenFile(const char* name) {
147  ASSERT(!IsEnabled());
148  output_handle_ = OS::FOpen(name, OS::LogFileOpenMode);
149  Write = WriteToFile;
150  Init();
151}
152
153
154void Log::OpenMemoryBuffer() {
155  ASSERT(!IsEnabled());
156  output_buffer_ = new LogDynamicBuffer(
157      kDynamicBufferBlockSize, kMaxDynamicBufferSize,
158      kDynamicBufferSeal, strlen(kDynamicBufferSeal));
159  Write = WriteToMemory;
160  Init();
161}
162
163
164void Log::Close() {
165  if (Write == WriteToFile) {
166    if (output_handle_ != NULL) fclose(output_handle_);
167    output_handle_ = NULL;
168  } else if (Write == WriteToMemory) {
169    delete output_buffer_;
170    output_buffer_ = NULL;
171  } else {
172    ASSERT(Write == NULL);
173  }
174  Write = NULL;
175
176  DeleteArray(message_buffer_);
177  message_buffer_ = NULL;
178
179  delete mutex_;
180  mutex_ = NULL;
181
182  is_stopped_ = false;
183}
184
185
186int Log::GetLogLines(int from_pos, char* dest_buf, int max_size) {
187  if (Write != WriteToMemory) return 0;
188  ASSERT(output_buffer_ != NULL);
189  ASSERT(from_pos >= 0);
190  ASSERT(max_size >= 0);
191  int actual_size = output_buffer_->Read(from_pos, dest_buf, max_size);
192  ASSERT(actual_size <= max_size);
193  if (actual_size == 0) return 0;
194
195  // Find previous log line boundary.
196  char* end_pos = dest_buf + actual_size - 1;
197  while (end_pos >= dest_buf && *end_pos != '\n') --end_pos;
198  actual_size = end_pos - dest_buf + 1;
199  ASSERT(actual_size <= max_size);
200  return actual_size;
201}
202
203
204LogMessageBuilder::WriteFailureHandler
205    LogMessageBuilder::write_failure_handler = NULL;
206
207
208LogMessageBuilder::LogMessageBuilder(): sl(Log::mutex_), pos_(0) {
209  ASSERT(Log::message_buffer_ != NULL);
210}
211
212
213void LogMessageBuilder::Append(const char* format, ...) {
214  Vector<char> buf(Log::message_buffer_ + pos_,
215                   Log::kMessageBufferSize - pos_);
216  va_list args;
217  va_start(args, format);
218  AppendVA(format, args);
219  va_end(args);
220  ASSERT(pos_ <= Log::kMessageBufferSize);
221}
222
223
224void LogMessageBuilder::AppendVA(const char* format, va_list args) {
225  Vector<char> buf(Log::message_buffer_ + pos_,
226                   Log::kMessageBufferSize - pos_);
227  int result = v8::internal::OS::VSNPrintF(buf, format, args);
228
229  // Result is -1 if output was truncated.
230  if (result >= 0) {
231    pos_ += result;
232  } else {
233    pos_ = Log::kMessageBufferSize;
234  }
235  ASSERT(pos_ <= Log::kMessageBufferSize);
236}
237
238
239void LogMessageBuilder::Append(const char c) {
240  if (pos_ < Log::kMessageBufferSize) {
241    Log::message_buffer_[pos_++] = c;
242  }
243  ASSERT(pos_ <= Log::kMessageBufferSize);
244}
245
246
247void LogMessageBuilder::Append(String* str) {
248  AssertNoAllocation no_heap_allocation;  // Ensure string stay valid.
249  int length = str->length();
250  for (int i = 0; i < length; i++) {
251    Append(static_cast<char>(str->Get(i)));
252  }
253}
254
255
256void LogMessageBuilder::AppendAddress(Address addr) {
257  static Address last_address_ = NULL;
258  AppendAddress(addr, last_address_);
259  last_address_ = addr;
260}
261
262
263void LogMessageBuilder::AppendAddress(Address addr, Address bias) {
264  if (!FLAG_compress_log) {
265    Append("0x%" V8PRIxPTR, addr);
266  } else if (bias == NULL) {
267    Append("%" V8PRIxPTR, addr);
268  } else {
269    uintptr_t delta;
270    char sign;
271    if (addr >= bias) {
272      delta = addr - bias;
273      sign = '+';
274    } else {
275      delta = bias - addr;
276      sign = '-';
277    }
278    Append("%c%" V8PRIxPTR, sign, delta);
279  }
280}
281
282
283void LogMessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
284  AssertNoAllocation no_heap_allocation;  // Ensure string stay valid.
285  int len = str->length();
286  if (len > 0x1000)
287    len = 0x1000;
288  if (show_impl_info) {
289    Append(str->IsAsciiRepresentation() ? 'a' : '2');
290    if (StringShape(str).IsExternal())
291      Append('e');
292    if (StringShape(str).IsSymbol())
293      Append('#');
294    Append(":%i:", str->length());
295  }
296  for (int i = 0; i < len; i++) {
297    uc32 c = str->Get(i);
298    if (c > 0xff) {
299      Append("\\u%04x", c);
300    } else if (c < 32 || c > 126) {
301      Append("\\x%02x", c);
302    } else if (c == ',') {
303      Append("\\,");
304    } else if (c == '\\') {
305      Append("\\\\");
306    } else {
307      Append("%lc", c);
308    }
309  }
310}
311
312
313void LogMessageBuilder::AppendStringPart(const char* str, int len) {
314  if (pos_ + len > Log::kMessageBufferSize) {
315    len = Log::kMessageBufferSize - pos_;
316    ASSERT(len >= 0);
317    if (len == 0) return;
318  }
319  Vector<char> buf(Log::message_buffer_ + pos_,
320                   Log::kMessageBufferSize - pos_);
321  OS::StrNCpy(buf, str, len);
322  pos_ += len;
323  ASSERT(pos_ <= Log::kMessageBufferSize);
324}
325
326
327bool LogMessageBuilder::StoreInCompressor(LogRecordCompressor* compressor) {
328  return compressor->Store(Vector<const char>(Log::message_buffer_, pos_));
329}
330
331
332bool LogMessageBuilder::RetrieveCompressedPrevious(
333    LogRecordCompressor* compressor, const char* prefix) {
334  pos_ = 0;
335  if (prefix[0] != '\0') Append(prefix);
336  Vector<char> prev_record(Log::message_buffer_ + pos_,
337                           Log::kMessageBufferSize - pos_);
338  const bool has_prev = compressor->RetrievePreviousCompressed(&prev_record);
339  if (!has_prev) return false;
340  pos_ += prev_record.length();
341  return true;
342}
343
344
345void LogMessageBuilder::WriteToLogFile() {
346  ASSERT(pos_ <= Log::kMessageBufferSize);
347  const int written = Log::Write(Log::message_buffer_, pos_);
348  if (written != pos_ && write_failure_handler != NULL) {
349    write_failure_handler();
350  }
351}
352
353
354void LogMessageBuilder::WriteCStringToLogFile(const char* str) {
355  const int len = strlen(str);
356  const int written = Log::Write(str, len);
357  if (written != len && write_failure_handler != NULL) {
358    write_failure_handler();
359  }
360}
361
362
363// Formatting string for back references to the whole line. E.g. "#2" means
364// "the second line above".
365const char* LogRecordCompressor::kLineBackwardReferenceFormat = "#%d";
366
367// Formatting string for back references. E.g. "#2:10" means
368// "the second line above, start from char 10 (0-based)".
369const char* LogRecordCompressor::kBackwardReferenceFormat = "#%d:%d";
370
371
372LogRecordCompressor::~LogRecordCompressor() {
373  for (int i = 0; i < buffer_.length(); ++i) {
374    buffer_[i].Dispose();
375  }
376}
377
378
379static int GetNumberLength(int number) {
380  ASSERT(number >= 0);
381  ASSERT(number < 10000);
382  if (number < 10) return 1;
383  if (number < 100) return 2;
384  if (number < 1000) return 3;
385  return 4;
386}
387
388
389int LogRecordCompressor::GetBackwardReferenceSize(int distance, int pos) {
390  // See kLineBackwardReferenceFormat and kBackwardReferenceFormat.
391  return pos == 0 ? GetNumberLength(distance) + 1
392      : GetNumberLength(distance) + GetNumberLength(pos) + 2;
393}
394
395
396void LogRecordCompressor::PrintBackwardReference(Vector<char> dest,
397                                                 int distance,
398                                                 int pos) {
399  if (pos == 0) {
400    OS::SNPrintF(dest, kLineBackwardReferenceFormat, distance);
401  } else {
402    OS::SNPrintF(dest, kBackwardReferenceFormat, distance, pos);
403  }
404}
405
406
407bool LogRecordCompressor::Store(const Vector<const char>& record) {
408  // Check if the record is the same as the last stored one.
409  if (curr_ != -1) {
410    Vector<const char>& curr = buffer_[curr_];
411    if (record.length() == curr.length()
412        && strncmp(record.start(), curr.start(), record.length()) == 0) {
413      return false;
414    }
415  }
416  // buffer_ is circular.
417  prev_ = curr_++;
418  curr_ %= buffer_.length();
419  Vector<char> record_copy = Vector<char>::New(record.length());
420  memcpy(record_copy.start(), record.start(), record.length());
421  buffer_[curr_].Dispose();
422  buffer_[curr_] =
423      Vector<const char>(record_copy.start(), record_copy.length());
424  return true;
425}
426
427
428bool LogRecordCompressor::RetrievePreviousCompressed(
429    Vector<char>* prev_record) {
430  if (prev_ == -1) return false;
431
432  int index = prev_;
433  // Distance from prev_.
434  int distance = 0;
435  // Best compression result among records in the buffer.
436  struct {
437    intptr_t truncated_len;
438    int distance;
439    int copy_from_pos;
440    int backref_size;
441  } best = {-1, 0, 0, 0};
442  Vector<const char>& prev = buffer_[prev_];
443  const char* const prev_start = prev.start();
444  const char* const prev_end = prev.start() + prev.length();
445  do {
446    // We're moving backwards until we reach the current record.
447    // Remember that buffer_ is circular.
448    if (--index == -1) index = buffer_.length() - 1;
449    ++distance;
450    if (index == curr_) break;
451
452    Vector<const char>& data = buffer_[index];
453    if (data.start() == NULL) break;
454    const char* const data_end = data.start() + data.length();
455    const char* prev_ptr = prev_end;
456    const char* data_ptr = data_end;
457    // Compare strings backwards, stop on the last matching character.
458    while (prev_ptr != prev_start && data_ptr != data.start()
459          && *(prev_ptr - 1) == *(data_ptr - 1)) {
460      --prev_ptr;
461      --data_ptr;
462    }
463    const intptr_t truncated_len = prev_end - prev_ptr;
464    const int copy_from_pos = data_ptr - data.start();
465    // Check if the length of compressed tail is enough.
466    if (truncated_len <= kMaxBackwardReferenceSize
467        && truncated_len <= GetBackwardReferenceSize(distance, copy_from_pos)) {
468      continue;
469    }
470
471    // Record compression results.
472    if (truncated_len > best.truncated_len) {
473      best.truncated_len = truncated_len;
474      best.distance = distance;
475      best.copy_from_pos = copy_from_pos;
476      best.backref_size = GetBackwardReferenceSize(distance, copy_from_pos);
477    }
478  } while (true);
479
480  if (best.distance == 0) {
481    // Can't compress the previous record. Return as is.
482    ASSERT(prev_record->length() >= prev.length());
483    memcpy(prev_record->start(), prev.start(), prev.length());
484    prev_record->Truncate(prev.length());
485  } else {
486    // Copy the uncompressible part unchanged.
487    const intptr_t unchanged_len = prev.length() - best.truncated_len;
488    // + 1 for '\0'.
489    ASSERT(prev_record->length() >= unchanged_len + best.backref_size + 1);
490    memcpy(prev_record->start(), prev.start(), unchanged_len);
491    // Append the backward reference.
492    Vector<char> backref(
493        prev_record->start() + unchanged_len, best.backref_size + 1);
494    PrintBackwardReference(backref, best.distance, best.copy_from_pos);
495    ASSERT(strlen(backref.start()) - best.backref_size == 0);
496    prev_record->Truncate(unchanged_len + best.backref_size);
497  }
498  return true;
499}
500
501#endif  // ENABLE_LOGGING_AND_PROFILING
502
503} }  // namespace v8::internal
504