Commit 433b4984 authored by Dominik Inführ's avatar Dominik Inführ Committed by Commit Bot

[logging] Make Logger::SetUp and TearDownAndGetLogFile thread-safe

While so far this should only happen in tests in test-log.cc, it can
happen that background threads using Logger::is_logging() race with
Logger::TearDownAndGetLogFile().

Fix the race by protecting is_logging_ with the mutex that is also used
for writing log messages. Logger::is_logging_ now becomes relaxed
atomic, such that code for logging isn't required to lock the mutex
to check whether logging is enabled.

Also remove Log::IsEnabled() in favor of Logger::is_logging() to avoid
checking both flags since both are the same.

Bug: v8:10315
Change-Id: Ic14e7f74334eb8a8438abad82ad227d1e6752bb8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2416488
Commit-Queue: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69973}
parent c436607b
...@@ -46,20 +46,17 @@ bool Log::IsLoggingToTemporaryFile(std::string file_name) { ...@@ -46,20 +46,17 @@ bool Log::IsLoggingToTemporaryFile(std::string file_name) {
return file_name.compare(Log::kLogToTemporaryFile) == 0; return file_name.compare(Log::kLogToTemporaryFile) == 0;
} }
Log::Log(std::string file_name) Log::Log(Logger* logger, std::string file_name)
: file_name_(file_name), : logger_(logger),
file_name_(file_name),
output_handle_(Log::CreateOutputHandle(file_name)), output_handle_(Log::CreateOutputHandle(file_name)),
os_(output_handle_ == nullptr ? stdout : output_handle_), os_(output_handle_ == nullptr ? stdout : output_handle_),
is_enabled_(output_handle_ != nullptr),
format_buffer_(NewArray<char>(kMessageBufferSize)) { format_buffer_(NewArray<char>(kMessageBufferSize)) {
if (output_handle_ == nullptr) return; if (output_handle_) WriteLogHeader();
WriteLogHeader();
} }
void Log::WriteLogHeader() { void Log::WriteLogHeader() {
std::unique_ptr<Log::MessageBuilder> msg_ptr = NewMessageBuilder(); Log::MessageBuilder msg(this);
if (!msg_ptr) return;
Log::MessageBuilder& msg = *msg_ptr.get();
LogSeparator kNext = LogSeparator::kSeparator; LogSeparator kNext = LogSeparator::kSeparator;
msg << "v8-version" << kNext << Version::GetMajor() << kNext msg << "v8-version" << kNext << Version::GetMajor() << kNext
<< Version::GetMinor() << kNext << Version::GetBuild() << kNext << Version::GetMinor() << kNext << Version::GetBuild() << kNext
...@@ -72,22 +69,21 @@ void Log::WriteLogHeader() { ...@@ -72,22 +69,21 @@ void Log::WriteLogHeader() {
} }
std::unique_ptr<Log::MessageBuilder> Log::NewMessageBuilder() { std::unique_ptr<Log::MessageBuilder> Log::NewMessageBuilder() {
// Fast check of IsEnabled() without taking the lock. Bail out immediately if // Fast check of is_logging() without taking the lock. Bail out immediately if
// logging isn't enabled. // logging isn't enabled.
if (!IsEnabled()) return {}; if (!logger_->is_logging()) return {};
std::unique_ptr<Log::MessageBuilder> result(new Log::MessageBuilder(this)); std::unique_ptr<Log::MessageBuilder> result(new Log::MessageBuilder(this));
// The first invocation of IsEnabled() might still read an old value. It is // The first invocation of is_logging() might still read an old value. It is
// fine if a background thread starts logging a bit later, but we want to // fine if a background thread starts logging a bit later, but we want to
// avoid background threads continue logging after logging was closed. // avoid background threads continue logging after logging was already closed.
if (!IsEnabled()) return {}; if (!logger_->is_logging()) return {};
return result; return result;
} }
FILE* Log::Close() { FILE* Log::Close() {
base::MutexGuard guard(&mutex_);
FILE* result = nullptr; FILE* result = nullptr;
if (output_handle_ != nullptr) { if (output_handle_ != nullptr) {
fflush(output_handle_); fflush(output_handle_);
...@@ -95,7 +91,6 @@ FILE* Log::Close() { ...@@ -95,7 +91,6 @@ FILE* Log::Close() {
} }
output_handle_ = nullptr; output_handle_ = nullptr;
format_buffer_.reset(); format_buffer_.reset();
is_enabled_.store(false, std::memory_order_relaxed);
return result; return result;
} }
...@@ -227,7 +222,6 @@ void Log::MessageBuilder::AppendRawFormatString(const char* format, ...) { ...@@ -227,7 +222,6 @@ void Log::MessageBuilder::AppendRawFormatString(const char* format, ...) {
void Log::MessageBuilder::AppendRawCharacter(char c) { log_->os_ << c; } void Log::MessageBuilder::AppendRawCharacter(char c) { log_->os_ << c; }
void Log::MessageBuilder::WriteToLogFile() { void Log::MessageBuilder::WriteToLogFile() {
DCHECK(log_->IsEnabled());
log_->os_ << std::endl; log_->os_ << std::endl;
} }
......
...@@ -30,7 +30,7 @@ enum class LogSeparator { kSeparator }; ...@@ -30,7 +30,7 @@ enum class LogSeparator { kSeparator };
// Functions and data for performing output of log messages. // Functions and data for performing output of log messages.
class Log { class Log {
public: public:
explicit Log(std::string log_file_name); explicit Log(Logger* logger, std::string log_file_name);
static bool InitLogAtStart() { static bool InitLogAtStart() {
return FLAG_log || FLAG_log_all || FLAG_log_api || FLAG_log_code || return FLAG_log || FLAG_log_all || FLAG_log_api || FLAG_log_code ||
...@@ -51,9 +51,6 @@ class Log { ...@@ -51,9 +51,6 @@ class Log {
std::string file_name() const; std::string file_name() const;
// Returns whether logging is enabled.
bool IsEnabled() { return is_enabled_.load(std::memory_order_relaxed); }
// Size of buffer used for formatting log messages. // Size of buffer used for formatting log messages.
static const int kMessageBufferSize = 2048; static const int kMessageBufferSize = 2048;
...@@ -115,19 +112,20 @@ class Log { ...@@ -115,19 +112,20 @@ class Log {
private: private:
static FILE* CreateOutputHandle(std::string file_name); static FILE* CreateOutputHandle(std::string file_name);
base::Mutex* mutex() { return &mutex_; }
void WriteLogHeader(); void WriteLogHeader();
Logger* logger_;
std::string file_name_; std::string file_name_;
// When logging is active output_handle_ is used to store a pointer to log // When logging is active output_handle_ is used to store a pointer to log
// destination. mutex_ should be acquired before using output_handle_. // destination. mutex_ should be acquired before using output_handle_.
FILE* output_handle_; FILE* output_handle_;
OFStream os_; OFStream os_;
// Stores whether logging is enabled.
std::atomic<bool> is_enabled_;
// mutex_ is a Mutex used for enforcing exclusive // mutex_ is a Mutex used for enforcing exclusive
// access to the formatting buffer and the log file or log memory buffer. // access to the formatting buffer and the log file or log memory buffer.
base::Mutex mutex_; base::Mutex mutex_;
...@@ -135,6 +133,8 @@ class Log { ...@@ -135,6 +133,8 @@ class Log {
// Buffer used for formatting log messages. This is a singleton buffer and // Buffer used for formatting log messages. This is a singleton buffer and
// mutex_ should be acquired before using it. // mutex_ should be acquired before using it.
std::unique_ptr<char[]> format_buffer_; std::unique_ptr<char[]> format_buffer_;
friend class Logger;
}; };
template <> template <>
......
...@@ -4,11 +4,13 @@ ...@@ -4,11 +4,13 @@
#include "src/logging/log.h" #include "src/logging/log.h"
#include <atomic>
#include <cstdarg> #include <cstdarg>
#include <memory> #include <memory>
#include <sstream> #include <sstream>
#include "src/api/api-inl.h" #include "src/api/api-inl.h"
#include "src/base/platform/mutex.h"
#include "src/base/platform/platform.h" #include "src/base/platform/platform.h"
#include "src/builtins/profile-data-reader.h" #include "src/builtins/profile-data-reader.h"
#include "src/codegen/bailout-reason.h" #include "src/codegen/bailout-reason.h"
...@@ -1113,7 +1115,7 @@ void Logger::BuiltinHashEvent(const char* name, int hash) { ...@@ -1113,7 +1115,7 @@ void Logger::BuiltinHashEvent(const char* name, int hash) {
bool Logger::is_logging() { bool Logger::is_logging() {
// Disable logging while the CPU profiler is running. // Disable logging while the CPU profiler is running.
if (isolate_->is_profiling()) return false; if (isolate_->is_profiling()) return false;
return is_logging_; return is_logging_.load(std::memory_order_relaxed);
} }
// Instantiate template methods. // Instantiate template methods.
...@@ -1429,7 +1431,7 @@ void Logger::SharedFunctionInfoMoveEvent(Address from, Address to) { ...@@ -1429,7 +1431,7 @@ void Logger::SharedFunctionInfoMoveEvent(Address from, Address to) {
void Logger::CodeMovingGCEvent() { void Logger::CodeMovingGCEvent() {
if (!is_listening_to_code_events()) return; if (!is_listening_to_code_events()) return;
if (!log_->IsEnabled() || !FLAG_ll_prof) return; if (!FLAG_ll_prof) return;
base::OS::SignalCodeMovingGC(); base::OS::SignalCodeMovingGC();
} }
...@@ -1473,7 +1475,7 @@ void Logger::ProcessDeoptEvent(Handle<Code> code, SourcePosition position, ...@@ -1473,7 +1475,7 @@ void Logger::ProcessDeoptEvent(Handle<Code> code, SourcePosition position,
void Logger::CodeDeoptEvent(Handle<Code> code, DeoptimizeKind kind, Address pc, void Logger::CodeDeoptEvent(Handle<Code> code, DeoptimizeKind kind, Address pc,
int fp_to_sp_delta, bool reuse_code) { int fp_to_sp_delta, bool reuse_code) {
if (!log_->IsEnabled()) return; if (!is_logging()) return;
Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(*code, pc); Deoptimizer::DeoptInfo info = Deoptimizer::GetDeoptInfo(*code, pc);
ProcessDeoptEvent(code, info.position, ProcessDeoptEvent(code, info.position,
Deoptimizer::MessageFor(kind, reuse_code), Deoptimizer::MessageFor(kind, reuse_code),
...@@ -1483,7 +1485,7 @@ void Logger::CodeDeoptEvent(Handle<Code> code, DeoptimizeKind kind, Address pc, ...@@ -1483,7 +1485,7 @@ void Logger::CodeDeoptEvent(Handle<Code> code, DeoptimizeKind kind, Address pc,
void Logger::CodeDependencyChangeEvent(Handle<Code> code, void Logger::CodeDependencyChangeEvent(Handle<Code> code,
Handle<SharedFunctionInfo> sfi, Handle<SharedFunctionInfo> sfi,
const char* reason) { const char* reason) {
if (!log_->IsEnabled()) return; if (!is_logging()) return;
SourcePosition position(sfi->StartPosition(), -1); SourcePosition position(sfi->StartPosition(), -1);
ProcessDeoptEvent(code, position, "dependency-change", reason); ProcessDeoptEvent(code, position, "dependency-change", reason);
} }
...@@ -2030,7 +2032,7 @@ bool Logger::SetUp(Isolate* isolate) { ...@@ -2030,7 +2032,7 @@ bool Logger::SetUp(Isolate* isolate) {
std::ostringstream log_file_name; std::ostringstream log_file_name;
PrepareLogFileName(log_file_name, isolate, FLAG_logfile); PrepareLogFileName(log_file_name, isolate, FLAG_logfile);
log_ = std::make_unique<Log>(log_file_name.str()); log_ = std::make_unique<Log>(this, log_file_name.str());
#if V8_OS_LINUX #if V8_OS_LINUX
if (FLAG_perf_basic_prof) { if (FLAG_perf_basic_prof) {
...@@ -2059,17 +2061,22 @@ bool Logger::SetUp(Isolate* isolate) { ...@@ -2059,17 +2061,22 @@ bool Logger::SetUp(Isolate* isolate) {
ticker_ = std::make_unique<Ticker>(isolate, FLAG_prof_sampling_interval); ticker_ = std::make_unique<Ticker>(isolate, FLAG_prof_sampling_interval);
if (Log::InitLogAtStart()) is_logging_ = true; bool activate_logging = false;
if (Log::InitLogAtStart()) activate_logging = true;
timer_.Start(); timer_.Start();
if (FLAG_prof_cpp) { if (FLAG_prof_cpp) {
profiler_ = std::make_unique<Profiler>(isolate); profiler_ = std::make_unique<Profiler>(isolate);
is_logging_ = true; activate_logging = true;
profiler_->Engage(); profiler_->Engage();
} }
if (is_logging_) AddCodeEventListener(this); if (activate_logging) {
AddCodeEventListener(this);
UpdateIsLogging(true);
}
return true; return true;
} }
...@@ -2108,7 +2115,7 @@ void Logger::StopProfilerThread() { ...@@ -2108,7 +2115,7 @@ void Logger::StopProfilerThread() {
FILE* Logger::TearDownAndGetLogFile() { FILE* Logger::TearDownAndGetLogFile() {
if (!is_initialized_) return nullptr; if (!is_initialized_) return nullptr;
is_initialized_ = false; is_initialized_ = false;
is_logging_ = false; UpdateIsLogging(false);
// Stop the profiler thread before closing the file. // Stop the profiler thread before closing the file.
StopProfilerThread(); StopProfilerThread();
...@@ -2141,6 +2148,13 @@ FILE* Logger::TearDownAndGetLogFile() { ...@@ -2141,6 +2148,13 @@ FILE* Logger::TearDownAndGetLogFile() {
return log_->Close(); return log_->Close();
} }
void Logger::UpdateIsLogging(bool value) {
base::MutexGuard guard(log_->mutex());
// Relaxed atomic to avoid locking the mutex for the most common case: when
// logging is disabled.
is_logging_.store(value, std::memory_order_relaxed);
}
void ExistingCodeLogger::LogCodeObject(Object object) { void ExistingCodeLogger::LogCodeObject(Object object) {
HandleScope scope(isolate_); HandleScope scope(isolate_);
Handle<AbstractCode> abstract_code(AbstractCode::cast(object), isolate_); Handle<AbstractCode> abstract_code(AbstractCode::cast(object), isolate_);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef V8_LOGGING_LOG_H_ #ifndef V8_LOGGING_LOG_H_
#define V8_LOGGING_LOG_H_ #define V8_LOGGING_LOG_H_
#include <atomic>
#include <memory> #include <memory>
#include <set> #include <set>
#include <string> #include <string>
...@@ -283,6 +284,8 @@ class Logger : public CodeEventListener { ...@@ -283,6 +284,8 @@ class Logger : public CodeEventListener {
void LogCodeObject(Object code_object); void LogCodeObject(Object code_object);
private: private:
void UpdateIsLogging(bool value);
// Emits the profiler's first message. // Emits the profiler's first message.
void ProfilerBeginEvent(); void ProfilerBeginEvent();
...@@ -324,7 +327,7 @@ class Logger : public CodeEventListener { ...@@ -324,7 +327,7 @@ class Logger : public CodeEventListener {
// Internal implementation classes with access to private members. // Internal implementation classes with access to private members.
friend class Profiler; friend class Profiler;
bool is_logging_; std::atomic<bool> is_logging_;
std::unique_ptr<Log> log_; std::unique_ptr<Log> log_;
#if V8_OS_LINUX #if V8_OS_LINUX
std::unique_ptr<PerfBasicLogger> perf_basic_logger_; std::unique_ptr<PerfBasicLogger> perf_basic_logger_;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment