xo-gc stack: + gc-report-object-types() primitive
This commit is contained in:
parent
70e6d3a5bc
commit
057f0acc72
16 changed files with 286 additions and 12 deletions
|
|
@ -120,6 +120,25 @@
|
|||
noexcept: true,
|
||||
attributes: [],
|
||||
},
|
||||
// bool report_types(obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output);
|
||||
{
|
||||
name: "report_object_types",
|
||||
doc: [
|
||||
"Report gc object types, at discretion of collector implementation.",
|
||||
"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<AGCObject> return type to avoid #include cycle"
|
||||
],
|
||||
return_type: "bool",
|
||||
args: [
|
||||
{type: "obj<AAllocator>", name: "report_mm"},
|
||||
{type: "obj<AAllocator>", name: "error_mm"},
|
||||
{type: "obj<AGCObject> *", name: "output"},
|
||||
],
|
||||
const: true,
|
||||
noexcept: true,
|
||||
attributes: [],
|
||||
},
|
||||
],
|
||||
nonconst_methods: [
|
||||
// bool install_type(const AGCObject & iface)
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ 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<AGCObject> return type to avoid #include cycle **/
|
||||
virtual bool report_statistics(Copaque data, obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) const noexcept = 0;
|
||||
/** Report gc object types, at discretion of collector implementation.
|
||||
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<AGCObject> return type to avoid #include cycle **/
|
||||
virtual bool report_object_types(Copaque data, obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) const noexcept = 0;
|
||||
|
||||
// nonconst methods
|
||||
/** install interface @p iface for representation with typeseq @p tseq
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ namespace mm {
|
|||
[[noreturn]] bool contains(Copaque, role, const void *) const noexcept override { _fatal(); }
|
||||
[[noreturn]] bool is_type_installed(Copaque, typeseq) const noexcept override { _fatal(); }
|
||||
[[noreturn]] bool report_statistics(Copaque, obj<AAllocator>, obj<AAllocator>, obj<AGCObject> *) const noexcept override { _fatal(); }
|
||||
[[noreturn]] bool report_object_types(Copaque, obj<AAllocator>, obj<AAllocator>, obj<AGCObject> *) const noexcept override { _fatal(); }
|
||||
|
||||
// nonconst methods
|
||||
[[noreturn]] bool install_type(Opaque, const AGCObject &) override;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ namespace mm {
|
|||
bool report_statistics(Copaque data, obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) const noexcept override {
|
||||
return I::report_statistics(_dcast(data), report_mm, error_mm, output);
|
||||
}
|
||||
bool report_object_types(Copaque data, obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) const noexcept override {
|
||||
return I::report_object_types(_dcast(data), report_mm, error_mm, output);
|
||||
}
|
||||
|
||||
// non-const methods
|
||||
bool install_type(Opaque data, const AGCObject & iface) override {
|
||||
|
|
|
|||
|
|
@ -97,6 +97,9 @@ public:
|
|||
bool report_statistics(obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) const noexcept {
|
||||
return O::iface()->report_statistics(O::data(), report_mm, error_mm, output);
|
||||
}
|
||||
bool report_object_types(obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) const noexcept {
|
||||
return O::iface()->report_object_types(O::data(), report_mm, error_mm, output);
|
||||
}
|
||||
|
||||
// non-const methods (still const in router!)
|
||||
bool install_type(const AGCObject & iface) {
|
||||
|
|
|
|||
|
|
@ -96,17 +96,23 @@ namespace xo {
|
|||
|
||||
/** true iff this slot is empty **/
|
||||
bool is_null() const noexcept {
|
||||
return this->iface()->_has_null_vptr();
|
||||
return this->_iface()->_has_null_vptr();
|
||||
}
|
||||
|
||||
bool is_occupied() const noexcept {
|
||||
return !this->is_null();
|
||||
}
|
||||
|
||||
AGCObject * iface() const noexcept {
|
||||
AGCObject * _iface() const noexcept {
|
||||
return std::launder((AGCObject *)&iface_[0]);
|
||||
}
|
||||
|
||||
AGCObject * iface() const noexcept {
|
||||
AGCObject * x = this->_iface();
|
||||
|
||||
return (x->_has_null_vptr() ? nullptr : x);
|
||||
}
|
||||
|
||||
/** Store interface pointer @p iface.
|
||||
* We just want the vtable here
|
||||
**/
|
||||
|
|
@ -230,6 +236,17 @@ namespace xo {
|
|||
obj<AAllocator> error_mm,
|
||||
obj<AGCObject> * p_output) const noexcept;
|
||||
|
||||
/** Report per-object-type information as a dictionary.
|
||||
* Scans to-space to count per-object-type information
|
||||
*
|
||||
* @p mm allocate stats dictionary from this allocator. May be the same as this collector.
|
||||
* @p error_mm Allocator for last-report error reporting when out-of-memory.
|
||||
* @p p_output on exit @p *p_output contains stats dictionary
|
||||
**/
|
||||
bool report_object_types(obj<AAllocator> mm,
|
||||
obj<AAllocator> error_mm,
|
||||
obj<AGCObject> * p_output) const noexcept;
|
||||
|
||||
// ----- queries -----
|
||||
|
||||
/** introspection for memory use.
|
||||
|
|
|
|||
|
|
@ -62,6 +62,11 @@ 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<AGCObject> return type to avoid #include cycle **/
|
||||
static bool report_statistics(const DX1Collector & self, obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) noexcept;
|
||||
/** Report gc object types, at discretion of collector implementation.
|
||||
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<AGCObject> return type to avoid #include cycle **/
|
||||
static bool report_object_types(const DX1Collector & self, obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) noexcept;
|
||||
|
||||
// non-const methods
|
||||
/** install interface @p iface for representation with typeseq @p tseq
|
||||
|
|
|
|||
|
|
@ -6,7 +6,10 @@
|
|||
#include "X1Collector.hpp"
|
||||
#include <xo/gc/DX1CollectorIterator.hpp>
|
||||
#include <xo/object2/Dictionary.hpp>
|
||||
#include <xo/object2/Array.hpp>
|
||||
#include <xo/object2/Integer.hpp>
|
||||
#include <xo/object2/Boolean.hpp>
|
||||
#include <xo/stringtable2/String.hpp>
|
||||
#include <xo/alloc2/GCObject.hpp>
|
||||
#include <xo/alloc2/Allocator.hpp>
|
||||
#include <xo/alloc2/Arena.hpp>
|
||||
|
|
@ -20,10 +23,12 @@
|
|||
#include <unistd.h> // for ::getpagesize()
|
||||
|
||||
namespace xo {
|
||||
// for report_statistics()
|
||||
// for report_statistics(), report_object_types()
|
||||
using xo::scm::DDictionary;
|
||||
using xo::scm::DArray;
|
||||
using xo::scm::DString;
|
||||
using xo::scm::DInteger;
|
||||
using xo::scm::DBoolean;
|
||||
|
||||
using xo::mm::AAllocator;
|
||||
using xo::facet::TypeRegistry;
|
||||
|
|
@ -347,6 +352,9 @@ namespace xo {
|
|||
|
||||
DDictionary * rpt = DDictionary::make(mm);
|
||||
|
||||
if (!rpt)
|
||||
return false;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// note: totals taken across both roles and generations,
|
||||
|
|
@ -388,6 +396,117 @@ namespace xo {
|
|||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
DX1Collector::report_object_types(obj<AAllocator> mm,
|
||||
obj<AAllocator> error_mm,
|
||||
obj<AGCObject> * p_output) const noexcept
|
||||
{
|
||||
scope log(XO_DEBUG(true));
|
||||
|
||||
(void)error_mm;
|
||||
|
||||
bool ok = true;
|
||||
|
||||
// stats, indexed by tseq
|
||||
// could use c++ vector in scratch space instead of running on
|
||||
// boxed types.
|
||||
//
|
||||
DArray * stats_v = DArray::empty(mm, object_types_.size());
|
||||
|
||||
if (!stats_v)
|
||||
return false;
|
||||
|
||||
stats_v->resize(stats_v->capacity());
|
||||
|
||||
log && log(xtag("object_types_.size", object_types_.size()),
|
||||
xtag("stats_v.capacity", stats_v->capacity()),
|
||||
xtag("stats_v.size", stats_v->size()));
|
||||
|
||||
// count #of occupied type slots
|
||||
std::uint32_t n_tseq_present = 0;
|
||||
// largest tseq present with non-null AGCObject* iface
|
||||
std::int32_t max_tseq = 0;
|
||||
|
||||
for (const ObjectTypeSlot & slot : object_types_) {
|
||||
AGCObject * iface = slot.iface();
|
||||
|
||||
if (iface) {
|
||||
typeseq tseq = iface->_typeseq();
|
||||
|
||||
++n_tseq_present;
|
||||
if (max_tseq < tseq.seqno())
|
||||
max_tseq = tseq.seqno();
|
||||
|
||||
assert(tseq.seqno() >= 0);
|
||||
|
||||
auto tname_sv = TypeRegistry::id2name(tseq);
|
||||
DString * tname = DString::from_view(mm, tname_sv);
|
||||
|
||||
DDictionary * recd = DDictionary::make(mm);
|
||||
|
||||
if (!recd)
|
||||
return false;
|
||||
|
||||
recd->upsert_cstr(mm, "name", obj<AGCObject,DString>(tname));
|
||||
recd->upsert_cstr(mm, "tseq", DInteger::box(mm, tseq.seqno()));
|
||||
recd->upsert_cstr(mm, "n-live", DInteger::box(mm, 0));
|
||||
recd->upsert_cstr(mm, "bytes", DInteger::box(mm, 0));
|
||||
|
||||
stats_v->assign_at(tseq.seqno(), obj<AGCObject,DDictionary>(recd));
|
||||
}
|
||||
}
|
||||
|
||||
// scan to-space, count objects by type
|
||||
|
||||
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 ix = info.tseq();
|
||||
size_t z = info.size();
|
||||
|
||||
auto recd = obj<AGCObject,DDictionary>::from(stats_v->at(ix));
|
||||
|
||||
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<AGCObject,DInteger>::from(n_live_opt.value());
|
||||
auto bytes_gco = obj<AGCObject,DInteger>::from(bytes_opt.value());
|
||||
|
||||
n_live_gco->assign_value(n_live_gco->value() + 1);
|
||||
bytes_gco->assign_value(bytes_gco->value() + z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stats_v->resize(max_tseq + 1);
|
||||
|
||||
DArray * final_stats_v = DArray::empty(mm, n_tseq_present);
|
||||
|
||||
for (std::size_t i = 0, n = stats_v->size(); i < n; ++i) {
|
||||
auto recd = stats_v->at(i);
|
||||
|
||||
if (recd) {
|
||||
bool ok = final_stats_v->push_back(recd);
|
||||
assert(ok);
|
||||
}
|
||||
}
|
||||
|
||||
*p_output = obj<AGCObject,DArray>(final_stats_v);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
size_type
|
||||
DX1Collector::header2size(header_type hdr) const noexcept
|
||||
{
|
||||
|
|
|
|||
|
|
@ -51,6 +51,12 @@ namespace xo {
|
|||
return self.report_statistics(report_mm, error_mm, output);
|
||||
}
|
||||
|
||||
auto
|
||||
ICollector_DX1Collector::report_object_types(const DX1Collector & self, obj<AAllocator> report_mm, obj<AAllocator> error_mm, obj<AGCObject> * output) noexcept -> bool
|
||||
{
|
||||
return self.report_object_types(report_mm, error_mm, output);
|
||||
}
|
||||
|
||||
auto
|
||||
ICollector_DX1Collector::install_type(DX1Collector & self, const AGCObject & iface) -> bool
|
||||
{
|
||||
|
|
|
|||
|
|
@ -112,6 +112,8 @@ namespace xo {
|
|||
|
||||
/** return value associated with @p key, if key is present **/
|
||||
std::optional<obj<AGCObject>> lookup(const DString * key) const noexcept;
|
||||
/** return value associated with @p key, if key is present **/
|
||||
std::optional<obj<AGCObject>> lookup_cstr(const char * key) const noexcept;
|
||||
|
||||
/** return element @p key-value pair at position @p index (0-based) **/
|
||||
std::pair<const DString *, obj<AGCObject>> at_index(size_type index) const;
|
||||
|
|
@ -139,6 +141,11 @@ namespace xo {
|
|||
**/
|
||||
bool try_update(const pair_type & kvpair);
|
||||
|
||||
/** update key-value pair for existing @p key to map to @p value.
|
||||
* false if @p key not already present.
|
||||
**/
|
||||
bool try_update_cstr(const char * key, obj<AGCObject> value);
|
||||
|
||||
/** convenience method:
|
||||
* try_upsert pair (k, @p value), after boxing c-style string @p key with @p mm to get k
|
||||
**/
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ namespace xo {
|
|||
|
||||
operator long() const noexcept { return value_; }
|
||||
|
||||
void assign_value(long x) noexcept { this->value_ = x; }
|
||||
|
||||
// GCObject facet
|
||||
|
||||
std::size_t shallow_size() const noexcept;
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ namespace xo {
|
|||
bool
|
||||
DArray::resize(size_type new_z) noexcept
|
||||
{
|
||||
if (new_z >= capacity_) {
|
||||
if (new_z > capacity_) {
|
||||
return false;
|
||||
} else if (new_z > size_) {
|
||||
// ensure new size is zeroed (we/re not zeroing if/when we shrink)
|
||||
|
|
@ -149,7 +149,25 @@ namespace xo {
|
|||
pps->write("]");
|
||||
return true;
|
||||
} else {
|
||||
pps->write("[...]");
|
||||
pps->write("[");
|
||||
|
||||
for (size_type i = 0, n = this->size(); i < n; ++i) {
|
||||
if (i == 0) {
|
||||
/* indent, but credit initial [ */
|
||||
pps->indent(std::max(pps->indent_width(), 1u) - 1);
|
||||
} else {
|
||||
/* indent after newline */
|
||||
pps->newline_indent(ppii.ci1());
|
||||
}
|
||||
|
||||
obj<APrintable> elt = this->at(i).to_facet<APrintable>();
|
||||
|
||||
assert(elt.data());
|
||||
|
||||
pps->pretty(elt);
|
||||
}
|
||||
|
||||
pps->write("]");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,21 @@ namespace xo {
|
|||
return {};
|
||||
}
|
||||
|
||||
std::optional<obj<AGCObject>>
|
||||
DDictionary::lookup_cstr(const char * key) const noexcept
|
||||
{
|
||||
for (DArray::size_type i = 0, z = keys_->size(); i < z; ++i) {
|
||||
auto i_key = obj<AGCObject,DString>::from(keys_->at(i));
|
||||
|
||||
assert(i_key);
|
||||
|
||||
if (strcmp(key, i_key->data()) == 0)
|
||||
return values_->at(i);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::pair<const DString *, obj<AGCObject>>
|
||||
DDictionary::at_index(size_type ix) const
|
||||
{
|
||||
|
|
@ -104,6 +119,24 @@ namespace xo {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DDictionary::try_update_cstr(const char * key, obj<AGCObject> value)
|
||||
{
|
||||
for (size_type i = 0, n = keys_->size(); i < n; ++i) {
|
||||
auto key_i = obj<AGCObject,DString>::from((*keys_)[i]);
|
||||
|
||||
assert(key_i);
|
||||
|
||||
if (strcmp(key, key_i->data()) == 0) {
|
||||
values_->assign_at(i, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
DDictionary::try_upsert_cstr(obj<AAllocator> mm, const char * key_cstr, obj<AGCObject> value)
|
||||
{
|
||||
|
|
@ -183,8 +216,7 @@ namespace xo {
|
|||
pps->write("{");
|
||||
|
||||
for (size_type i = 0, n = this->size(); i < n; ++i) {
|
||||
if (i > 0)
|
||||
pps->write(" ");
|
||||
pps->write(" ");
|
||||
|
||||
obj<APrintable> key
|
||||
= FacetRegistry::instance().variant<APrintable,AGCObject>((*keys_)[i]);
|
||||
|
|
@ -203,7 +235,7 @@ namespace xo {
|
|||
pps->write(";");
|
||||
}
|
||||
|
||||
pps->write("}");
|
||||
pps->write(" }");
|
||||
return true;
|
||||
} else {
|
||||
pps->write("{");
|
||||
|
|
@ -211,10 +243,10 @@ namespace xo {
|
|||
for (size_type i = 0, n = this->size(); i < n; ++i) {
|
||||
if (i == 0) {
|
||||
/* indent, but credit initial {. using same line for first (key,value) */
|
||||
ppii.pps()->indent(std::max(ppii.pps()->indent_width(), 1u) - 1);
|
||||
pps->indent(std::max(pps->indent_width(), 1u) - 1);
|
||||
} else {
|
||||
/* indent after newline */
|
||||
ppii.pps()->newline_indent(ppii.ci1());
|
||||
pps->newline_indent(ppii.ci1());
|
||||
}
|
||||
|
||||
obj<APrintable> key
|
||||
|
|
|
|||
|
|
@ -23,6 +23,10 @@ namespace xo {
|
|||
static DPrimitive_gco_0 * make_report_gc_statistics_pm(obj<AAllocator> mm,
|
||||
StringTable * stbl);
|
||||
|
||||
/** create primitive: report gc object-type statistics **/
|
||||
static DPrimitive_gco_0 * make_report_gc_object_types_pm(obj<AAllocator> mm,
|
||||
StringTable * stbl);
|
||||
|
||||
/** create primitive: request collection **/
|
||||
static DPrimitive_gco_1_gco * make_request_gc_pm(obj<AAllocator> mm,
|
||||
StringTable * stbl);
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ namespace xo {
|
|||
xfer_report_gc_statistics(obj<ARuntimeContext> rcx)
|
||||
{
|
||||
if (rcx.collector()) {
|
||||
// status currently only implemented for X1 collector
|
||||
|
||||
obj<AGCObject> stats;
|
||||
bool ok = rcx.collector().report_statistics(rcx.allocator(),
|
||||
rcx.error_allocator(),
|
||||
|
|
@ -51,6 +49,36 @@ namespace xo {
|
|||
return DPrimitive_gco_0::_make(mm, "report-gc-statistics", pm_ty, &xfer_report_gc_statistics);
|
||||
}
|
||||
|
||||
// ----- report-gc-object-types -----
|
||||
|
||||
obj<AGCObject>
|
||||
xfer_report_gc_object_types(obj<ARuntimeContext> rcx)
|
||||
{
|
||||
if (rcx.collector()) {
|
||||
obj<AGCObject> stats;
|
||||
bool ok = rcx.collector().report_object_types(rcx.allocator(), rcx.error_allocator(), &stats);
|
||||
|
||||
|
||||
if (ok && stats)
|
||||
return stats;
|
||||
}
|
||||
|
||||
return DBoolean::box(rcx.allocator(), false);
|
||||
}
|
||||
|
||||
DPrimitive_gco_0 *
|
||||
GcPrimitives::make_report_gc_object_types_pm(obj<AAllocator> mm,
|
||||
StringTable * stbl)
|
||||
{
|
||||
(void)stbl;
|
||||
|
||||
auto any_ty = DAtomicType::make(mm, Metatype::t_any());
|
||||
auto pm_ty = obj<AType,DFunctionType>(DFunctionType::_make(mm, any_ty));
|
||||
|
||||
return DPrimitive_gco_0::_make(mm, "report-gc-object-types", pm_ty, &xfer_report_gc_object_types);
|
||||
|
||||
}
|
||||
|
||||
// ----- request-gc -----
|
||||
|
||||
obj<AGCObject>
|
||||
|
|
|
|||
|
|
@ -137,6 +137,11 @@ namespace xo {
|
|||
GcPrimitives::make_report_gc_statistics_pm(mm, stbl),
|
||||
flags & InstallFlags::f_generalpurpose));
|
||||
|
||||
ok = ok & (PrimitiveRegistry::install_aux
|
||||
(sink,
|
||||
GcPrimitives::make_report_gc_object_types_pm(mm, stbl),
|
||||
flags & InstallFlags::f_generalpurpose));
|
||||
|
||||
ok = ok & (PrimitiveRegistry::install_aux
|
||||
(sink,
|
||||
GcPrimitives::make_request_gc_pm(mm, stbl),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue