// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. #include "vm/log.h" #include "vm/dart.h" #include "vm/flags.h" #include "vm/isolate.h" #include "vm/thread.h" namespace dart { DEFINE_FLAG(bool, force_log_flush, false, "Always flush log messages."); // The following flag is useful when debugging on Android, since // adb logcat truncates messages that are "too long" (and always // flushing would result in too many short messages). DEFINE_FLAG( int, force_log_flush_at_size, 0, "Flush log messages when buffer exceeds given size (disabled when 0)."); DEFINE_FLAG(charp, isolate_log_filter, nullptr, "Log isolates whose name include the filter. " "Default: service isolate log messages are suppressed " "(specify 'vm-service' to log them)."); DEFINE_FLAG(charp, redirect_isolate_log_to, nullptr, "Log isolate messages into the given file."); namespace { class LogFile { public: static const LogFile& Instance() { static LogFile log_file; return log_file; } static void Print(const char* data) { Dart::file_write_callback()(data, strlen(data), Instance().handle_); } private: LogFile() : handle_(Dart::file_open_callback()(FLAG_redirect_isolate_log_to, /*write=*/true)) {} ~LogFile() { Dart::file_close_callback()(handle_); } void* handle_; }; } // namespace Log::Log(LogPrinter printer) : printer_(printer), manual_flush_(0), buffer_(0) { if (printer_ == nullptr) { if (FLAG_redirect_isolate_log_to == nullptr) { printer_ = [](const char* data) { OS::PrintErr("%s", data); }; } else { printer_ = &LogFile::Print; } } } Log::~Log() { // Did someone enable manual flushing and then forgot to Flush? ASSERT(cursor() == 0); } Log* Log::Current() { Thread* thread = Thread::Current(); if (thread == nullptr) { OSThread* os_thread = OSThread::Current(); ASSERT(os_thread != nullptr); return os_thread->log(); } IsolateGroup* isolate_group = thread->isolate_group(); if ((isolate_group != nullptr) && Log::ShouldLogForIsolateGroup(isolate_group)) { OSThread* os_thread = thread->os_thread(); ASSERT(os_thread != nullptr); return os_thread->log(); } else { return Log::NoOpLog(); } } void Log::Print(const char* format, ...) { if (this == NoOpLog()) { return; } va_list args; va_start(args, format); VPrint(format, args); va_end(args); } void Log::VPrint(const char* format, va_list args) { if (this == NoOpLog()) { return; } // Measure. va_list measure_args; va_copy(measure_args, args); intptr_t len = Utils::VSNPrint(nullptr, 0, format, measure_args); va_end(measure_args); // Print. char* buffer = reinterpret_cast(malloc(len + 1)); va_list print_args; va_copy(print_args, args); Utils::VSNPrint(buffer, (len + 1), format, print_args); va_end(print_args); // Append. // NOTE: does not append the '\0' character. for (intptr_t i = 0; i < len; i++) { buffer_.Add(buffer[i]); } free(buffer); if (ShouldFlush()) { Flush(); } } void Log::Flush(const intptr_t cursor) { if (this == NoOpLog()) { return; } if (buffer_.is_empty()) { return; } if (buffer_.length() <= cursor) { return; } TerminateString(); const char* str = &buffer_[cursor]; ASSERT(str != nullptr); printer_(str); buffer_.TruncateTo(cursor); } intptr_t Log::cursor() const { return buffer_.length(); } bool Log::ShouldLogForIsolateGroup(const IsolateGroup* isolate_group) { if (FLAG_isolate_log_filter == nullptr) { // By default, do not log for the service or kernel isolates. if (IsolateGroup::IsSystemIsolateGroup(isolate_group)) { return false; } return true; } const char* name = isolate_group->source()->name; ASSERT(name != nullptr); if (strstr(name, FLAG_isolate_log_filter) == nullptr) { // Filter does not match, do not log for this isolate. return false; } return true; } Log Log::noop_log_; Log* Log::NoOpLog() { return &noop_log_; } void Log::TerminateString() { if (this == NoOpLog()) { return; } buffer_.Add('\0'); } void Log::EnableManualFlush() { if (this == NoOpLog()) { return; } manual_flush_++; } void Log::DisableManualFlush(const intptr_t cursor) { if (this == NoOpLog()) { return; } manual_flush_--; ASSERT(manual_flush_ >= 0); if (manual_flush_ == 0) { Flush(cursor); } } bool Log::ShouldFlush() const { #ifdef DART_TARGET_OS_ANDROID // Android truncates on 1023 characters, flush more eagerly. // Flush on newlines, because otherwise Android inserts newlines everywhere. if (*(buffer_.end() - 1) == '\n') { return true; } #endif // DART_TARGET_OS_ANDROID return ((manual_flush_ == 0) || FLAG_force_log_flush || ((FLAG_force_log_flush_at_size > 0) && (cursor() > FLAG_force_log_flush_at_size))); } void LogBlock::Initialize() { log_->EnableManualFlush(); } LogBlock::~LogBlock() { log_->DisableManualFlush(cursor_); } } // namespace dart