From a3e72f33a54c528d5485d1680a21b9299d3ac8a6 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 10 Apr 2026 20:32:23 -0400 Subject: [PATCH] xo-arena: + src_fn argument in alloc_error + contributaries --- include/xo/arena/AllocError.hpp | 17 +++++++---- include/xo/arena/DArena.hpp | 8 +++-- include/xo/arena/DArenaVector.hpp | 12 ++++---- include/xo/arena/print.hpp | 9 ++++-- src/arena/CMakeLists.txt | 3 ++ src/arena/DArena.cpp | 49 ++++++++++++++++++++++--------- src/arena/DArenaIterator.cpp | 4 +-- src/arena/arena_streambuf.cpp | 6 ++-- utest/DArena.test.cpp | 2 +- 9 files changed, 73 insertions(+), 37 deletions(-) diff --git a/include/xo/arena/AllocError.hpp b/include/xo/arena/AllocError.hpp index ca98b367..19ae9f08 100644 --- a/include/xo/arena/AllocError.hpp +++ b/include/xo/arena/AllocError.hpp @@ -48,20 +48,25 @@ namespace xo { uint32_t seq) : error_{err}, error_seq_{seq} {} AllocError(error err, + const char * src_fn, uint32_t seq, size_type req_z, size_type com_z, - size_type rsv_z) : error_{err}, - error_seq_{seq}, - request_z_{req_z}, - committed_z_{com_z}, - reserved_z_{rsv_z} {} + size_type rsv_z) : error_{err}, + src_fn_{src_fn}, + error_seq_{seq}, + request_z_{req_z}, + committed_z_{com_z}, + reserved_z_{rsv_z} {} static const char * error_description(error x); /** error code **/ error error_ = error::ok; - + /** source function. Typically injected with __PRETTY_FUNCTION__ + * somewhere suitable on stack + **/ + const char * src_fn_ = nullptr; /** sequence# of this error. * Each error event within an allocator gets next sequence number **/ diff --git a/include/xo/arena/DArena.hpp b/include/xo/arena/DArena.hpp index 74f48322..13850d75 100644 --- a/include/xo/arena/DArena.hpp +++ b/include/xo/arena/DArena.hpp @@ -203,19 +203,21 @@ namespace xo { /** capture error information: advance error count + set last_error **/ void capture_error(error err, + const char * src_fn, size_type target_z = 0) const; /** alloc driver. shared by alloc(), super_alloc(), sub_alloc() **/ value_type _alloc(std::size_t req_z, alloc_mode mode, typeseq tseq, - uint32_t age); + uint32_t age, + const char * src_fn); /** expand committed space in arena @p d - * to size at least @p z + * to size at least @p z, on behalf of @p src_fn * In practice will round up to a multiple of @ref page_z_. **/ - bool expand(size_type z) noexcept; + bool expand(size_type z, const char * src_fn) noexcept; /** create initial guard **/ void establish_initial_guard() noexcept; diff --git a/include/xo/arena/DArenaVector.hpp b/include/xo/arena/DArenaVector.hpp index 21e059d1..7b017268 100644 --- a/include/xo/arena/DArenaVector.hpp +++ b/include/xo/arena/DArenaVector.hpp @@ -181,7 +181,7 @@ namespace xo { template void DArenaVector::reserve(size_type z) { - store_.expand(z * sizeof(T)); + store_.expand(z * sizeof(T), __PRETTY_FUNCTION__); } template @@ -193,7 +193,7 @@ namespace xo { if (z > size_) { // expand arena to accomodate - if (!store_.expand(req_z)) + if (!store_.expand(req_z, __PRETTY_FUNCTION__)) return false; // run ctors @@ -258,7 +258,7 @@ namespace xo { size_type new_z = size_ + 1; size_type req_z = new_z * sizeof(T); - store_.expand(req_z); + store_.expand(req_z, __PRETTY_FUNCTION__); } // move elements [i .. z-1] right by one position. @@ -283,7 +283,7 @@ namespace xo { size_type new_z = size_ + 1; size_type req_z = new_z * sizeof(T); - store_.expand(req_z); + store_.expand(req_z, __PRETTY_FUNCTION__); } // move elements [i .. z-1] right by one position. @@ -322,7 +322,7 @@ namespace xo { size_type z = size_ + 1; size_type req_z = z * sizeof(T); - if (this->store_.expand(req_z)) { + if (this->store_.expand(req_z, __PRETTY_FUNCTION__)) { T * addr = this->_address_of(size_); new (addr) T{std::move(x)}; @@ -336,7 +336,7 @@ namespace xo { DArenaVector::push_back(const T & x) { size_type z = size_ + 1; - if (this->store_.expand(z * sizeof(T))) { + if (this->store_.expand(z * sizeof(T), __PRETTY_FUNCTION__)) { T * addr = this->_address_of(size_); new (addr) T{x}; diff --git a/include/xo/arena/print.hpp b/include/xo/arena/print.hpp index 5c474762..4618d5d4 100644 --- a/include/xo/arena/print.hpp +++ b/include/xo/arena/print.hpp @@ -20,12 +20,17 @@ namespace xo { inline std::ostream & operator<<(std::ostream & os, const AllocError & x) { os << ""; + return os; } } diff --git a/src/arena/CMakeLists.txt b/src/arena/CMakeLists.txt index b3bb35a6..6b87729e 100644 --- a/src/arena/CMakeLists.txt +++ b/src/arena/CMakeLists.txt @@ -11,6 +11,7 @@ set(SELF_SRCS DArena.cpp DArenaIterator.cpp DCircularBuffer.cpp + backtrace.cpp ) xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS}) @@ -24,5 +25,7 @@ xo_install_include_tree3(include/xo/arena) xo_dependency(${SELF_LIB} xo_reflectutil) xo_dependency(${SELF_LIB} indentlog) +xo_external_pkgconfig_dependency(${SELF_LIB} LIBUNWIND libunwind-generic) +xo_external_pkgconfig_dependency(${SELF_LIB} LIBDW libdw) # end src/CMakeLists.txt diff --git a/src/arena/DArena.cpp b/src/arena/DArena.cpp index d3ca1f23..cf5bc090 100644 --- a/src/arena/DArena.cpp +++ b/src/arena/DArena.cpp @@ -7,6 +7,7 @@ #include "DArena.hpp" #include "DArenaIterator.hpp" #include "mmap_util.hpp" +#include "backtrace.hpp" #include #include #include @@ -230,7 +231,7 @@ namespace xo { DArena::alloc_info(value_type mem) const noexcept { if (!config_.store_header_flag_) [[unlikely]] { - this->capture_error(error::alloc_info_disabled); + this->capture_error(error::alloc_info_disabled, __PRETTY_FUNCTION__); return AllocInfo::error_not_configured(&config_.header_); } @@ -272,7 +273,7 @@ namespace xo { DArena::begin_header() const noexcept { if (config_.store_header_flag_ == false) { - this->capture_error(error::alloc_iterator_not_supported); + this->capture_error(error::alloc_iterator_not_supported, __PRETTY_FUNCTION__); return nullptr; } @@ -284,7 +285,7 @@ namespace xo { DArena::end_header() const noexcept { if (config_.store_header_flag_ == false) { - this->capture_error(error::alloc_iterator_not_supported); + this->capture_error(error::alloc_iterator_not_supported, __PRETTY_FUNCTION__); return nullptr; } @@ -299,7 +300,11 @@ namespace xo { * exactly 1 header per alloc() call. * - store_header_flag follows configuration */ - return _alloc(req_z, alloc_mode::standard, t, 0 /*age*/); + return _alloc(req_z, + alloc_mode::standard, + t, + 0 /*age*/, + __PRETTY_FUNCTION__); } std::byte * @@ -316,7 +321,8 @@ namespace xo { return _alloc(req_z, alloc_mode::super, t, - 0 /*age*/); + 0 /*age*/, + __PRETTY_FUNCTION__); } std::byte * @@ -334,7 +340,8 @@ namespace xo { ? alloc_mode::sub_complete : alloc_mode::sub_incomplete), typeseq::sentinel() /*typeseq: ignored*/, - 0 /*age - ignored */); + 0 /*age - ignored */, + __PRETTY_FUNCTION__); } std::byte * @@ -354,17 +361,20 @@ namespace xo { typeseq tseq = typeseq(src_info.tseq()); uint32_t age = src_info.age(); - return _alloc(req_z, alloc_mode::standard, tseq, age + 1); + return _alloc(req_z, alloc_mode::standard, tseq, age + 1, + __PRETTY_FUNCTION__); } void DArena::capture_error(error err, + const char * src_fn, size_type target_z) const { DArena * self = const_cast(this); ++(self->error_count_); self->last_error_ = AllocError(err, + src_fn, error_count_, target_z, committed_z_, @@ -375,7 +385,8 @@ namespace xo { DArena::_alloc(std::size_t req_z, alloc_mode mode, typeseq tseq, - uint32_t age) + uint32_t age, + const char * src_fn) { scope log(XO_DEBUG(config_.debug_flag_)); @@ -444,7 +455,7 @@ namespace xo { hz = sizeof(header); } else { /* req_z doesn't fit in configured header_size_mask bits */ - capture_error(error::header_size_mask); + capture_error(error::header_size_mask, src_fn); return nullptr; } } @@ -453,7 +464,7 @@ namespace xo { assert(padding::is_aligned(z1)); - if (!this->expand(this->allocated() + z1)) [[unlikely]] { + if (!this->expand(this->allocated() + z1, src_fn)) [[unlikely]] { /* (error state already captured) */ return nullptr; } @@ -509,11 +520,12 @@ namespace xo { } bool - DArena::expand(size_t target_z) noexcept + DArena::expand(size_t target_z, const char * src_fn) noexcept { scope log(XO_DEBUG(config_.debug_flag_), xtag("target_z", target_z), - xtag("committed_z", committed_z_)); + xtag("committed_z", committed_z_), + xtag("src_fn", src_fn)); if (target_z <= committed_z_) [[likely]] { log && log("trivial success, offset within committed range", @@ -523,7 +535,12 @@ namespace xo { } if (lo_ + target_z > hi_) [[unlikely]] { - this->capture_error(error::reserve_exhausted, target_z); + this->capture_error(error::reserve_exhausted, src_fn, target_z); + + fprintf(stderr, "DArena::expand: reserve exhausted"); + print_backtrace_dwarf(true /*demangle_flag*/); + std::terminate(); + return false; } @@ -566,7 +583,11 @@ namespace xo { ); } - capture_error(error::commit_failed, add_commit_z); + this->capture_error(error::commit_failed, src_fn, add_commit_z); + + fprintf(stderr, "DArena::expand: mprotect failed (system oom?)"); + print_backtrace_dwarf(false /*!demangle_flag*/); + return false; } diff --git a/src/arena/DArenaIterator.cpp b/src/arena/DArenaIterator.cpp index 931108bb..78769b7a 100644 --- a/src/arena/DArenaIterator.cpp +++ b/src/arena/DArenaIterator.cpp @@ -74,7 +74,7 @@ namespace xo { xtag("bounds_flag", bounds_flag)); if (!contains_flag || !bounds_flag) { - arena_->capture_error(error::alloc_iterator_deref); + arena_->capture_error(error::alloc_iterator_deref, __PRETTY_FUNCTION__); return AllocInfo::error_invalid_iterator(&(arena_->config_.header_)); } @@ -122,7 +122,7 @@ namespace xo { xtag("bounds_flag", bounds_flag)); if (!contains_flag || !bounds_flag) { - arena_->capture_error(error::alloc_iterator_next); + arena_->capture_error(error::alloc_iterator_next, __PRETTY_FUNCTION__); return; } diff --git a/src/arena/arena_streambuf.cpp b/src/arena/arena_streambuf.cpp index 18ea132e..7947da0d 100644 --- a/src/arena/arena_streambuf.cpp +++ b/src/arena/arena_streambuf.cpp @@ -51,7 +51,7 @@ namespace xo { this->color_escape_chars_ = 0; this->color_escape_start_ = nullptr; } - + void arena_streambuf::rewind_to(rewind_state s) { @@ -85,7 +85,7 @@ namespace xo { /* note: local_ppos_ invariant across expand_to() */ - arena_->expand(new_z); + arena_->expand(new_z, __PRETTY_FUNCTION__); char * p_base = (char *)(arena_->lo_); char * p_hi = (char *)(arena_->limit_); @@ -207,7 +207,7 @@ namespace xo { return this->pptr() - this->pbase(); } /*seekoff*/ - + } /*namespace mm*/ } /*namespace xo*/ diff --git a/utest/DArena.test.cpp b/utest/DArena.test.cpp index 03e1633f..9ac40384 100644 --- a/utest/DArena.test.cpp +++ b/utest/DArena.test.cpp @@ -118,7 +118,7 @@ namespace xo { REQUIRE(arena.allocated() == 0); size_t z2 = 512; - bool ok = arena.expand(z2); + bool ok = arena.expand(z2, __PRETTY_FUNCTION__); INFO(xtag("last_error", arena.last_error()));