diff --git a/runtime-common/core/allocator/pool-allocator.h b/runtime-common/core/allocator/pool-allocator.h index fe85ebd2c1..07db2219ee 100644 --- a/runtime-common/core/allocator/pool-allocator.h +++ b/runtime-common/core/allocator/pool-allocator.h @@ -25,6 +25,7 @@ struct pool_allocator : private vk::not_copyable { size_t m_min_extra_mem_size{0}; auto request_extra_memory(size_t requested_size) noexcept -> void; + [[noreturn]] auto raise_out_of_memory(size_t requested_size) const noexcept -> void; public: pool_allocator(size_t script_mem_size, size_t min_extra_mem_size, size_t oom_handling_mem_size) noexcept; diff --git a/runtime-light/allocator/pool-allocator.cpp b/runtime-light/allocator/pool-allocator.cpp index c487c29799..da7bfe5f55 100644 --- a/runtime-light/allocator/pool-allocator.cpp +++ b/runtime-light/allocator/pool-allocator.cpp @@ -50,7 +50,9 @@ auto pool_allocator::alloc_script_memory(size_t size) noexcept -> void* { request_extra_memory(size); mem = memory_resource.allocate(size); - kphp::log::assertion(mem != nullptr); + if (mem == nullptr) [[unlikely]] { + raise_out_of_memory(size); + } } return mem; @@ -64,7 +66,9 @@ auto pool_allocator::calloc_script_memory(size_t size) noexcept -> void* { request_extra_memory(size); mem = memory_resource.allocate0(size); - kphp::log::assertion(mem != nullptr); + if (mem == nullptr) [[unlikely]] { + raise_out_of_memory(size); + } } return mem; @@ -78,7 +82,9 @@ auto pool_allocator::realloc_script_memory(void* old_mem, size_t new_size, size_ request_extra_memory(new_size * 2); new_mem = memory_resource.reallocate(old_mem, new_size, old_size); - kphp::log::assertion(new_mem != nullptr); + if (new_mem == nullptr) [[unlikely]] { + raise_out_of_memory(new_size); + } } return new_mem; @@ -104,8 +110,20 @@ auto pool_allocator::request_extra_memory(size_t requested_size) noexcept -> voi extra_mem_size = std::bit_ceil(extra_mem_size); auto* extra_mem{kphp::memory::platform::alloc(extra_mem_size)}; - kphp::log::assertion(extra_mem != nullptr); + if (extra_mem == nullptr) [[unlikely]] { + raise_out_of_memory(extra_mem_size); + } memory_resource.add_extra_memory(new (extra_mem) memory_resource::extra_memory_pool{extra_mem_size}); } +auto pool_allocator::raise_out_of_memory(size_t requested_size) const noexcept -> void { + const auto& stats{memory_resource.get_memory_stats()}; + kphp::log::error("pool_allocator: out of memory (requested {} bytes), " + "memory usage: {} of {} bytes (max {}), real usage: {} bytes (max {}), " + "total allocations: {}, total memory allocated: {} bytes, " + "defragmentation calls: {}, huge memory pieces: {}, small memory pieces: {}", + requested_size, stats.memory_used, stats.memory_limit, stats.max_memory_used, stats.real_memory_used, stats.max_real_memory_used, + stats.total_allocations, stats.total_memory_allocated, stats.defragmentation_calls, stats.huge_memory_pieces, stats.small_memory_pieces); +} + } // namespace kphp::memory diff --git a/runtime-light/stdlib/diagnostics/logs.h b/runtime-light/stdlib/diagnostics/logs.h index b332553be9..2f45138140 100644 --- a/runtime-light/stdlib/diagnostics/logs.h +++ b/runtime-light/stdlib/diagnostics/logs.h @@ -10,6 +10,7 @@ #include #include #include +#include #include "runtime-light/k2-platform/k2-api.h" #include "runtime-light/stdlib/diagnostics/backtrace.h" @@ -37,29 +38,36 @@ void log(level level, std::optional> trace, std::format_s return std::nullopt; })}; - const size_t tagged_entries_size{ - static_cast((trace.has_value() ? 1 : 0) + opt_tags.transform([](contextual_tags& tags) noexcept { return tags.size(); }).value_or(0))}; - kphp::stl::vector tagged_entries{}; - tagged_entries.reserve(tagged_entries_size); + size_t tagged_entries_size{}; + static constexpr size_t TAGGED_ENTRIES_BUFFER_SIZE{24UZ}; + std::array tagged_entries_buffer; // NOLINT - opt_tags.transform([&tagged_entries](contextual_tags& tags) noexcept { + // Reserve one slot for the trace when it's present + const size_t tagged_entries_capacity{trace.has_value() ? TAGGED_ENTRIES_BUFFER_SIZE - 1 : TAGGED_ENTRIES_BUFFER_SIZE}; + + opt_tags.transform([&tagged_entries_buffer, &tagged_entries_size, tagged_entries_capacity](contextual_tags& tags) noexcept { for (const auto& [key, value] : tags.values()) { - tagged_entries.push_back(k2::LogTaggedEntry{.key = key.data(), .value = value.data(), .key_len = key.size(), .value_len = value.size()}); + if (tagged_entries_size == tagged_entries_capacity) { + break; + } + + tagged_entries_buffer[tagged_entries_size++] = + k2::LogTaggedEntry{.key = key.data(), .value = value.data(), .key_len = key.size(), .value_len = value.size()}; } return 0; }); if (trace.has_value()) { - static constexpr std::string_view BACKTRACE_KEY = "trace"; - static constexpr size_t BACKTRACE_BUFFER_SIZE = 1024UZ * 4UZ; + static constexpr std::string_view BACKTRACE_KEY{"trace"}; + static constexpr size_t BACKTRACE_BUFFER_SIZE{1024UZ * 4UZ}; std::array backtrace_buffer; // NOLINT size_t backtrace_size{impl::resolve_log_trace(backtrace_buffer, *trace)}; - tagged_entries.push_back( - k2::LogTaggedEntry{.key = BACKTRACE_KEY.data(), .value = backtrace_buffer.data(), .key_len = BACKTRACE_KEY.size(), .value_len = backtrace_size}); - k2::log(std::to_underlying(level), message, tagged_entries); + tagged_entries_buffer[tagged_entries_size++] = + k2::LogTaggedEntry{.key = BACKTRACE_KEY.data(), .value = backtrace_buffer.data(), .key_len = BACKTRACE_KEY.size(), .value_len = backtrace_size}; + k2::log(std::to_underlying(level), message, {tagged_entries_buffer.data(), tagged_entries_size}); return; } - k2::log(std::to_underlying(level), message, tagged_entries); + k2::log(std::to_underlying(level), message, {tagged_entries_buffer.data(), tagged_entries_size}); } } // namespace impl