diff --git a/include/xo/gc/DX1Collector.hpp b/include/xo/gc/DX1Collector.hpp index 8279c8e..c81191d 100644 --- a/include/xo/gc/DX1Collector.hpp +++ b/include/xo/gc/DX1Collector.hpp @@ -250,6 +250,19 @@ namespace xo { obj error_mm, obj * p_output) const noexcept; + /** Report per-age-bucket information as an array of dictionaries. + * Scans to-space to count per-age statistics. + * Each dictionary has keys "n-live" and "bytes". + * Array index corresponds to object age. + * + * @p mm allocate stats from this allocator. + * @p error_mm allocator for error reporting when out-of-memory. + * @p p_output on exit @p *p_output contains stats array + **/ + bool report_object_ages(obj mm, + obj error_mm, + obj * p_output) const noexcept; + // ----- queries ----- /** introspection for memory use. diff --git a/include/xo/gc/detail/ICollector_DX1Collector.hpp b/include/xo/gc/detail/ICollector_DX1Collector.hpp index f73cb01..1ae5fac 100644 --- a/include/xo/gc/detail/ICollector_DX1Collector.hpp +++ b/include/xo/gc/detail/ICollector_DX1Collector.hpp @@ -71,6 +71,12 @@ Creates dictionary using memory from @p report_mm. If unable to comply (e.g. oom), return runtime error allocated from @p error_mm. Avoiding obj return type to avoid #include cycle **/ static bool report_object_types(const DX1Collector & self, obj report_mm, obj error_mm, obj * output) noexcept; + /** Report gc object ages, at discretion of collector implementation. +Creates array of dictionaries using memory from @p report_mm. +Each dictionary has keys n-live and bytes, indexed by object age. +If unable to comply (e.g. oom), return runtime error allocated from @p error_mm. +Avoiding obj return type to avoid #include cycle **/ + static bool report_object_ages(const DX1Collector & self, obj report_mm, obj error_mm, obj * output) noexcept; // non-const methods /** install interface @p iface for representation with typeseq @p tseq diff --git a/src/gc/DX1Collector.cpp b/src/gc/DX1Collector.cpp index 8a90a0b..f8eb751 100644 --- a/src/gc/DX1Collector.cpp +++ b/src/gc/DX1Collector.cpp @@ -529,6 +529,87 @@ namespace xo { return ok; } + bool + DX1Collector::report_object_ages(obj mm, + obj error_mm, + obj * p_output) const noexcept + { + scope log(XO_DEBUG(true)); + + (void)error_mm; + + std::uint64_t n_age = config_.arena_config_.header_.max_age() + 1; + + // stats, indexed by age + DArray * stats_v = DArray::empty(mm, n_age); + + if (!stats_v) + return false; + + // pre-populate with empty dictionaries for each age bucket + for (std::uint64_t a = 0; a < n_age; ++a) { + DDictionary * recd = DDictionary::make(mm); + + if (!recd) + return false; + + recd->upsert_cstr(mm, "age", DInteger::box(mm, a)); + recd->upsert_cstr(mm, "n-live", DInteger::box(mm, 0)); + recd->upsert_cstr(mm, "bytes", DInteger::box(mm, 0)); + + stats_v->push_back(obj(recd)); + } + + log && log(xtag("n_age", n_age), + xtag("stats_v.size", stats_v->size())); + + // scan to-space, count objects by age + + // track largest age with at least one object + std::int64_t max_age_present = 0; + + for (Generation g{0}; g < config_.n_generation_; ++g) { + const DArena * arena = this->get_space(role::to_space(), g); + + for (AllocInfo info : *arena) { + if (info.is_forwarding_tseq()) { + assert(false); + return false; + } + + uint32_t age = info.age(); + size_t z = info.size(); + + if (static_cast(age) > max_age_present) + max_age_present = age; + + auto recd = obj::from(stats_v->at(age)); + + assert(recd); + + auto n_live_opt = recd->lookup_cstr("n-live"); + assert(n_live_opt); + auto bytes_opt = recd->lookup_cstr("bytes"); + assert(bytes_opt); + + if (n_live_opt && bytes_opt) { + auto n_live_gco = obj::from(n_live_opt.value()); + auto bytes_gco = obj::from(bytes_opt.value()); + + n_live_gco->assign_value(n_live_gco->value() + 1); + bytes_gco->assign_value(bytes_gco->value() + z); + } + } + } + + // trim to only report ages up to max observed + stats_v->resize(max_age_present + 1); + + *p_output = obj(stats_v); + + return true; + } + size_type DX1Collector::header2size(header_type hdr) const noexcept { diff --git a/src/gc/facet/ICollector_DX1Collector.cpp b/src/gc/facet/ICollector_DX1Collector.cpp index b07f169..33a28aa 100644 --- a/src/gc/facet/ICollector_DX1Collector.cpp +++ b/src/gc/facet/ICollector_DX1Collector.cpp @@ -63,6 +63,12 @@ namespace xo { return self.report_object_types(report_mm, error_mm, output); } + auto + ICollector_DX1Collector::report_object_ages(const DX1Collector & self, obj report_mm, obj error_mm, obj * output) noexcept -> bool + { + return self.report_object_ages(report_mm, error_mm, output); + } + auto ICollector_DX1Collector::install_type(DX1Collector & self, const AGCObject & iface) -> bool {