Allocate LogMessageData from LowLevelAlloc and lazily construct its ostream. When LOG(...) or LOG(FATAL) is invoked from inside a MallocHook callback (such as in MallocDetector or ThreadLocalMallocHook), allocating LogMessageData via operator new or destroying an unused std::ostream in ~LogMessageData (which calls free(nullptr) in libc++'s ios_base::~ios_base) reenters the memory allocator. Allocate LogMessageData from an unhooked LowLevelAlloc arena and wrap LogMessageData::manipulated in std::optional<std::ostream> so the stream is constructed only when OstreamView is actually used. Also use ABSL_RAW_LOG in logging_unittest's MallocHook test callback. PiperOrigin-RevId: 1002204028
diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt index 1da38e4..13fbd09 100644 --- a/absl/log/CMakeLists.txt +++ b/absl/log/CMakeLists.txt
@@ -211,6 +211,7 @@ absl::log_severity absl::log_sink absl::log_sink_registry + absl::malloc_internal absl::memory absl::nullability absl::raw_logging_internal
diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel index 42114ed..202e257 100644 --- a/absl/log/internal/BUILD.bazel +++ b/absl/log/internal/BUILD.bazel
@@ -208,6 +208,7 @@ "//absl/base:core_headers", "//absl/base:errno_saver", "//absl/base:log_severity", + "//absl/base:malloc_internal", "//absl/base:nullability", "//absl/base:raw_logging_internal", "//absl/base:strerror",
diff --git a/absl/log/internal/log_message.cc b/absl/log/internal/log_message.cc index e56ef25..9b63217 100644 --- a/absl/log/internal/log_message.cc +++ b/absl/log/internal/log_message.cc
@@ -25,12 +25,15 @@ #include <atomic> #include <ios> #include <memory> +#include <optional> #include <ostream> #include <string> #include <string_view> #include "absl/base/attributes.h" +#include "absl/base/call_once.h" #include "absl/base/config.h" +#include "absl/base/internal/low_level_alloc.h" #include "absl/base/internal/raw_logging.h" #include "absl/base/internal/strerror.h" #include "absl/base/internal/sysinfo.h" @@ -145,6 +148,17 @@ auto* cast_os = static_cast<std::ostream*>(os); *cast_os << data; } + +#ifndef ABSL_LOW_LEVEL_ALLOC_MISSING +absl::base_internal::LowLevelAlloc::Arena* LogMessageArena() { + ABSL_CONST_INIT static absl::once_flag once; + ABSL_CONST_INIT static absl::base_internal::LowLevelAlloc::Arena* arena = + nullptr; + absl::base_internal::LowLevelCallOnce( + &once, []() { arena = absl::base_internal::LowLevelAlloc::NewArena(0); }); + return arena; +} +#endif } // namespace struct LogMessage::LogMessageData final { @@ -153,6 +167,16 @@ LogMessageData(const LogMessageData&) = delete; LogMessageData& operator=(const LogMessageData&) = delete; +#ifndef ABSL_LOW_LEVEL_ALLOC_MISSING + static void* operator new(size_t s) { + return absl::base_internal::LowLevelAlloc::AllocWithArena( + s, LogMessageArena()); + } + static void operator delete(void* p) { + absl::base_internal::LowLevelAlloc::Free(p); + } +#endif + // `LogEntry` sent to `LogSink`s; contains metadata. absl::LogEntry entry; @@ -169,7 +193,8 @@ // non-sink targets (e.g. stderr, log files). bool extra_sinks_only; - std::ostream manipulated; // ostream with IO manipulators applied + std::optional<std::ostream> manipulated; // Lazily constructed ostream + // with IO manipulators applied // A `logging.proto.Event` proto message is built into `encoded_buf`. std::array<char, kLogMessageBufferSize> encoded_buf; @@ -200,12 +225,10 @@ void FinalizeEncodingAndFormat(); }; -LogMessage::LogMessageData::LogMessageData(absl::string_view file, - int line, absl::LogSeverity severity, +LogMessage::LogMessageData::LogMessageData(absl::string_view file, int line, + absl::LogSeverity severity, absl::Time timestamp) - : extra_sinks_only(false), manipulated(nullptr) { - // Legacy defaults for LOG's ostream: - manipulated.setf(std::ios_base::showbase | std::ios_base::boolalpha); + : extra_sinks_only(false) { entry.full_filename_ = file; entry.base_filename_ = Basename(file); entry.line_ = line; @@ -439,12 +462,12 @@ LogMessage& LogMessage::operator<<(std::ostream& (*m)(std::ostream& os)) { OstreamView view(*data_); - data_->manipulated << m; + view.stream() << m; return *this; } LogMessage& LogMessage::operator<<(std::ios_base& (*m)(std::ios_base& os)) { OstreamView view(*data_); - data_->manipulated << m; + view.stream() << m; return *this; } // NOLINTBEGIN(runtime/int) @@ -501,6 +524,11 @@ LogMessage::OstreamView::OstreamView(LogMessageData& message_data) : data_(message_data), encoded_remaining_copy_(data_.encoded_remaining()) { + if (!data_.manipulated.has_value()) { + data_.manipulated.emplace(nullptr); + // Legacy defaults for LOG's ostream: + data_.manipulated->setf(std::ios_base::showbase | std::ios_base::boolalpha); + } // This constructor sets the `streambuf` up so that streaming into an attached // ostream encodes string data in-place. To do that, we write appropriate // headers into the buffer using a copy of the buffer view so that we can @@ -515,11 +543,11 @@ &encoded_remaining_copy_); setp(encoded_remaining_copy_.data(), encoded_remaining_copy_.data() + encoded_remaining_copy_.size()); - data_.manipulated.rdbuf(this); + data_.manipulated->rdbuf(this); } LogMessage::OstreamView::~OstreamView() { - data_.manipulated.rdbuf(nullptr); + data_.manipulated->rdbuf(nullptr); if (!string_start_.data()) { // The second field header didn't fit. Whether the first one did or not, we // shouldn't commit `encoded_remaining_copy_`, and we also need to zero the @@ -536,7 +564,7 @@ data_.encoded_remaining() = encoded_remaining_copy_; } -std::ostream& LogMessage::OstreamView::stream() { return data_.manipulated; } +std::ostream& LogMessage::OstreamView::stream() { return *data_.manipulated; } bool LogMessage::IsFatal() const { return data_->entry.log_severity() == absl::LogSeverity::kFatal &&