| // 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<char*>(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); |
| } |
| |
| void Log::Clear() { |
| if (this == NoOpLog()) { |
| return; |
| } |
| buffer_.TruncateTo(0); |
| } |
| |
| intptr_t Log::cursor() const { |
| return buffer_.length(); |
| } |
| |
| bool Log::ShouldLogForIsolateGroup(const IsolateGroup* isolate_group) { |
| if (FLAG_isolate_log_filter == nullptr) { |
| if (IsolateGroup::IsSystemIsolateGroup(isolate_group)) { |
| // By default, do not log for the service or kernel isolates. |
| 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 |