xo-interpreter2 .. xo-arena. memory pool introspection

This commit is contained in:
Roland Conybeare 2026-02-03 01:05:36 -05:00
commit c931fca242
32 changed files with 157 additions and 172 deletions

View file

@ -5,6 +5,7 @@
#pragma once
#include <xo/arena/MemorySizeInfo.hpp>
#include <xo/arena/AllocError.hpp>
#include "AllocInfo.hpp"
//#include "AllocIterator.hpp"
@ -33,9 +34,11 @@ namespace xo {
struct AAllocator {
/** @defgroup mm-allocator-type-traits allocator type traits **/
///@{
/** @brief type used for allocation amounts **/
/** memory size report **/
using MemorySizeInfo = xo::mm::MemorySizeInfo;
/** type used for allocation amounts **/
using size_type = std::size_t;
/** @brief type used for allocation responses **/
/** type used for allocation responses **/
using value_type = std::byte *;
/** object header, if configured **/
using header_type = std::uint64_t;
@ -95,6 +98,12 @@ namespace xo {
* Includes alloc headers and guard regions
**/
virtual size_type allocated(Copaque d) const noexcept = 0;
/** call @p fn(i,n,info) for each memory pool owned by this allocator.
* Note: using std::function instead of obj<> to avoid leveling trouble
* with DArena
**/
virtual void visit_pools(Copaque d,
const MemorySizeVisitor & fn) const = 0;
/** true iff allocator @p d is responsible for memory at address @p p.
**/
virtual bool contains(Copaque d, const void * p) const noexcept = 0;
@ -146,12 +155,6 @@ namespace xo {
virtual value_type alloc_copy(Opaque d, value_type src) const = 0;
/** reset allocator @p d to empty state. **/
virtual void clear(Opaque d) const = 0;
#ifdef OBSOLETE
/** Destruct allocator @p d.
* Releases allocator memory to operating system.
**/
virtual void destruct_data(Opaque d) const = 0;
#endif
///@}
}; /*AAllocator*/

View file

@ -45,6 +45,8 @@ namespace xo {
[[noreturn]] size_type available(Copaque) const noexcept override { _fatal(); }
[[noreturn]] size_type allocated(Copaque) const noexcept override { _fatal(); }
[[noreturn]] bool contains(Copaque, const void *) const noexcept override { _fatal(); }
[[noreturn]] void visit_pools(Copaque,
const MemorySizeVisitor &) const override { _fatal(); }
[[noreturn]] AllocError last_error(Copaque) const noexcept override { _fatal(); }
[[noreturn]] AllocInfo alloc_info(Copaque, value_type) const noexcept override { _fatal(); }
// defn in .cpp - problematic to require compiler know vt<AAllocIterator> defn here

View file

@ -41,7 +41,7 @@ namespace xo {
typeseq _typeseq() const noexcept override { return s_typeseq; }
/** invoke native c++ dtor **/
void _drop(Opaque d) const noexcept override { _dcast(d).~DRepr(); }
// const methods
std::string_view name(Copaque d) const noexcept override { return I::name(_dcast(d)); }
@ -53,6 +53,7 @@ namespace xo {
bool contains(Copaque d, const void * p) const noexcept override {
return I::contains(_dcast(d), p);
}
void visit_pools(Copaque d, const MemorySizeVisitor & fn) const override { I::visit_pools(_dcast(d), fn); }
AllocError last_error(Copaque d) const noexcept override { return I::last_error(_dcast(d)); }
AllocInfo alloc_info(Copaque d, value_type mem) const noexcept override {
return I::alloc_info(_dcast(d), mem);

View file

@ -39,6 +39,7 @@ namespace xo {
size_type committed() const noexcept { return O::iface()->committed(O::data()); }
size_type available() const noexcept { return O::iface()->available(O::data()); }
size_type allocated() const noexcept { return O::iface()->allocated(O::data()); }
void visit_pools(const MemorySizeVisitor & fn) const { O::iface()->visit_pools(O::data(), fn); }
bool contains(const void * p) const noexcept { return O::iface()->contains(O::data(), p); }
AllocError last_error() const noexcept { return O::iface()->last_error(O::data()); }
AllocInfo alloc_info(value_type mem) const noexcept { return O::iface()->alloc_info(O::data(), mem); }

View file

@ -40,6 +40,8 @@ namespace xo {
static size_type committed(const DArena &) noexcept;
static size_type available(const DArena &) noexcept;
static size_type allocated(const DArena &) noexcept;
static void visit_pools(const DArena &,
const MemorySizeVisitor & visitor);
static bool contains(const DArena &, const void * p) noexcept;
static AllocError last_error(const DArena &) noexcept;
/** retrieve allocation bookkeeping info for @p mem from arena @p d **/

View file

@ -52,6 +52,13 @@ namespace xo {
return s.allocated();
}
void
IAllocator_DArena::visit_pools(const DArena & s,
const MemorySizeVisitor & visitor)
{
s.visit_pools(visitor);
}
bool
IAllocator_DArena::contains(const DArena & s,
const void * p) noexcept

View file

@ -19,13 +19,13 @@ namespace xo {
struct ArenaConfig {
/** @defgroup mm-arenaconfig-ctors **/
ArenaConfig with_name(std::string name) {
ArenaConfig with_name(std::string name) const {
ArenaConfig copy(*this);
copy.name_ = name;
return copy;
}
ArenaConfig with_size(std::size_t z) {
ArenaConfig with_size(std::size_t z) const {
ArenaConfig copy(*this);
copy.size_ = z;
return copy;

View file

@ -138,12 +138,14 @@ namespace xo {
**/
AllocHeader * end_header() const noexcept;
/** report memory use for this arena to @p fn.
* For DArena reporting just one pool = arena's memory range
**/
void visit_pools(const MemorySizeVisitor & fn) const;
/** get header from allocated object address **/
header_type * obj2hdr(void * obj) noexcept;
/** resource ocnsumption in normal form **/
MemorySizeInfo _store_info() const noexcept;
/** report alloc book-keeping info for allocation at @p mem
*
* Require:

View file

@ -45,7 +45,7 @@ namespace xo {
using value_type = std::pair<const Key, Value>;
using key_hash = Hash;
using key_equal = Equal;
using MemorySizeInfo = xo::mm::MemorySizeInfo;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
using byte = std::byte;
using group_type = detail::ControlGroup;
using store_type = detail::HashMapStore<Key, Value>;
@ -77,9 +77,8 @@ namespace xo {
iterator begin() { return _promote_iterator(_begin_aux()); }
iterator end() { return _promote_iterator(_end_aux()); }
std::size_t _n_store() const noexcept { return store_._n_store(); }
MemorySizeInfo _store_info(std::size_t i) const noexcept {
return store_._store_info(i);
void visit_pools(const MemorySizeVisitor & visitor) const {
return store_.visit_pools(visitor);
}
/** insert @p kv_pair into hash map.

View file

@ -82,7 +82,7 @@ namespace xo {
/** arena used for element storage
* (Might prefer obj<AResourceVisitor> here; refrain to avoid leveling violation)
**/
MemorySizeInfo _store_info() const { return store_._store_info(); }
void visit_pools(const MemorySizeVisitor & fn) const { store_.visit_pools(fn); }
/** reserve space, if possible, for at least @p z elements.
* Always limited by ArenaConfig.size_

View file

@ -83,8 +83,8 @@ namespace xo {
const_span_type occupied_range() const noexcept { return occupied_range_; }
const_span_type input_range() const noexcept { return input_range_; }
std::size_t _n_store() const noexcept;
MemorySizeInfo _store_info(std::size_t i) const noexcept;
/** report memory-size info for this buffer to @p fn **/
void visit_pools(const MemorySizeVisitor & fn) const;
/** verify DCircularBuffer invariants.
* Act on failure according to policy @p p

View file

@ -5,7 +5,8 @@
#pragma once
#include <cstdint>
#include <string_view>
#include <cstddef>
namespace xo {
namespace mm {
@ -31,7 +32,12 @@ namespace xo {
std::size_t reserved_ = 0;
};
}
/** function that visits MemorySizeInfo for a collection of @p n memory pools.
* Each pool reported with index @p i in [0, n), with associated
* size record @p info.
**/
using MemorySizeVisitor = std::function<void (const MemorySizeInfo & info)>;
}
}
/* end MemorySizeInfo.hpp */

View file

@ -19,7 +19,7 @@ namespace xo {
using group_type = detail::ControlGroup;
using control_vector_type = xo::mm::DArenaVector<uint8_t>;
using slot_vector_type = xo::mm::DArenaVector<value_type>;
using MemorySizeInfo = xo::mm::MemorySizeInfo;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
public:
/** group_exp2: number of groups {x, 2^x} **/
@ -48,16 +48,9 @@ namespace xo {
size_type capacity() const noexcept { return n_group_ * c_group_size; }
float load_factor() const noexcept { return size_ / static_cast<float>(n_slot_); }
std::size_t _n_store() const noexcept { return 2; }
MemorySizeInfo _store_info(std::size_t i) const noexcept {
switch (i) {
case 0:
return control_._store_info();
case 1:
return slots_._store_info();
}
return MemorySizeInfo::sentinel();
void visit_pools(const MemorySizeVisitor & visitor) const {
control_.visit_pools(visitor);
slots_.visit_pools(visitor);
}
void resize_from_empty(const std::pair<size_type,

View file

@ -165,13 +165,12 @@ namespace xo {
return (header_type *)((byte *)obj - sizeof(header_type));
}
MemorySizeInfo
DArena::_store_info() const noexcept
{
return MemorySizeInfo(config_.name_,
this->allocated(),
this->committed(),
this->reserved());
void
DArena::visit_pools(const MemorySizeVisitor & fn) const {
fn(MemorySizeInfo(config_.name_,
this->allocated(),
this->committed(),
this->reserved()));
}
AllocInfo

View file

@ -79,30 +79,16 @@ namespace xo {
{
}
std::size_t
DCircularBuffer::_n_store() const noexcept
void
DCircularBuffer::visit_pools(const MemorySizeVisitor & visitor) const
{
return 2;
}
visitor(MemorySizeInfo(config_.name_,
occupied_range_.size(),
mapped_range_.size(),
reserved_range_.size()));
MemorySizeInfo
DCircularBuffer::_store_info(std::size_t i) const noexcept
{
switch (i) {
case 0:
return MemorySizeInfo(config_.name_,
occupied_range_.size(),
mapped_range_.size(),
reserved_range_.size());
case 1:
return pinned_spans_._store_info();
default:
break;
}
return MemorySizeInfo::sentinel();
pinned_spans_.visit_pools(visitor);
}
bool
DCircularBuffer::verify_ok(verify_policy policy) const

View file

@ -21,7 +21,7 @@ namespace xo {
class StringTable {
public:
using DArena = xo::mm::DArena;
using MemorySizeInfo = xo::mm::MemorySizeInfo;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
using StringMap = xo::map::DArenaHashMap<std::string_view,
DUniqueString*>;
using size_type = StringMap::size_type;
@ -46,8 +46,8 @@ namespace xo {
**/
bool verify_ok(verify_policy p = verify_policy::throw_only()) const;
std::size_t _n_store() const noexcept;
MemorySizeInfo _store_info(std::size_t i) const noexcept;
/** visit string-table memory pools, call visitor(info) for each **/
void visit_pools(const MemorySizeVisitor & visitor) const;
private:
/** allocate string storage in this arena; use DString to represent each string.

View file

@ -160,22 +160,11 @@ namespace xo {
return true;
}
std::size_t
StringTable::_n_store() const noexcept
void
StringTable::visit_pools(const MemorySizeVisitor & visitor) const
{
return 1 + map_._n_store();
}
MemorySizeInfo
StringTable::_store_info(std::size_t i) const noexcept
{
if (i == 0)
return strings_._store_info();
if (i+1 < map_._n_store())
return map_._store_info(i-1);
return MemorySizeInfo::sentinel();
strings_.visit_pools(visitor);
map_.visit_pools(visitor);
}
} /*namespace scm*/

View file

@ -81,7 +81,7 @@ namespace xo {
/** faceted object pointer to this instance */
template <typename AFacet = AAllocator>
obj<AFacet,DX1Collector> ref() { return obj<AFacet,DX1Collector>(this); }
#ifdef NOT_YET
/** create instance with default configuration,
* generation size @p gen_z
@ -110,6 +110,11 @@ namespace xo {
/** total allocated memory in bytes, across all {role, generation} **/
size_type allocated_total() const noexcept;
/** introspection for memory use.
* Call @p visitor(info) for each pool owned by this allocator
**/
void visit_pools(const MemorySizeVisitor & visitor) const;
/** true iff address @p addr allocated from this collector
* in role @p r (according to current GC state)
**/

View file

@ -48,6 +48,8 @@ namespace xo {
static size_type available(const DX1Collector &) noexcept;
/** space used by @p d across all {roles, generations}. **/
static size_type allocated(const DX1Collector &) noexcept;
/** visit memory pools owned by this allocator; call fn(info) for each pool **/
static void visit_pools(const DX1Collector & d, const MemorySizeVisitor & fn);
/** true iff address @p p comes from collector @p d **/
static bool contains(const DX1Collector & d, const void * p) noexcept;
/** report last error, if any, for collector @p d **/

View file

@ -96,8 +96,18 @@ namespace xo {
.store_header_flag_ = false});
for (uint32_t igen = 0, ngen = cfg.n_generation_; igen < ngen; ++igen) {
space_storage_[0][igen] = DArena::map(cfg.arena_config_);
space_storage_[1][igen] = DArena::map(cfg.arena_config_);
{
char buf[40];
snprintf(buf, sizeof(buf), "x1-space-G%u-a", igen);
space_storage_[0][igen] = DArena::map(cfg.arena_config_.with_name(std::string(buf)));
}
{
char buf[40];
snprintf(buf, sizeof(buf), "x1-space-G%u-b", igen);
space_storage_[1][igen] = DArena::map(cfg.arena_config_.with_name(std::string(buf)));
}
space_[role::to_space()][igen] = &space_storage_[0][igen];
space_[role::from_space()][igen] = &space_storage_[1][igen];
@ -109,6 +119,19 @@ namespace xo {
}
}
void
DX1Collector::visit_pools(const MemorySizeVisitor & visitor) const
{
object_types_.visit_pools(visitor);
roots_.visit_pools(visitor);
for (uint32_t i = 0; i < c_n_role; ++i) {
for (uint32_t j = 0; j < config_.n_generation_; ++j) {
space_storage_[i][j].visit_pools(visitor);
}
}
}
bool
DX1Collector::contains(role r, const void * addr) const noexcept
{

View file

@ -56,6 +56,12 @@ namespace xo {
return d.allocated_total();
}
void
IAllocator_DX1Collector::visit_pools(const DX1Collector & d, const MemorySizeVisitor & visitor)
{
d.visit_pools(visitor);
}
bool
IAllocator_DX1Collector::contains(const DX1Collector & d, const void * addr) noexcept
{

View file

@ -61,14 +61,14 @@ namespace xo {
using Stack = void *;
using AAllocator = xo::mm::AAllocator;
using AGCObject = xo::mm::AGCObject;
using MemorySizeInfo = xo::mm::MemorySizeInfo;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
using span_type = xo::mm::span<const char>;
public:
VirtualSchematikaMachine(const VsmConfig & config);
size_t _n_store() const noexcept;
MemorySizeInfo _store_info(std::size_t i) const noexcept;
/** visit vsm-owned memory pools; call visitor(info) for each **/
void visit_pools(const MemorySizeVisitor & visitor) const;
/** begin interactive session. **/
void begin_interactive_session();

View file

@ -30,20 +30,11 @@ namespace xo {
reader_{config.rdr_config_, mm_.to_op()}
{}
std::size_t
VirtualSchematikaMachine::_n_store() const noexcept
void
VirtualSchematikaMachine::visit_pools(const MemorySizeVisitor & visitor) const
{
// oops. need something that goes through AAllocator api
return reader_._n_store();
}
MemorySizeInfo
VirtualSchematikaMachine::_store_info(std::size_t i) const noexcept
{
// oops. need something poly that goes through AAllocator api
return reader_._store_info(i);
mm_.visit_pools(visitor);
reader_.visit_pools(visitor);
}
void

View file

@ -28,6 +28,7 @@ namespace xo {
using xo::scm::VsmResultExt;
using xo::scm::DFloat;
using xo::mm::AGCObject;
using xo::mm::MemorySizeInfo;
using span_type = xo::scm::VirtualSchematikaMachine::span_type;
using Catch::Matchers::WithinAbs;
@ -55,6 +56,8 @@ namespace xo {
namespace ut {
TEST_CASE("VirtualSchematikaMachine-ctor", "[interpreter2][VSM]")
{
scope log(XO_DEBUG(true));
VsmConfig cfg;
VirtualSchematikaMachine vsm(cfg);
@ -73,6 +76,15 @@ namespace xo {
REQUIRE(res.remaining_.size() == 1);
REQUIRE(*res.remaining_.lo() == '\n');
auto visitor = [&log](const MemorySizeInfo & info) {
log && log(xtag("resource", info.resource_name_),
xtag("alloc", info.allocated_),
xtag("commit", info.committed_),
xtag("resv", info.reserved_));
};
vsm.visit_pools(visitor);
}
} /*namespace ut*/

View file

@ -36,7 +36,7 @@ namespace xo {
using AAllocator = xo::mm::AAllocator;
using ArenaConfig = xo::mm::ArenaConfig;
using DArena = xo::mm::DArena;
using MemorySizeInfo = xo::mm::MemorySizeInfo;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
using size_type = std::size_t;
public:
@ -62,10 +62,8 @@ namespace xo {
/** top of parser stack **/
obj<ASyntaxStateMachine> top_ssm() const;
/** number of distinct memory pools owned by PS **/
std::size_t _n_store() const noexcept;
/** memory consumption for i'th memory pool **/
MemorySizeInfo _store_info(std::size_t i) const noexcept;
/** visit psm-owned memory pools; call visitor(info) for each **/
void visit_pools(const MemorySizeVisitor & visitor) const;
///@}

View file

@ -156,7 +156,7 @@ namespace xo {
using token_type = Token;
using ArenaConfig = xo::mm::ArenaConfig;
using AAllocator = xo::mm::AAllocator;
using MemorySizeInfo = xo::mm::MemorySizeInfo;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
using ppindentinfo = xo::print::ppindentinfo;
using size_type = std::size_t;
@ -193,10 +193,8 @@ namespace xo {
/** top of parser stack **/
obj<ASyntaxStateMachine> top_ssm() const;
/** number of distinct memory pools owned by PS **/
std::size_t _n_store() const noexcept;
/** memory consumption for i'th memory pool **/
MemorySizeInfo _store_info(std::size_t i) const noexcept;
/** visit parser-owned memory pools; invoke visitor(info) for each **/
void visit_pools(const MemorySizeVisitor & visitor) const;
///@}
/** scm-schematikaparser-general-methods **/

View file

@ -36,7 +36,7 @@ namespace xo {
class SchematikaReader {
public:
using AAllocator = xo::mm::AAllocator;
using MemorySizeInfo = xo::mm::MemorySizeInfo;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
using span_type = xo::mm::span<const char>;
using size_type = std::size_t;
@ -44,8 +44,11 @@ namespace xo {
SchematikaReader(const ReaderConfig & config,
obj<AAllocator> expr_alloc);
std::size_t _n_store() const noexcept;
MemorySizeInfo _store_info(std::size_t i) const noexcept;
/** visit reader-owned memory pools; call visitor(info) for each.
* Specifically exclude expr_alloc, since we don't consider
* that reader-owned
**/
void visit_pools(const MemorySizeVisitor & visitor) const;
/** true iff parser is at top-level.
* false iff parser is working on incomplete expression

View file

@ -54,27 +54,14 @@ namespace xo {
return this->stack_->top();
}
std::size_t
ParserStateMachine::_n_store() const noexcept
void
ParserStateMachine::visit_pools(const MemorySizeVisitor & visitor) const
{
return stringtable_._n_store() + 1;
}
MemorySizeInfo
ParserStateMachine::_store_info(std::size_t i) const noexcept
{
size_t n0 = stringtable_._n_store();
if (i < n0)
return stringtable_._store_info(i);
if (i == n0)
return parser_alloc_._store_info();
stringtable_.visit_pools(visitor);
parser_alloc_.visit_pools(visitor);
// not counting expr_alloc_. We don't consider
// that to be owned by ParserStateMachine
return MemorySizeInfo::sentinel();
}
void

View file

@ -47,16 +47,10 @@ namespace xo {
return psm_.top_ssm();
}
std::size_t
SchematikaParser::_n_store() const noexcept
void
SchematikaParser::visit_pools(const MemorySizeVisitor & visitor) const
{
return psm_._n_store();
}
MemorySizeInfo
SchematikaParser::_store_info(std::size_t i) const noexcept
{
return psm_._store_info(i);
return psm_.visit_pools(visitor);
}
void

View file

@ -21,27 +21,11 @@ namespace xo {
{
}
std::size_t
SchematikaReader::_n_store() const noexcept
void
SchematikaReader::visit_pools(const MemorySizeVisitor & visitor) const
{
return tokenizer_._n_store() + parser_._n_store();
}
MemorySizeInfo
SchematikaReader::_store_info(std::size_t i) const noexcept
{
size_t n_tk = tokenizer_._n_store();
if (i < n_tk) {
return tokenizer_._store_info(i);
}
size_t n_pr = parser_._n_store();
if (i < n_tk + n_pr)
return parser_._store_info(i - n_tk);
return MemorySizeInfo::sentinel();
tokenizer_.visit_pools(visitor);
parser_.visit_pools(visitor);
}
bool

View file

@ -61,7 +61,7 @@ namespace xo {
using error_type = TokenizerError;
using DCircularBuffer = xo::mm::DCircularBuffer;
using CircularBufferConfig = xo::mm::CircularBufferConfig;
using MemorySizeInfo = xo::mm::MemorySizeInfo;
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
using span_type = xo::mm::span<const CharT>;
//using input_state_type = TkInputState;
using result_type = scan_result;
@ -91,10 +91,8 @@ namespace xo {
const TkInputState & input_state() const { return input_state_; }
#pragma GCC diagnostic pop
/** number of distinct memory pools owned by tokenizer **/
std::size_t _n_store() const noexcept;
/** memory consumption for i'th memory pool **/
MemorySizeInfo _store_info(std::size_t i) const noexcept;
/** visit tokenizer-owned memory pools; invoke visitor(info) for each one **/
void visit_pools(const MemorySizeVisitor & visitor) const;
///@}

View file

@ -22,16 +22,10 @@ namespace xo {
this->input_state_.discard_current_line();
}
std::size_t
Tokenizer::_n_store() const noexcept
void
Tokenizer::visit_pools(const MemorySizeVisitor & visitor) const
{
return input_buffer_._n_store();
}
MemorySizeInfo
Tokenizer::_store_info(std::size_t i) const noexcept
{
return input_buffer_._store_info(i);
input_buffer_.visit_pools(visitor);
}
bool