diff --git a/software/src/config.cpp b/software/src/config.cpp index 1026ef187..75ad126c9 100644 --- a/software/src/config.cpp +++ b/software/src/config.cpp @@ -27,34 +27,6 @@ #include "tools.h" #include "string_builder.h" -#define UINT_SLOTS 512 -Config::ConfUint::Slot *uint_buf = nullptr; -size_t uint_buf_size = 0; - -#define INT_SLOTS 128 -Config::ConfInt::Slot *int_buf = nullptr; -size_t int_buf_size = 0; - -#define FLOAT_SLOTS 384 -Config::ConfFloat::Slot *float_buf = nullptr; -size_t float_buf_size = 0; - -#define STRING_SLOTS 384 -Config::ConfString::Slot *string_buf = nullptr; -size_t string_buf_size = 0; - -#define ARRAY_SLOTS 64 -Config::ConfArray::Slot *array_buf = nullptr; -size_t array_buf_size = 0; - -#define OBJECT_SLOTS 256 -Config::ConfObject::Slot *object_buf = nullptr; -size_t object_buf_size = 0; - -#define UNION_SLOTS 32 -Config::ConfUnion::Slot *union_buf = nullptr; -size_t union_buf_size = 0; - static ConfigRoot nullconf = Config{Config::ConfVariant{}}; static ConfigRoot confirmconf; @@ -818,77 +790,3 @@ void Config::set_updated(uint8_t api_backend_flag) ASSERT_MAIN_THREAD(); value.updated |= api_backend_flag; } - -void config_pre_init() -{ - uint_buf = Config::ConfUint::allocSlotBuf(UINT_SLOTS); - int_buf = Config::ConfInt::allocSlotBuf(INT_SLOTS); - float_buf = Config::ConfFloat::allocSlotBuf(FLOAT_SLOTS); - string_buf = Config::ConfString::allocSlotBuf(STRING_SLOTS); - array_buf = Config::ConfArray::allocSlotBuf(ARRAY_SLOTS); - object_buf = Config::ConfObject::allocSlotBuf(OBJECT_SLOTS); - union_buf = Config::ConfUnion::allocSlotBuf(UNION_SLOTS); - - uint_buf_size = UINT_SLOTS; - int_buf_size = INT_SLOTS; - float_buf_size = FLOAT_SLOTS; - string_buf_size = STRING_SLOTS; - array_buf_size = ARRAY_SLOTS; - object_buf_size = OBJECT_SLOTS; - union_buf_size = UNION_SLOTS; -} - -template -static void shrinkToFit(typename T::Slot * &buf, size_t &buf_size) { - ASSERT_MAIN_THREAD(); - size_t last_used_slot = 0; - int empty_slots = 0; - - // Search for last used slot first. - // All empty slots behind the last used slot will be cut off. - size_t pos = buf_size; - while (pos > 0) { - pos--; - if (!T::slotEmpty(pos)) { - last_used_slot = pos; - break; - } - } - while (pos > 0) { - pos--; - if (T::slotEmpty(pos)) { - empty_slots++; - } - } - - // Shrink the buffer so that the last used slot fits - // (we are not allowed to move used slots in the buffer!) - // and we have SLOT_HEADROOM free slots. - // If there are empty slots before the last used one, prefer those. - // If there are not enough, add some empty slots behind the last used one. - size_t new_size = last_used_slot + 1 + (size_t)std::max(0, SLOT_HEADROOM - empty_slots); - - // Don't increase buffer size. It will be increased automatically when required. - if (new_size >= buf_size) - return; - - auto new_buf = T::allocSlotBuf(new_size); - - for (size_t i = 0; i <= last_used_slot; ++i) - new_buf[i] = std::move(buf[i]); - - T::freeSlotBuf(buf); - buf = new_buf; - buf_size = new_size; -} - -void config_post_setup() -{ - shrinkToFit(uint_buf, uint_buf_size); - shrinkToFit(int_buf, int_buf_size); - shrinkToFit(float_buf, float_buf_size); - shrinkToFit(string_buf, string_buf_size); - shrinkToFit(array_buf, array_buf_size); - shrinkToFit(object_buf, object_buf_size); - shrinkToFit(union_buf, union_buf_size); -} diff --git a/software/src/config.h b/software/src/config.h index 3cf1af15e..aa027da5b 100644 --- a/software/src/config.h +++ b/software/src/config.h @@ -48,7 +48,6 @@ struct Config; class StringBuilder; -void config_pre_init(); void config_post_setup(); struct ConfigRoot; @@ -81,10 +80,9 @@ struct Config { Slot *getSlot(); public: - static bool slotEmpty(size_t i); + static bool slotEmpty(const Slot *slot); static constexpr const char *variantName = "ConfString"; static Slot *allocSlotBuf(size_t elements); - static void freeSlotBuf(Slot *buf); CoolString *getVal(); const CoolString *getVal() const; @@ -113,10 +111,9 @@ struct Config { void setSlot(float val, float min, float max); public: - static bool slotEmpty(size_t i); + static bool slotEmpty(const Slot *slot); static constexpr const char *variantName = "ConfFloat"; static Slot *allocSlotBuf(size_t elements); - static void freeSlotBuf(Slot *buf); float getVal() const; float getMin() const; @@ -143,10 +140,9 @@ struct Config { Slot *getSlot(); public: - static bool slotEmpty(size_t i); + static bool slotEmpty(const Slot *slot); static constexpr const char *variantName = "ConfInt"; static Slot *allocSlotBuf(size_t elements); - static void freeSlotBuf(Slot *buf); int32_t *getVal(); const int32_t *getVal() const; @@ -171,10 +167,9 @@ struct Config { Slot *getSlot(); public: - static bool slotEmpty(size_t i); + static bool slotEmpty(const Slot *slot); static constexpr const char *variantName = "ConfUint"; static Slot *allocSlotBuf(size_t elements); - static void freeSlotBuf(Slot *buf); uint32_t *getVal(); const uint32_t *getVal() const; @@ -207,10 +202,9 @@ struct Config { Slot *getSlot(); public: - static bool slotEmpty(size_t i); + static bool slotEmpty(const Slot *slot); static constexpr const char *variantName = "ConfArray"; static Slot *allocSlotBuf(size_t elements); - static void freeSlotBuf(Slot *buf); Config *get(size_t i); const Config *get(size_t i) const; @@ -236,10 +230,9 @@ struct Config { uint16_t idx; public: - static bool slotEmpty(size_t i); + static bool slotEmpty(const Slot *slot); static constexpr const char *variantName = "ConfObject"; static Slot *allocSlotBuf(size_t elements); - static void freeSlotBuf(Slot *buf); Config *get(const char *s, size_t s_len); const Config *get(const char *s, size_t s_len) const; @@ -265,10 +258,9 @@ struct Config { Slot *getSlot(); public: - static bool slotEmpty(size_t i); + static bool slotEmpty(const Slot *slot); static constexpr const char *variantName = "ConfUnion"; static Slot *allocSlotBuf(size_t elements); - static void freeSlotBuf(Slot *buf); uint8_t getTag() const; bool changeUnionVariant(uint8_t tag); diff --git a/software/src/config/conf_array.cpp b/software/src/config/conf_array.cpp index bdd020e98..28de588fa 100644 --- a/software/src/config/conf_array.cpp +++ b/software/src/config/conf_array.cpp @@ -18,13 +18,14 @@ */ #include "config/private.h" +#include "config/slot_allocator.h" #include "event_log_prefix.h" #include "main_dependencies.h" -bool Config::ConfArray::slotEmpty(size_t i) +bool Config::ConfArray::slotEmpty(const Slot *slot) { - return !array_buf[i].inUse; + return !slot->inUse; } Config::ConfArray::Slot *Config::ConfArray::allocSlotBuf(size_t elements) @@ -32,11 +33,6 @@ Config::ConfArray::Slot *Config::ConfArray::allocSlotBuf(size_t elements) return new Config::ConfArray::Slot[elements]; } -void Config::ConfArray::freeSlotBuf(Config::ConfArray::Slot *buf) -{ - delete[] buf; -} - [[gnu::noinline]] [[gnu::noreturn]] static void abort_on_index_oob(size_t index, const std::vector *val, const Config::ConfArray *this_arr) @@ -65,15 +61,15 @@ const Config *Config::ConfArray::get(size_t i) const return &(*val)[i]; } -std::vector *Config::ConfArray::getVal() { return &array_buf[idx].val; } -const std::vector *Config::ConfArray::getVal() const { return &array_buf[idx].val; } +std::vector *Config::ConfArray::getVal() { return &get_slot(idx)->val; } +const std::vector *Config::ConfArray::getVal() const { return &get_slot(idx)->val; } -const Config::ConfArray::Slot *Config::ConfArray::getSlot() const { return &array_buf[idx]; } -Config::ConfArray::Slot *Config::ConfArray::getSlot() { return &array_buf[idx]; } +const Config::ConfArray::Slot *Config::ConfArray::getSlot() const { return get_slot(idx); } +Config::ConfArray::Slot *Config::ConfArray::getSlot() { return get_slot(idx); } Config::ConfArray::ConfArray(std::vector val, const Config *prototype, uint16_t minElements, uint16_t maxElements, int8_t variantType) { - idx = nextSlot(array_buf, array_buf_size); + idx = nextSlot(); auto *slot = this->getSlot(); slot->inUse = true; @@ -92,7 +88,7 @@ Config::ConfArray::ConfArray(std::vector val, const Config *prototype, u Config::ConfArray::ConfArray(const ConfArray &cpy) { - idx = nextSlot(array_buf, array_buf_size); + idx = nextSlot(); // We have to mark this slot as in use here: // This array could contain a nested array that will be copied over // The inner array's copy constructor then takes the first free slot, i.e. @@ -119,6 +115,8 @@ Config::ConfArray::~ConfArray() slot->minElements = 0; slot->maxElements = 0; slot->variantType = 0; + + notify_free_slot(idx); } Config::ConfArray &Config::ConfArray::operator=(const ConfArray &cpy) diff --git a/software/src/config/conf_float.cpp b/software/src/config/conf_float.cpp index 95a035209..7fa78c442 100644 --- a/software/src/config/conf_float.cpp +++ b/software/src/config/conf_float.cpp @@ -18,22 +18,19 @@ */ #include "config/private.h" +#include "config/slot_allocator.h" +#include "tools/malloc.h" -bool Config::ConfFloat::slotEmpty(size_t i) { - return float_buf[i].val == 0 - && float_buf[i].min == 0 - && float_buf[i].max == 0; +bool Config::ConfFloat::slotEmpty(const Slot *slot) { + return slot->val == 0 + && slot->min == 0 + && slot->max == 0; } Config::ConfFloat::Slot *Config::ConfFloat::allocSlotBuf(size_t elements) { return (Config::ConfFloat::Slot *)calloc_32bit_addressed(elements, sizeof(Config::ConfFloat::Slot)); } -void Config::ConfFloat::freeSlotBuf(Config::ConfFloat::Slot *buf) -{ - free_any(buf); -} - typedef union { float f; uint32_t u; @@ -41,50 +38,52 @@ typedef union { float Config::ConfFloat::getVal() const { float_uint result; - result.u = float_buf[idx].val; + result.u = get_slot(idx)->val; return result.f; } void Config::ConfFloat::setVal(float f) { float_uint v; v.f = f; - float_buf[idx].val = v.u; + get_slot(idx)->val = v.u; } float Config::ConfFloat::getMin() const { float_uint result; - result.u = float_buf[idx].min; + result.u = get_slot(idx)->min; return result.f; } void Config::ConfFloat::setSlot(float val, float min, float max) { + Slot *slot = get_slot(idx); + float_uint va, mi, ma; va.f = val; mi.f = min; ma.f = max; - float_buf[idx].val = va.u; - float_buf[idx].min = mi.u; - float_buf[idx].max = ma.u; + slot->val = va.u; + slot->min = mi.u; + slot->max = ma.u; } float Config::ConfFloat::getMax() const { float_uint result; - result.u = float_buf[idx].max; + result.u = get_slot(idx)->max; return result.f; } -const Config::ConfFloat::Slot *Config::ConfFloat::getSlot() const { return &float_buf[idx]; } -Config::ConfFloat::Slot *Config::ConfFloat::getSlot() { return &float_buf[idx]; } +const Config::ConfFloat::Slot *Config::ConfFloat::getSlot() const { return get_slot(idx); } +Config::ConfFloat::Slot *Config::ConfFloat::getSlot() { return get_slot(idx); } Config::ConfFloat::ConfFloat(float val, float min, float max) { - idx = nextSlot(float_buf, float_buf_size); + idx = nextSlot(); this->setSlot(val, min, max); } Config::ConfFloat::ConfFloat(const ConfFloat &cpy) { - idx = nextSlot(float_buf, float_buf_size); + idx = nextSlot(); auto tmp = *cpy.getSlot(); *this->getSlot() = std::move(tmp); } @@ -98,6 +97,8 @@ Config::ConfFloat::~ConfFloat() slot->val = 0; slot->min = 0; slot->max = 0; + + notify_free_slot(idx); } Config::ConfFloat &Config::ConfFloat::operator=(const ConfFloat &cpy) diff --git a/software/src/config/conf_int.cpp b/software/src/config/conf_int.cpp index c53a05026..b96fee438 100644 --- a/software/src/config/conf_int.cpp +++ b/software/src/config/conf_int.cpp @@ -18,12 +18,14 @@ */ #include "config/private.h" +#include "config/slot_allocator.h" +#include "tools/malloc.h" -bool Config::ConfInt::slotEmpty(size_t i) +bool Config::ConfInt::slotEmpty(const Slot *slot) { - return int_buf[i].val == 0 - && int_buf[i].min == 0 - && int_buf[i].max == 0; + return slot->val == 0 + && slot->min == 0 + && slot->max == 0; } Config::ConfInt::Slot *Config::ConfInt::allocSlotBuf(size_t elements) @@ -31,20 +33,15 @@ Config::ConfInt::Slot *Config::ConfInt::allocSlotBuf(size_t elements) return (Config::ConfInt::Slot *)calloc_32bit_addressed(elements, sizeof(Config::ConfInt::Slot)); } -void Config::ConfInt::freeSlotBuf(Config::ConfInt::Slot *buf) -{ - free_any(buf); -} +int32_t* Config::ConfInt::getVal() { return &get_slot(idx)->val; } +const int32_t* Config::ConfInt::getVal() const { return &get_slot(idx)->val; } -int32_t* Config::ConfInt::getVal() { return &int_buf[idx].val; } -const int32_t* Config::ConfInt::getVal() const { return &int_buf[idx].val; } - -const Config::ConfInt::Slot *Config::ConfInt::getSlot() const { return &int_buf[idx]; } -Config::ConfInt::Slot *Config::ConfInt::getSlot() { return &int_buf[idx]; } +const Config::ConfInt::Slot *Config::ConfInt::getSlot() const { return get_slot(idx); } +Config::ConfInt::Slot *Config::ConfInt::getSlot() { return get_slot(idx); } Config::ConfInt::ConfInt(int32_t val, int32_t min, int32_t max) { - idx = nextSlot(int_buf, int_buf_size); + idx = nextSlot(); auto *slot = this->getSlot(); slot->val = val; slot->min = min; @@ -53,7 +50,7 @@ Config::ConfInt::ConfInt(int32_t val, int32_t min, int32_t max) Config::ConfInt::ConfInt(const ConfInt &cpy) { - idx = nextSlot(int_buf, int_buf_size); + idx = nextSlot(); auto tmp = *cpy.getSlot(); *this->getSlot() = std::move(tmp); } @@ -67,6 +64,8 @@ Config::ConfInt::~ConfInt() slot->val = 0; slot->min = 0; slot->max = 0; + + notify_free_slot(idx); } Config::ConfInt &Config::ConfInt::operator=(const ConfInt &cpy) diff --git a/software/src/config/conf_object.cpp b/software/src/config/conf_object.cpp index 190a9809d..ad4640765 100644 --- a/software/src/config/conf_object.cpp +++ b/software/src/config/conf_object.cpp @@ -18,14 +18,15 @@ */ #include "config/private.h" +#include "config/slot_allocator.h" #include "event_log_prefix.h" #include "main_dependencies.h" #include "tools/memory.h" -bool Config::ConfObject::slotEmpty(size_t i) +bool Config::ConfObject::slotEmpty(const Slot *slot) { - return object_buf[i].schema == nullptr; + return slot->schema == nullptr; } Config::ConfObject::Slot *Config::ConfObject::allocSlotBuf(size_t elements) @@ -33,11 +34,6 @@ Config::ConfObject::Slot *Config::ConfObject::allocSlotBuf(size_t elements) return (Config::ConfObject::Slot *)calloc_32bit_addressed(elements, sizeof(Config::ConfObject::Slot)); } -void Config::ConfObject::freeSlotBuf(Config::ConfObject::Slot *buf) -{ - free_any(buf); -} - [[gnu::noinline]] [[gnu::noreturn]] static void abort_on_key_not_found(const char *needle) @@ -113,8 +109,8 @@ const Config *Config::ConfObject::get(const char *needle, size_t needle_len) con abort_on_key_not_found(needle); } -const Config::ConfObject::Slot *Config::ConfObject::getSlot() const { return &object_buf[idx]; } -Config::ConfObject::Slot *Config::ConfObject::getSlot() { return &object_buf[idx]; } +const Config::ConfObject::Slot *Config::ConfObject::getSlot() const { return get_slot(idx); } +Config::ConfObject::Slot *Config::ConfObject::getSlot() { return get_slot(idx); } Config::ConfObject::ConfObject(std::vector> &&val) { @@ -133,7 +129,7 @@ Config::ConfObject::ConfObject(std::vector> &&va schema->keys[i].length = strlen(key); } - idx = nextSlot(object_buf, object_buf_size); + idx = nextSlot(); auto *slot = this->getSlot(); slot->schema = schema; @@ -146,7 +142,7 @@ Config::ConfObject::ConfObject(std::vector> &&va Config::ConfObject::ConfObject(const ConfObject &cpy) { - idx = nextSlot(object_buf, object_buf_size); + idx = nextSlot(); // TODO: could we just use *this = cpy here? @@ -179,6 +175,8 @@ Config::ConfObject::~ConfObject() delete[] slot->values; slot->values = nullptr; + + notify_free_slot(idx); } Config::ConfObject &Config::ConfObject::operator=(const ConfObject &cpy) diff --git a/software/src/config/conf_string.cpp b/software/src/config/conf_string.cpp index bab010ac5..50b4c3f58 100644 --- a/software/src/config/conf_string.cpp +++ b/software/src/config/conf_string.cpp @@ -18,10 +18,11 @@ */ #include "config/private.h" +#include "config/slot_allocator.h" -bool Config::ConfString::slotEmpty(size_t i) +bool Config::ConfString::slotEmpty(const Slot *slot) { - return !string_buf[i].val; + return !slot->val; } Config::ConfString::Slot *Config::ConfString::allocSlotBuf(size_t elements) @@ -35,20 +36,15 @@ Config::ConfString::Slot *Config::ConfString::allocSlotBuf(size_t elements) return result; } -void Config::ConfString::freeSlotBuf(Config::ConfString::Slot *buf) -{ - delete[] buf; -} +CoolString* Config::ConfString::getVal() { return &get_slot(idx)->val; } +const CoolString* Config::ConfString::getVal() const { return &get_slot(idx)->val; } -CoolString* Config::ConfString::getVal() { return &string_buf[idx].val; } -const CoolString* Config::ConfString::getVal() const { return &string_buf[idx].val; } - -const Config::ConfString::Slot* Config::ConfString::getSlot() const { return &string_buf[idx]; } -Config::ConfString::Slot* Config::ConfString::getSlot() { return &string_buf[idx]; } +const Config::ConfString::Slot* Config::ConfString::getSlot() const { return get_slot(idx); } +Config::ConfString::Slot* Config::ConfString::getSlot() { return get_slot(idx); } Config::ConfString::ConfString(const char *val, uint16_t minChars, uint16_t maxChars) { - idx = nextSlot(string_buf, string_buf_size); + idx = nextSlot(); auto *slot = this->getSlot(); slot->val = val; @@ -58,7 +54,7 @@ Config::ConfString::ConfString(const char *val, uint16_t minChars, uint16_t maxC Config::ConfString::ConfString(const String &val, uint16_t minChars, uint16_t maxChars) { - idx = nextSlot(string_buf, string_buf_size); + idx = nextSlot(); auto *slot = this->getSlot(); slot->val = val; @@ -68,7 +64,7 @@ Config::ConfString::ConfString(const String &val, uint16_t minChars, uint16_t ma Config::ConfString::ConfString(String &&val, uint16_t minChars, uint16_t maxChars) { - idx = nextSlot(string_buf, string_buf_size); + idx = nextSlot(); auto *slot = this->getSlot(); slot->val = std::move(val); @@ -78,7 +74,7 @@ Config::ConfString::ConfString(String &&val, uint16_t minChars, uint16_t maxChar Config::ConfString::ConfString(const ConfString &cpy) { - idx = nextSlot(string_buf, string_buf_size); + idx = nextSlot(); // this->getSlot() is evaluated before the RHS of the assignment is copied over. // This results in the LHS pointing to a deallocated array if copying the RHS @@ -98,6 +94,8 @@ Config::ConfString::~ConfString() slot->val.make_invalid(); slot->minChars = 0; slot->maxChars = 0; + + notify_free_slot(idx); } Config::ConfString &Config::ConfString::operator=(const ConfString &cpy) diff --git a/software/src/config/conf_uint.cpp b/software/src/config/conf_uint.cpp index bd3e4fda7..8100e7149 100644 --- a/software/src/config/conf_uint.cpp +++ b/software/src/config/conf_uint.cpp @@ -18,11 +18,13 @@ */ #include "config/private.h" +#include "config/slot_allocator.h" +#include "tools/malloc.h" -bool Config::ConfUint::slotEmpty(size_t i) { - return uint_buf[i].val == 0 - && uint_buf[i].min == 0 - && uint_buf[i].max == 0; +bool Config::ConfUint::slotEmpty(const Slot *slot) { + return slot->val == 0 + && slot->min == 0 + && slot->max == 0; } Config::ConfUint::Slot *Config::ConfUint::allocSlotBuf(size_t elements) @@ -30,20 +32,15 @@ Config::ConfUint::Slot *Config::ConfUint::allocSlotBuf(size_t elements) return (Config::ConfUint::Slot *)calloc_32bit_addressed(elements, sizeof(Config::ConfUint::Slot)); } -void Config::ConfUint::freeSlotBuf(Config::ConfUint::Slot *buf) -{ - free_any(buf); -} +uint32_t* Config::ConfUint::getVal() { return &get_slot(idx)->val; } +const uint32_t* Config::ConfUint::getVal() const { return &get_slot(idx)->val; } -uint32_t* Config::ConfUint::getVal() { return &uint_buf[idx].val; } -const uint32_t* Config::ConfUint::getVal() const { return &uint_buf[idx].val; } - -const Config::ConfUint::Slot *Config::ConfUint::getSlot() const { return &uint_buf[idx]; } -Config::ConfUint::Slot *Config::ConfUint::getSlot() { return &uint_buf[idx]; } +const Config::ConfUint::Slot *Config::ConfUint::getSlot() const { return get_slot(idx); } +Config::ConfUint::Slot *Config::ConfUint::getSlot() { return get_slot(idx); } Config::ConfUint::ConfUint(uint32_t val, uint32_t min, uint32_t max) { - idx = nextSlot(uint_buf, uint_buf_size); + idx = nextSlot(); auto *slot = this->getSlot(); slot->val = val; slot->min = min; @@ -52,7 +49,7 @@ Config::ConfUint::ConfUint(uint32_t val, uint32_t min, uint32_t max) Config::ConfUint::ConfUint(const ConfUint &cpy) { - idx = nextSlot(uint_buf, uint_buf_size); + idx = nextSlot(); auto tmp = *cpy.getSlot(); *this->getSlot() = std::move(tmp); } @@ -66,6 +63,8 @@ Config::ConfUint::~ConfUint() slot->val = 0; slot->min = 0; slot->max = 0; + + notify_free_slot(idx); } Config::ConfUint &Config::ConfUint::operator=(const ConfUint &cpy) diff --git a/software/src/config/conf_union.cpp b/software/src/config/conf_union.cpp index 6a061f58d..0c47f2813 100644 --- a/software/src/config/conf_union.cpp +++ b/software/src/config/conf_union.cpp @@ -18,10 +18,11 @@ */ #include "config/private.h" +#include "config/slot_allocator.h" -bool Config::ConfUnion::slotEmpty(size_t i) +bool Config::ConfUnion::slotEmpty(const Slot *slot) { - return union_buf[i].prototypes == nullptr; + return slot->prototypes == nullptr; } Config::ConfUnion::Slot *Config::ConfUnion::allocSlotBuf(size_t elements) @@ -29,36 +30,32 @@ Config::ConfUnion::Slot *Config::ConfUnion::allocSlotBuf(size_t elements) return new Config::ConfUnion::Slot[elements]; } -void Config::ConfUnion::freeSlotBuf(Config::ConfUnion::Slot *buf) -{ - delete[] buf; -} - bool Config::ConfUnion::changeUnionVariant(uint8_t tag) { - auto &slot = union_buf[idx]; - for (int i = 0; i < slot.prototypes_len; ++i) { - if (slot.prototypes[i].tag == tag) { - union_buf[idx].tag = tag; - slot.val = slot.prototypes[i].config; - slot.val.set_updated(0xFF); + Slot *slot = get_slot(idx); + for (int i = 0; i < slot->prototypes_len; ++i) { + if (slot->prototypes[i].tag == tag) { + slot->tag = tag; + slot->val = slot->prototypes[i].config; + slot->val.set_updated(0xFF); return true; } } return false; } -uint8_t Config::ConfUnion::getTag() const { return union_buf[idx].tag; } -Config* Config::ConfUnion::getVal() { return &union_buf[idx].val; } -const Config* Config::ConfUnion::getVal() const { return &union_buf[idx].val; } +uint8_t Config::ConfUnion::getTag() const { return get_slot(idx)->tag; } + +Config* Config::ConfUnion::getVal() { return &get_slot(idx)->val; } +const Config* Config::ConfUnion::getVal() const { return &get_slot(idx)->val; } -const Config::ConfUnion::Slot* Config::ConfUnion::getSlot() const { return &union_buf[idx]; } -Config::ConfUnion::Slot* Config::ConfUnion::getSlot() { return &union_buf[idx]; } +const Config::ConfUnion::Slot* Config::ConfUnion::getSlot() const { return get_slot(idx); } +Config::ConfUnion::Slot* Config::ConfUnion::getSlot() { return get_slot(idx); } Config::ConfUnion::ConfUnion(const Config &val, uint8_t tag, uint8_t prototypes_len, const ConfUnionPrototypeInternal prototypes[]) { - idx = nextSlot(union_buf, union_buf_size); + idx = nextSlot(); auto *slot = this->getSlot(); slot->tag = tag; @@ -69,7 +66,7 @@ Config::ConfUnion::ConfUnion(const Config &val, uint8_t tag, uint8_t prototypes_ Config::ConfUnion::ConfUnion(const ConfUnion &cpy) { - idx = nextSlot(union_buf, union_buf_size); + idx = nextSlot(); // We have to mark this slot as in use here: // This union could contain a nested union that will be copied over @@ -96,6 +93,8 @@ Config::ConfUnion::~ConfUnion() slot->tag = 0; slot->prototypes_len = 0; slot->prototypes = nullptr; + + notify_free_slot(idx); } Config::ConfUnion &Config::ConfUnion::operator=(const ConfUnion &cpy) @@ -106,11 +105,9 @@ Config::ConfUnion &Config::ConfUnion::operator=(const ConfUnion &cpy) *this->getSlot() = *cpy.getSlot(); - return *this; } - Config::ConfUnion::ConfUnion(ConfUnion &&cpy) { this->idx = cpy.idx; cpy.idx = std::numeric_limits::max(); diff --git a/software/src/config/private.h b/software/src/config/private.h index 8b14445d2..f1de0de50 100644 --- a/software/src/config/private.h +++ b/software/src/config/private.h @@ -20,9 +20,6 @@ #pragma once #include "config.h" -#include "tools/malloc.h" - -#define SLOT_HEADROOM 20 struct ConfStringSlot { CoolString val = ""; @@ -77,47 +74,3 @@ struct ConfUnionSlot { Config val; const ConfUnionPrototypeInternal *prototypes = nullptr; }; - -extern Config::ConfUint::Slot *uint_buf; -extern size_t uint_buf_size; - -extern Config::ConfInt::Slot *int_buf; -extern size_t int_buf_size; - -extern Config::ConfFloat::Slot *float_buf; -extern size_t float_buf_size; - -extern Config::ConfString::Slot *string_buf; -extern size_t string_buf_size; - -extern Config::ConfArray::Slot *array_buf; -extern size_t array_buf_size; - -extern Config::ConfObject::Slot *object_buf; -extern size_t object_buf_size; - -extern Config::ConfUnion::Slot *union_buf; -extern size_t union_buf_size; - -template -static size_t nextSlot(typename T::Slot *&buf, size_t &buf_size) { - ASSERT_MAIN_THREAD(); - for (size_t i = 0; i < buf_size; i++) - { - if (!T::slotEmpty(i)) - continue; - - return i; - } - - auto new_buf = T::allocSlotBuf(buf_size + SLOT_HEADROOM); - - for (size_t i = 0; i < buf_size; ++i) - new_buf[i] = std::move(buf[i]); - - T::freeSlotBuf(buf); - buf = new_buf; - size_t result = buf_size; - buf_size = buf_size + SLOT_HEADROOM; - return result; -} diff --git a/software/src/config/slot_allocator.cpp b/software/src/config/slot_allocator.cpp new file mode 100644 index 000000000..55a0fbc07 --- /dev/null +++ b/software/src/config/slot_allocator.cpp @@ -0,0 +1,238 @@ +/* esp32-firmware + * Copyright (C) 2025 Mattias Schäffersmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config/slot_allocator.h" + +#include "event_log_prefix.h" +#include "main_dependencies.h" +#include "tools/malloc.h" + +[[gnu::section(".iram.data")]] static Superblock first_superblock_ConfUint; +[[gnu::section(".iram.data")]] static Superblock first_superblock_ConfInt; +[[gnu::section(".iram.data")]] static Superblock first_superblock_ConfFloat; +[[gnu::section(".iram.data")]] static Superblock first_superblock_ConfString; +[[gnu::section(".iram.data")]] static Superblock first_superblock_ConfArray; +[[gnu::section(".iram.data")]] static Superblock first_superblock_ConfObject; +[[gnu::section(".iram.data")]] static Superblock first_superblock_ConfUnion; + +template<> Superblock *RootBlock::first_superblock = &first_superblock_ConfUint; +template<> Superblock *RootBlock::first_superblock = &first_superblock_ConfInt; +template<> Superblock *RootBlock::first_superblock = &first_superblock_ConfFloat; +template<> Superblock *RootBlock::first_superblock = &first_superblock_ConfString; +template<> Superblock *RootBlock::first_superblock = &first_superblock_ConfArray; +template<> Superblock *RootBlock::first_superblock = &first_superblock_ConfObject; +template<> Superblock *RootBlock::first_superblock = &first_superblock_ConfUnion; + +template uint16_t RootBlock::first_free_slot = 0; +template uint16_t RootBlock::allocated_blocks = 0; + +#if MODULE_DEBUG_AVAILABLE() +template uint16_t RootBlock::used_slots = 0; +template uint16_t RootBlock::last_used_slot = 0; +template uint16_t RootBlock::slots_hwm = 0; + +template size_t RootBlock::allocs = 0; +template size_t RootBlock::frees = 0; +#endif + +template +size_t nextSlot() +{ + ASSERT_MAIN_THREAD(); + + Superblock *superblock = RootBlock::first_superblock; + size_t superblock_offset = 0; + + size_t block_i = RootBlock::first_free_slot / SlotConfig::slots_per_block; + size_t slot_i = RootBlock::first_free_slot % SlotConfig::slots_per_block; + + while (block_i > SlotConfig::blocks_per_superblock) { + block_i -= SlotConfig::blocks_per_superblock; + superblock = superblock->next_superblock; + superblock_offset++; + } + + while (true) { + for (; block_i < SlotConfig::blocks_per_superblock; block_i++) { + typename ConfigT::Slot *block = superblock->blocks[block_i]; + if (!block) { + block = ConfigT::allocSlotBuf(SlotConfig::slots_per_block); + superblock->blocks[block_i] = block; + RootBlock::allocated_blocks++; + } + + for (; slot_i < SlotConfig::slots_per_block; slot_i++) { + if (ConfigT::slotEmpty(block + slot_i)) { + size_t idx = superblock_offset * SlotConfig::slots_per_superblock + block_i * SlotConfig::slots_per_block + slot_i; + RootBlock::first_free_slot = idx + 1; + +#if MODULE_DEBUG_AVAILABLE() + RootBlock::used_slots++; + RootBlock::allocs++; + + if (idx > RootBlock::last_used_slot) { + RootBlock::last_used_slot = idx; + + if (idx > RootBlock::slots_hwm) { + RootBlock::slots_hwm = idx; + } + } +#endif + return idx; + } + } + slot_i = 0; + } + + if (!superblock->next_superblock) { + Superblock *new_superblock = static_cast(calloc_32bit_addressed(1, sizeof(*superblock))); + superblock->next_superblock = new_superblock; + logger.printfln("Allocated new superblock at %p for %s", new_superblock, ConfigT::variantName); + } + superblock = superblock->next_superblock; + superblock_offset++; + + block_i = 0; + } +} + +template size_t nextSlot(); +template size_t nextSlot(); +template size_t nextSlot(); +template size_t nextSlot(); +template size_t nextSlot(); +template size_t nextSlot(); +template size_t nextSlot(); + +template +typename ConfigT::Slot *get_slot(size_t idx) +{ + Superblock *superblock = RootBlock::first_superblock; + + size_t block_idx = idx / SlotConfig::slots_per_block; + size_t slot_idx = idx % SlotConfig::slots_per_block; + + while (block_idx >= SlotConfig::blocks_per_superblock) { + block_idx -= SlotConfig::blocks_per_superblock; + superblock = superblock->next_superblock; + } + + return superblock->blocks[block_idx] + slot_idx; +} + +template Config::ConfUint::Slot *get_slot(size_t idx); +template Config::ConfInt::Slot *get_slot(size_t idx); +template Config::ConfFloat::Slot *get_slot(size_t idx); +template Config::ConfString::Slot *get_slot(size_t idx); +template Config::ConfArray::Slot *get_slot(size_t idx); +template Config::ConfObject::Slot *get_slot(size_t idx); +template Config::ConfUnion::Slot *get_slot(size_t idx); + +template +static void check_slot_accounting() +{ + size_t last_idx = RootBlock::first_free_slot; + Superblock *superblock = RootBlock::first_superblock; + + size_t used_slots = 0; + + while (superblock) { + for (size_t block_i = 0; block_i < SlotConfig::blocks_per_superblock; block_i++) { + typename ConfigT::Slot *block = superblock->blocks[block_i]; + + if (!block) { + break; + } + + for (size_t slot_i = 0; slot_i < SlotConfig::slots_per_block; slot_i++) { + if (!ConfigT::slotEmpty(block + slot_i)) { + used_slots++; + } + } + } + + superblock = superblock->next_superblock; + } + + if (used_slots != RootBlock::used_slots) { + logger.printfln("used_slots mismatch for %s. Counted %zu, expected %hu. allocs %zu frees %zu diff %zu", + ConfigT::variantName, used_slots, RootBlock::used_slots, RootBlock::allocs, RootBlock::frees, RootBlock::allocs - RootBlock::frees); + } + + superblock = RootBlock::first_superblock; + + while (true) { + if (!superblock) { + if (last_idx == 0) { + // Points to first slot in unallocated superblock, which is ok. + return; + } + + logger.printfln("First free slot %zu points into an unallocated superblock for %s", last_idx, ConfigT::variantName); + return; + } + + for (size_t block_i = 0; block_i < SlotConfig::blocks_per_superblock; block_i++) { + typename ConfigT::Slot *block = superblock->blocks[block_i]; + + if (!block) { + if (last_idx == 0) { + // Points to first slot in unallocated block, which is ok. + return; + } + + logger.printfln("First free slot %zu points into an unallocated block %zu for %s", last_idx, block_i, ConfigT::variantName); + return; + } + + for (size_t slot_i = 0; slot_i < SlotConfig::slots_per_block; slot_i++) { + if (slot_i >= last_idx) { + return; + } + if (ConfigT::slotEmpty(block + slot_i)) { + if (slot_i != last_idx) { + logger.printfln("First free slot mismatch. Expected %zu but found %zu in block %zu for %s.", last_idx, slot_i, block_i, ConfigT::variantName); + } + + return; + } + } + + last_idx -= SlotConfig::slots_per_block; + } + + superblock = superblock->next_superblock; + } + +} + +void config_post_setup() +{ +#ifdef DEBUG_FS_ENABLE + task_scheduler.scheduleWithFixedDelay([]() { + check_slot_accounting(); + check_slot_accounting(); + check_slot_accounting(); + check_slot_accounting(); + check_slot_accounting(); + check_slot_accounting(); + check_slot_accounting(); + }, 1_m, 1_m); +#endif +} diff --git a/software/src/config/slot_allocator.h b/software/src/config/slot_allocator.h new file mode 100644 index 000000000..c2b6f2ee3 --- /dev/null +++ b/software/src/config/slot_allocator.h @@ -0,0 +1,224 @@ +/* esp32-firmware + * Copyright (C) 2025 Mattias Schäffersmann + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#pragma once + +#include + +#include "config.h" +#include "config/private.h" +#include "main_available.h" + +template +struct SlotConfig; + +template<> struct SlotConfig { + static constexpr const size_t slots_per_superblock = 2048; + static constexpr const size_t slots_per_block = 256; + static constexpr const size_t blocks_per_superblock = slots_per_superblock / slots_per_block;; + + static_assert(slots_per_superblock % slots_per_block == 0); + static_assert((slots_per_block & (slots_per_block - 1)) == 0); +}; + +template<> struct SlotConfig { + static constexpr const size_t slots_per_superblock = 512; + static constexpr const size_t slots_per_block = 64; + static constexpr const size_t blocks_per_superblock = slots_per_superblock / slots_per_block;; + + static_assert(slots_per_superblock % slots_per_block == 0); + static_assert((slots_per_block & (slots_per_block - 1)) == 0); +}; + +template<> struct SlotConfig { + static constexpr const size_t slots_per_superblock = 1024; + static constexpr const size_t slots_per_block = 128; + static constexpr const size_t blocks_per_superblock = slots_per_superblock / slots_per_block;; + + static_assert(slots_per_superblock % slots_per_block == 0); + static_assert((slots_per_block & (slots_per_block - 1)) == 0); +}; + +template<> struct SlotConfig { + static constexpr const size_t slots_per_superblock = 512; + static constexpr const size_t slots_per_block = 64; + static constexpr const size_t blocks_per_superblock = slots_per_superblock / slots_per_block;; + + static_assert(slots_per_superblock % slots_per_block == 0); + static_assert((slots_per_block & (slots_per_block - 1)) == 0); +}; + +template<> struct SlotConfig { + static constexpr const size_t slots_per_superblock = 256; + static constexpr const size_t slots_per_block = 32; + static constexpr const size_t blocks_per_superblock = slots_per_superblock / slots_per_block;; + + static_assert(slots_per_superblock % slots_per_block == 0); + static_assert((slots_per_block & (slots_per_block - 1)) == 0); +}; + +template<> struct SlotConfig { + static constexpr const size_t slots_per_superblock = 2048; + static constexpr const size_t slots_per_block = 256; + static constexpr const size_t blocks_per_superblock = slots_per_superblock / slots_per_block;; + + static_assert(slots_per_superblock % slots_per_block == 0); + static_assert((slots_per_block & (slots_per_block - 1)) == 0); +}; + +template<> struct SlotConfig { + static constexpr const size_t slots_per_superblock = 128; + static constexpr const size_t slots_per_block = 16; + static constexpr const size_t blocks_per_superblock = slots_per_superblock / slots_per_block;; + + static_assert(slots_per_superblock % slots_per_block == 0); + static_assert((slots_per_block & (slots_per_block - 1)) == 0); +}; + +template +struct Superblock { + typename ConfigT::Slot *blocks[SlotConfig::blocks_per_superblock]; + Superblock *next_superblock; +}; + +template +struct RootBlock { + static Superblock *first_superblock; + static uint16_t allocated_blocks; + static uint16_t first_free_slot; + +#if MODULE_DEBUG_AVAILABLE() + static uint16_t used_slots; + static uint16_t last_used_slot; + static uint16_t slots_hwm; + + static size_t allocs; + static size_t frees; +#endif +}; + +template +size_t nextSlot(); + +extern template size_t nextSlot(); +extern template size_t nextSlot(); +extern template size_t nextSlot(); +extern template size_t nextSlot(); +extern template size_t nextSlot(); +extern template size_t nextSlot(); +extern template size_t nextSlot(); + +#if MODULE_DEBUG_AVAILABLE() +template +size_t find_last_used_slot(Superblock *superblock, size_t last_slot_to_check) +{ + if (last_slot_to_check >= SlotConfig::slots_per_superblock) { + size_t last_slot = find_last_used_slot(superblock->next_superblock, last_slot_to_check - SlotConfig::slots_per_superblock); + + if (last_slot < std::numeric_limits::max()) { + return last_slot + SlotConfig::slots_per_superblock; + } + } + + size_t last_block_idx = last_slot_to_check / SlotConfig::slots_per_block; + size_t last_slot_idx = last_slot_to_check % SlotConfig::slots_per_block; + + while (true) { + typename ConfigT::Slot *block = superblock->blocks[last_block_idx]; + + while (true) { + if (!ConfigT::slotEmpty(block + last_slot_idx)) { + return last_slot_idx + last_block_idx * SlotConfig::slots_per_block; + } + + if (last_slot_idx == 0) { + break; + } + last_slot_idx--; + } + + if (last_block_idx == 0) { + break; + } + last_block_idx--; + last_slot_idx = SlotConfig::slots_per_block - 1; + } + + return std::numeric_limits::max(); +} +#endif + +template +inline void notify_free_slot(size_t idx) +{ + ASSERT_MAIN_THREAD(); + + if (idx < RootBlock::first_free_slot) { + RootBlock::first_free_slot = static_cast(idx); + } + +#if MODULE_DEBUG_AVAILABLE() + RootBlock::used_slots--; + RootBlock::frees++; + + if (idx == RootBlock::last_used_slot && RootBlock::last_used_slot > 0) { + RootBlock::last_used_slot = find_last_used_slot(RootBlock::first_superblock, RootBlock::last_used_slot - 1u); + } +#endif +} + +template +typename ConfigT::Slot *get_slot(size_t idx); + +extern template Config::ConfUint::Slot *get_slot(size_t idx); +extern template Config::ConfInt::Slot *get_slot(size_t idx); +extern template Config::ConfFloat::Slot *get_slot(size_t idx); +extern template Config::ConfString::Slot *get_slot(size_t idx); +extern template Config::ConfArray::Slot *get_slot(size_t idx); +extern template Config::ConfObject::Slot *get_slot(size_t idx); +extern template Config::ConfUnion::Slot *get_slot(size_t idx); + +template +size_t get_allocated_slot_memory() +{ + return RootBlock::allocated_blocks * SlotConfig::slots_per_block * sizeof(typename ConfigT::Slot); +} + +#if MODULE_DEBUG_AVAILABLE() +struct SlotDebugInfo { + uint16_t first_free_slot; + uint16_t used_slots; + uint16_t last_used_slot; + uint16_t slots_hwm; + uint16_t allocated_slots; +}; + +template +void get_slot_debug_info(SlotDebugInfo *slot_info) +{ + slot_info->first_free_slot = RootBlock::first_free_slot; + slot_info->used_slots = RootBlock::used_slots; + slot_info->last_used_slot = RootBlock::last_used_slot; + slot_info->slots_hwm = RootBlock::slots_hwm; + slot_info->allocated_slots = RootBlock::allocated_blocks * SlotConfig::slots_per_block; +} + +#endif + +#include "main_available_end.h" diff --git a/software/src/main.cpp b/software/src/main.cpp index d935427cd..4276b6b69 100644 --- a/software/src/main.cpp +++ b/software/src/main.cpp @@ -320,8 +320,6 @@ void setup() // However if BUILD_MONITOR_SPEED is not the ROM bootloader's preferred speed, this call will change the speed. Serial.begin(BUILD_MONITOR_SPEED); - config_pre_init(); - std::vector imodules; modules_get_imodules(&imodules); diff --git a/software/src/modules/debug/debug.cpp b/software/src/modules/debug/debug.cpp index 775708983..a025fb2fd 100644 --- a/software/src/modules/debug/debug.cpp +++ b/software/src/modules/debug/debug.cpp @@ -29,10 +29,10 @@ #include #include "event_log_prefix.h" +#include "config/slot_allocator.h" #include "module_dependencies.h" #include "backtrace.h" #include "string_builder.h" -#include "config/private.h" #include "tools/memory.h" #include "tools/fs.h" @@ -74,6 +74,16 @@ static void malloc_failed_hook(size_t size, uint32_t caps, const char *function_ } } +static void (*const slot_debug_info_fns[7])(SlotDebugInfo *slot_info) = { + &get_slot_debug_info, + &get_slot_debug_info, + &get_slot_debug_info, + &get_slot_debug_info, + &get_slot_debug_info, + &get_slot_debug_info, + &get_slot_debug_info, +}; + void Debug::pre_setup() { heap_caps_register_failed_alloc_callback(malloc_failed_hook); @@ -156,6 +166,24 @@ void Debug::pre_setup() {"conf_union_buf_size", Config::Uint32(0)}, }); + state_slots_prototype = Config::Array({}, + Config::get_prototype_uint16_0(), + 5, 5, Config::type_id() + ); + + state_slots = Config::Array({}, + &state_slots_prototype, + 7, 7, Config::type_id() + ); + + for (size_t i = 0; i < 7; i++) { + state_slots.add(); + for (size_t j = 0; j < 5; j++) { + // add() can trigger a move of ConfObjects, so get() must be called inside the loop. + state_slots.get(i)->add(); + } + } + state_hwm_prototype = Config::Object({ {"task_name", Config::Str("", 0, CONFIG_FREERTOS_MAX_TASK_NAME_LEN)}, {"hwm", Config::Uint32(0)}, @@ -206,13 +234,24 @@ void Debug::setup() state_slow.get("min_free_dram")->updateUint(dram_info.minimum_free_bytes); state_slow.get("min_free_psram")->updateUint(psram_info.minimum_free_bytes); - state_slow.get("conf_uint_buf_size")->updateUint(uint_buf_size * sizeof(ConfUintSlot)); - state_slow.get("conf_int_buf_size")->updateUint(int_buf_size * sizeof(ConfIntSlot)); - state_slow.get("conf_float_buf_size")->updateUint(float_buf_size * sizeof(ConfFloatSlot)); - state_slow.get("conf_string_buf_size")->updateUint(string_buf_size * sizeof(ConfStringSlot)); - state_slow.get("conf_array_buf_size")->updateUint(array_buf_size * sizeof(ConfArraySlot)); - state_slow.get("conf_object_buf_size")->updateUint(object_buf_size * sizeof(ConfObjectSlot)); - state_slow.get("conf_union_buf_size")->updateUint(union_buf_size * sizeof(ConfUnionSlot)); + state_slow.get("conf_uint_buf_size" )->updateUint(get_allocated_slot_memory()); + state_slow.get("conf_int_buf_size" )->updateUint(get_allocated_slot_memory()); + state_slow.get("conf_float_buf_size" )->updateUint(get_allocated_slot_memory()); + state_slow.get("conf_string_buf_size")->updateUint(get_allocated_slot_memory()); + state_slow.get("conf_array_buf_size" )->updateUint(get_allocated_slot_memory()); + state_slow.get("conf_object_buf_size")->updateUint(get_allocated_slot_memory()); + state_slow.get("conf_union_buf_size" )->updateUint(get_allocated_slot_memory()); + + for (size_t i = 0; i < 7; i++) { + SlotDebugInfo slot_info; + slot_debug_info_fns[i](&slot_info); + auto arr = state_slots.get(i); + arr->get(0)->updateUint(slot_info.used_slots); + arr->get(1)->updateUint(slot_info.slots_hwm); + arr->get(2)->updateUint(slot_info.allocated_slots); + arr->get(3)->updateUint(slot_info.first_free_slot); + arr->get(4)->updateUint(slot_info.last_used_slot); + } if (dram_info.largest_free_block < 2000) { logger.printfln("Heap full. Largest block is %u bytes.", dram_info.largest_free_block); @@ -327,6 +366,7 @@ void Debug::register_urls() api.addState("debug/state_static", &state_static); api.addState("debug/state_fast", &state_fast); api.addState("debug/state_slow", &state_slow); + api.addState("debug/state_slots", &state_slots); api.addState("debug/state_hwm", &state_hwm); #ifdef DEBUG_FS_ENABLE diff --git a/software/src/modules/debug/debug.h b/software/src/modules/debug/debug.h index c43424788..39bdc1532 100644 --- a/software/src/modules/debug/debug.h +++ b/software/src/modules/debug/debug.h @@ -53,9 +53,11 @@ class Debug final : public IModule ConfigRoot state_static; ConfigRoot state_fast; ConfigRoot state_slow; + ConfigRoot state_slots; ConfigRoot state_hwm; Config state_spi_bus_prototype; + Config state_slots_prototype; Config state_hwm_prototype; std::vector task_handles; diff --git a/software/web/src/modules/debug/api.ts b/software/web/src/modules/debug/api.ts index b81731426..27312ff0b 100644 --- a/software/web/src/modules/debug/api.ts +++ b/software/web/src/modules/debug/api.ts @@ -45,6 +45,10 @@ export interface state_slow { conf_union_buf_size: number; } +type slot_info = number[]; + +export type state_slots = slot_info[]; + interface task_hwm { task_name: string; hwm: number; diff --git a/software/web/src/modules/debug/main.tsx b/software/web/src/modules/debug/main.tsx index 9c9b7eece..93be80923 100644 --- a/software/web/src/modules/debug/main.tsx +++ b/software/web/src/modules/debug/main.tsx @@ -56,8 +56,19 @@ export class Debug extends Component { let state_static = API.get('debug/state_static'); let state_fast = API.get('debug/state_fast'); let state_slow = API.get('debug/state_slow'); + let state_slots = API.get('debug/state_slots'); let state_hwm = API.get('debug/state_hwm'); + const config_type_names = [ + __("debug.content.conf_uint_buf"), + __("debug.content.conf_int_buf"), + __("debug.content.conf_float_buf"), + __("debug.content.conf_string_buf"), + __("debug.content.conf_array_buf"), + __("debug.content.conf_object_buf"), + __("debug.content.conf_union_buf"), + ]; + return ( @@ -159,6 +170,56 @@ export class Debug extends Component { }/> + + + +
+
+

{__("debug.content.slots_used")}

+
+
+

{__("debug.content.slots_hwm")}

+
+
+

{__("debug.content.slots_allocated")}

+
+
+

{__("debug.content.slots_first_free")}

+
+
+

{__("debug.content.slots_last_used")}

+
+
+

{__("debug.content.slots_holes")}

+
+
+
+ + {state_slots.map((slot, idx) => { + return +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ })} +