]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] mem_pool: expose per-callsite entries iteration
authorVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 2 May 2026 14:26:41 +0000 (15:26 +0100)
committerVsevolod Stakhov <vsevolod@rspamd.com>
Sat, 2 May 2026 14:26:41 +0000 (15:26 +0100)
Add rspamd_mempool_entry_stat_t and rspamd_mempool_entries_foreach() so
callers can introspect the per-location mempool registry (suggestion,
preallocated counts, average fragmentation/leftover) without reaching
into mem_pool_internal.h. Used by the upcoming memstat control command.

src/libutil/mem_pool.c
src/libutil/mem_pool.h

index d6362a06a15114e40628bfee90bc9864885162c4..fe11d4911cef418ca014e21cb3b42198a1f35851 100644 (file)
@@ -1026,6 +1026,46 @@ void rspamd_mempool_stat_reset(void)
        }
 }
 
+void rspamd_mempool_entries_foreach(rspamd_mempool_entry_cb cb, void *ud)
+{
+       khiter_t k;
+
+       if (cb == NULL || mempool_entries == NULL) {
+               return;
+       }
+
+       for (k = kh_begin(mempool_entries); k != kh_end(mempool_entries); ++k) {
+               if (!kh_exist(mempool_entries, k)) {
+                       continue;
+               }
+
+               struct rspamd_mempool_entry_point *elt = kh_value(mempool_entries, k);
+               rspamd_mempool_entry_stat_t st;
+               uint64_t sum_frag = 0;
+               uint64_t sum_left = 0;
+               unsigned int valid = 0;
+
+               for (unsigned int i = 0; i < G_N_ELEMENTS(elt->elts); i++) {
+                       if (elt->elts[i].fragmentation != 0 || elt->elts[i].leftover != 0) {
+                               sum_frag += elt->elts[i].fragmentation;
+                               sum_left += elt->elts[i].leftover;
+                               valid++;
+                       }
+               }
+
+               st.src = elt->src;
+               st.cur_suggestion = elt->cur_suggestion;
+               st.cur_elts = elt->cur_elts;
+               st.cur_vars = elt->cur_vars;
+               st.cur_dtors = elt->cur_dtors;
+               st.samples = valid;
+               st.avg_fragmentation = valid ? (uint32_t) (sum_frag / valid) : 0;
+               st.avg_leftover = valid ? (uint32_t) (sum_left / valid) : 0;
+
+               cb(&st, ud);
+       }
+}
+
 gsize rspamd_mempool_suggest_size_(const char *loc)
 {
        return 0;
index 16bd6f143c5c1315615970b2c18d874599158725..61a9c74b03373c792caaf9f31479ef2611afb3de 100644 (file)
@@ -373,6 +373,30 @@ void rspamd_mempool_stat(rspamd_mempool_stat_t *st);
  */
 void rspamd_mempool_stat_reset(void);
 
+/**
+ * Per-callsite mempool statistics, aggregated from the rolling history
+ */
+typedef struct rspamd_mempool_entry_stat_s {
+       const char *src;            /**< callsite location string (file:line)    */
+       uint32_t cur_suggestion;    /**< current suggested pool size              */
+       uint32_t cur_elts;          /**< suggested number of preallocated elts    */
+       uint32_t cur_vars;          /**< suggested number of preallocated vars    */
+       uint32_t cur_dtors;         /**< suggested number of preallocated dtors   */
+       uint32_t avg_fragmentation; /**< average fragmentation across history     */
+       uint32_t avg_leftover;      /**< average leftover across history          */
+       uint32_t samples;           /**< number of valid samples used for averages */
+} rspamd_mempool_entry_stat_t;
+
+typedef void (*rspamd_mempool_entry_cb)(const rspamd_mempool_entry_stat_t *stat,
+                                                                               void *ud);
+
+/**
+ * Iterate over all currently registered mempool callsite entries.
+ * The callback is invoked synchronously for each entry; the rspamd_mempool_entry_stat_t
+ * pointer and its src field are valid only for the duration of the callback.
+ */
+void rspamd_mempool_entries_foreach(rspamd_mempool_entry_cb cb, void *ud);
+
 /**
  * Get optimal pool size based on page size for this system
  * @return size of memory page in system