log-utils.cc 6.55 KB
Newer Older
1
// Copyright 2009 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/log-utils.h"
6 7 8 9

#include "src/assert-scope.h"
#include "src/base/platform/platform.h"
#include "src/objects-inl.h"
10
#include "src/string-stream.h"
11
#include "src/utils.h"
12
#include "src/version.h"
13 14 15 16

namespace v8 {
namespace internal {

17

18
const char* const Log::kLogToTemporaryFile = "&";
19
const char* const Log::kLogToConsole = "-";
20

21 22 23 24 25 26 27 28 29 30 31 32 33
// static
FILE* Log::CreateOutputHandle(const char* file_name) {
  // If we're logging anything, we need to open the log file.
  if (!Log::InitLogAtStart()) {
    return nullptr;
  } else if (strcmp(file_name, kLogToConsole) == 0) {
    return stdout;
  } else if (strcmp(file_name, kLogToTemporaryFile) == 0) {
    return base::OS::OpenTemporaryFile();
  } else {
    return base::OS::FOpen(file_name, base::OS::LogFileOpenMode);
  }
}
34

35 36 37 38 39 40
Log::Log(Logger* logger, const char* file_name)
    : is_stopped_(false),
      output_handle_(Log::CreateOutputHandle(file_name)),
      os_(output_handle_ == nullptr ? stdout : output_handle_),
      format_buffer_(NewArray<char>(kMessageBufferSize)),
      logger_(logger) {
41 42 43 44 45 46
  // --log-all enables all the log flags.
  if (FLAG_log_all) {
    FLAG_log_api = true;
    FLAG_log_code = true;
    FLAG_log_suspect = true;
    FLAG_log_handles = true;
47
    FLAG_log_internal_timer_events = true;
48
    FLAG_log_function_events = true;
49 50 51 52 53
  }

  // --prof implies --log-code.
  if (FLAG_prof) FLAG_log_code = true;

54 55 56 57 58 59 60 61
  if (output_handle_ == nullptr) return;
  Log::MessageBuilder msg(this);
  LogSeparator kNext = LogSeparator::kSeparator;
  msg << "v8-version" << kNext << Version::GetMajor() << kNext
      << Version::GetMinor() << kNext << Version::GetBuild() << kNext
      << Version::GetPatch();
  if (strlen(Version::GetEmbedder()) != 0) {
    msg << kNext << Version::GetEmbedder();
62
  }
63 64
  msg << kNext << Version::IsCandidate();
  msg.WriteToLogFile();
65 66
}

67
FILE* Log::Close() {
68 69
  FILE* result = nullptr;
  if (output_handle_ != nullptr) {
70 71 72 73 74
    if (strcmp(FLAG_logfile, kLogToTemporaryFile) != 0) {
      fclose(output_handle_);
    } else {
      result = output_handle_;
    }
75
  }
76
  output_handle_ = nullptr;
77

78 79
  DeleteArray(format_buffer_);
  format_buffer_ = nullptr;
80

81
  is_stopped_ = false;
82
  return result;
83 84
}

85 86
Log::MessageBuilder::MessageBuilder(Log* log)
    : log_(log), lock_guard_(&log_->mutex_) {
87
  DCHECK_NOT_NULL(log_->format_buffer_);
88 89 90
}


91
void Log::MessageBuilder::Append(const char* format, ...) {
92 93
  va_list args;
  va_start(args, format);
94
  AppendVA(format, args);
95 96 97 98
  va_end(args);
}


99
void Log::MessageBuilder::AppendVA(const char* format, va_list args) {
100 101 102
  Vector<char> buf(log_->format_buffer_, Log::kMessageBufferSize);
  int length = v8::internal::VSNPrintF(buf, format, args);
  // {length} is -1 if output was truncated.
103
  if (length == -1) length = Log::kMessageBufferSize;
104 105
  DCHECK_LE(length, Log::kMessageBufferSize);
  AppendStringPart(log_->format_buffer_, length);
106 107
}

108
void Log::MessageBuilder::AppendSymbolName(Symbol* symbol) {
109
  DCHECK(symbol);
110 111
  OFStream& os = log_->os_;
  os << "symbol(";
112
  if (!symbol->name()->IsUndefined()) {
113
    os << "\"";
114
    AppendDetailed(String::cast(symbol->name()), false);
115
    os << "\" ";
116
  }
117
  os << "hash " << std::hex << symbol->Hash() << std::dec << ")";
118 119 120
}

void Log::MessageBuilder::AppendDetailed(String* str, bool show_impl_info) {
121
  if (str == nullptr) return;
122
  DisallowHeapAllocation no_gc;  // Ensure string stay valid.
123
  OFStream& os = log_->os_;
124 125
  int limit = str->length();
  if (limit > 0x1000) limit = 0x1000;
126
  if (show_impl_info) {
127 128 129 130
    os << (str->IsOneByteRepresentation() ? 'a' : '2');
    if (StringShape(str).IsExternal()) os << 'e';
    if (StringShape(str).IsInternalized()) os << '#';
    os << ':' << str->length() << ':';
131
  }
132
  AppendStringPart(str, limit);
133 134
}

135
void Log::MessageBuilder::AppendString(String* str) {
136
  if (str == nullptr) return;
137
  int len = str->length();
138 139 140 141
  AppendStringPart(str, len);
}

void Log::MessageBuilder::AppendString(const char* string) {
142
  if (string == nullptr) return;
143 144 145
  for (const char* p = string; *p != '\0'; p++) {
    this->AppendCharacter(*p);
  }
146 147
}

148
void Log::MessageBuilder::AppendStringPart(String* str, int len) {
149 150 151
  DCHECK_LE(len, str->length());
  DisallowHeapAllocation no_gc;  // Ensure string stay valid.
  // TODO(cbruni): unify escaping.
152 153
  for (int i = 0; i < len; i++) {
    uc32 c = str->Get(i);
154
    if (c <= 0xFF) {
155
      AppendCharacter(static_cast<char>(c));
156
    } else {
157 158
      // Escape any non-ascii range characters.
      Append("\\u%04x", c);
159 160 161 162
    }
  }
}

163 164
void Log::MessageBuilder::AppendStringPart(const char* str, size_t len) {
  for (size_t i = 0; i < len; i++) {
165 166 167 168 169 170 171
    DCHECK_NE(str[i], '\0');
    this->AppendCharacter(str[i]);
  }
}

void Log::MessageBuilder::AppendCharacter(char c) {
  OFStream& os = log_->os_;
172
  // A log entry (separate by commas) cannot contain commas or line-breaks.
173 174
  if (c >= 32 && c <= 126) {
    if (c == ',') {
175
      // Escape commas (log field separator) directly.
Camillo Bruni's avatar
Camillo Bruni committed
176
      os << "\\x2C";
177 178 179 180
    } else {
      // Directly append any printable ascii character.
      os << c;
    }
181 182
  } else if (c == '\n') {
    os << "\\n";
183
  } else {
184
    // Escape any non-printable characters.
185 186
    Append("\\x%02x", c);
  }
187 188
}

189
void Log::MessageBuilder::WriteToLogFile() { log_->os_ << std::endl; }
190

191 192 193 194 195 196 197
template <>
Log::MessageBuilder& Log::MessageBuilder::operator<<<const char*>(
    const char* string) {
  this->AppendString(string);
  return *this;
}

198 199 200 201 202 203 204 205 206
template <>
Log::MessageBuilder& Log::MessageBuilder::operator<<<void*>(void* pointer) {
  OFStream& os = log_->os_;
  // Manually format the pointer since on Windows we do not consistently
  // get a "0x" prefix.
  os << "0x" << std::hex << reinterpret_cast<intptr_t>(pointer) << std::dec;
  return *this;
}

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
template <>
Log::MessageBuilder& Log::MessageBuilder::operator<<<char>(char c) {
  this->AppendCharacter(c);
  return *this;
}

template <>
Log::MessageBuilder& Log::MessageBuilder::operator<<<String*>(String* string) {
  this->AppendString(string);
  return *this;
}

template <>
Log::MessageBuilder& Log::MessageBuilder::operator<<<Symbol*>(Symbol* symbol) {
  this->AppendSymbolName(symbol);
  return *this;
}

template <>
Log::MessageBuilder& Log::MessageBuilder::operator<<<Name*>(Name* name) {
  if (name->IsString()) {
    this->AppendString(String::cast(name));
  } else {
    this->AppendSymbolName(Symbol::cast(name));
  }
  return *this;
}

template <>
Log::MessageBuilder& Log::MessageBuilder::operator<<<LogSeparator>(
    LogSeparator separator) {
  log_->os_ << ',';
  return *this;
}

242 243
}  // namespace internal
}  // namespace v8