<p>neels <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/25464">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  laforge: Looks good to me, approved
  daniel: Looks good to me, but someone else must approve
  pespin: Looks good to me, but someone else must approve
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">refactor stat_item: get rid of FIFO and "skipped" error<br><br>Intead of attempting to store all distinct values of a reporting period,<br>just store min, max, last as well as a sum and N of each reporting<br>period.<br><br>This gets rid of error messages like<br><br>  DLSTATS ERROR stat_item.c:285 num_bts:oml_connected: 44 stats values skipped<br><br>while at the same time more accurately reporting the max value for each<br>reporting period. (So far stats_item only reports the max value; keep<br>that part unchanged, as shown in stats_test.c.)<br><br>With the other so far unused values (min, sum), we are ready to also<br>report the minimum value as well as an average value per reporting<br>period in the future, if/when our stats reporter allows for it.<br><br>Store the complete record of the previous reporting period. So far we<br>only compare the 'max' value, but like this we are ready to also see<br>changes in min, last and average value between reporting periods.<br><br>This patch breaks API by removing:<br>- struct members osmo_stats_item.stats_next_id, .last_offs and .values[]<br>- struct osmo_stats_item_value<br>- osmo_stat_item_get_next()<br>- osmo_stat_item_discard()<br>- osmo_stat_item_discard_all()<br>and by making struct osmo_stats_item opaque.<br>In libosmocore, we do have a policy of never breaking API. But since the<br>above should never be accessed by users of the osmo_stats_item API -- or<br>if they are, would no longer yield useful results, we decided to make an<br>exception in this case. The alternative would be to introduce a new<br>osmo_stats_item2 API and maintaining an unused legacy osmo_stats_item<br>forever, but we decided that the effort is not worth it. There are no<br>known users of the removed items.<br><br>Related: SYS#5542<br>Change-Id: I137992a5479fc39bbceb6c6c2af9c227bd33b39b<br>---<br>M TODO-RELEASE<br>M include/osmocom/core/stat_item.h<br>M src/Makefile.am<br>M src/stat_item.c<br>A src/stat_item_internal.h<br>M src/stats.c<br>M src/vty/stats_vty.c<br>M src/vty/utils.c<br>M tests/Makefile.am<br>M tests/stats/stats_test.c<br>M tests/stats/stats_test.err<br>11 files changed, 364 insertions(+), 328 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/TODO-RELEASE b/TODO-RELEASE</span><br><span>index c8966cd..0f202e3 100644</span><br><span>--- a/TODO-RELEASE</span><br><span>+++ b/TODO-RELEASE</span><br><span>@@ -15,4 +15,8 @@</span><br><span> libosmosim  osim_card_{reset,close} New API</span><br><span> libosmocore     struct rate_ctr_group, osmo_stat_item_group_desc ABI breakage due to new struct members</span><br><span> libosmgsm      kdf functions   New API</span><br><span style="color: hsl(0, 100%, 40%);">-libosmocore osmo_stat_item          ABI + API breakage due to new struct members</span><br><span style="color: hsl(120, 100%, 40%);">+libosmocore       osmo_stat_item          API breakage: remove members stats_next_id, last_offs and values[], no users should exist.</span><br><span style="color: hsl(120, 100%, 40%);">+libosmocore osmo_stat_item          API breakage: remove functions osmo_stat_item_get_next(), osmo_stat_item_discard(), osmo_stat_item_discard_all(), no users should exist.</span><br><span style="color: hsl(120, 100%, 40%);">+libosmocore   osmo_stat_item_value    API breakage: struct definition removed, because no known users exist / no users should exist.</span><br><span style="color: hsl(120, 100%, 40%);">+libosmocore     osmo_stat_item          ABI breakage: struct osmo_stat_item made opaque.</span><br><span style="color: hsl(120, 100%, 40%);">+libosmocore   osmo_stat_item          No FIFO buffer of values used anymore, the "skipped values" error is no longer possible.</span><br><span>diff --git a/include/osmocom/core/stat_item.h b/include/osmocom/core/stat_item.h</span><br><span>index e600ecd..7f6857c 100644</span><br><span>--- a/include/osmocom/core/stat_item.h</span><br><span>+++ b/include/osmocom/core/stat_item.h</span><br><span>@@ -14,36 +14,16 @@</span><br><span> #define OSMO_STAT_ITEM_NOVALUE_ID 0</span><br><span> #define OSMO_STAT_ITEM_NO_UNIT NULL</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! Individual entry in value FIFO */</span><br><span style="color: hsl(0, 100%, 40%);">-struct osmo_stat_item_value {</span><br><span style="color: hsl(0, 100%, 40%);">-       int32_t id;             /*!< identifier of value */</span><br><span style="color: hsl(0, 100%, 40%);">-  int32_t value;          /*!< actual value */</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! data we keep for each actual item */</span><br><span style="color: hsl(0, 100%, 40%);">-struct osmo_stat_item {</span><br><span style="color: hsl(0, 100%, 40%);">-        /*! back-reference to the item description */</span><br><span style="color: hsl(0, 100%, 40%);">-   const struct osmo_stat_item_desc *desc;</span><br><span style="color: hsl(0, 100%, 40%);">- /* internal use by stats API (stats.c): the id of the next value to</span><br><span style="color: hsl(0, 100%, 40%);">-      * be read from the FIFO. If accessing osmo_stat_item directly, without</span><br><span style="color: hsl(0, 100%, 40%);">-  * the stats API, store this value elsewhere. */</span><br><span style="color: hsl(0, 100%, 40%);">-        int32_t stats_next_id;</span><br><span style="color: hsl(0, 100%, 40%);">-  /* internal use by stats API: indicate if the last value sent to</span><br><span style="color: hsl(0, 100%, 40%);">-         * reporters was actually the last value in the FIFO. This may not be</span><br><span style="color: hsl(0, 100%, 40%);">-    * the case, as always a max of 1 or more values gets sent (OS#5215) */</span><br><span style="color: hsl(0, 100%, 40%);">- bool stats_last_sent_was_max;</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! the index of the last value written to the FIFO */</span><br><span style="color: hsl(0, 100%, 40%);">-  int16_t last_offs;</span><br><span style="color: hsl(0, 100%, 40%);">-      /*! value FIFO */</span><br><span style="color: hsl(0, 100%, 40%);">-       struct osmo_stat_item_value values[0];</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(120, 100%, 40%);">+/*! data we keep for each actual item. Access via API functions only.</span><br><span style="color: hsl(120, 100%, 40%);">+ * (This struct was made opaque after libosmocore 1.5.1)*/</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_stat_item;</span><br><span> </span><br><span> /*! Statistics item description */</span><br><span> struct osmo_stat_item_desc {</span><br><span>        const char *name;       /*!< name of the item */</span><br><span>  const char *description;/*!< description of the item */</span><br><span>   const char *unit;       /*!< unit of a value */</span><br><span style="color: hsl(0, 100%, 40%);">-      unsigned int num_values;/*!< number of values to store in FIFO */</span><br><span style="color: hsl(120, 100%, 40%);">+  unsigned int num_values;/*!< DEPRECATED, this value is ignored after libosmocore version 1.5.1 */</span><br><span>         int32_t default_value;  /*!< default value */</span><br><span> };</span><br><span> </span><br><span>@@ -102,15 +82,9 @@</span><br><span> const struct osmo_stat_item *osmo_stat_item_get_by_name(</span><br><span>         const struct osmo_stat_item_group *statg, const char *name);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_id, int32_t *value);</span><br><span style="color: hsl(120, 100%, 40%);">+int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! Get the last (freshest) value */</span><br><span style="color: hsl(0, 100%, 40%);">-static int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *next_id);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_stat_item_discard_all(int32_t *next_id)</span><br><span style="color: hsl(0, 100%, 40%);">-        OSMO_DEPRECATED("Use osmo_stat_item_discard with item-specific next_id instead");</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_stat_item_flush(struct osmo_stat_item *item);</span><br><span> </span><br><span> typedef int (*osmo_stat_item_handler_t)(</span><br><span>         struct osmo_stat_item_group *, struct osmo_stat_item *, void *);</span><br><span>@@ -122,12 +96,20 @@</span><br><span> </span><br><span> int osmo_stat_item_for_each_group(osmo_stat_item_group_handler_t handle_group, void *data);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static inline int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- return item->values[item->last_offs].value;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> void osmo_stat_item_reset(struct osmo_stat_item *item);</span><br><span> void osmo_stat_item_group_reset(struct osmo_stat_item_group *statg);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+const struct osmo_stat_item_desc *osmo_stat_item_get_desc(struct osmo_stat_item *item);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* DEPRECATION: up until libosmocore 1.5.1, this API also defined</span><br><span style="color: hsl(120, 100%, 40%);">+ * - struct osmo_stat_item_value</span><br><span style="color: hsl(120, 100%, 40%);">+ * - osmo_stat_item_get_next()</span><br><span style="color: hsl(120, 100%, 40%);">+ * - osmo_stat_item_discard()</span><br><span style="color: hsl(120, 100%, 40%);">+ * - osmo_stat_item_discard_all()</span><br><span style="color: hsl(120, 100%, 40%);">+ * Despite our principle of never breaking API compatibility, we have decided to remove these, because there are no</span><br><span style="color: hsl(120, 100%, 40%);">+ * known users. These items were never practically used/usable outside of libosmocore since the generic stats reporter</span><br><span style="color: hsl(120, 100%, 40%);">+ * (stats.c) was introduced.</span><br><span style="color: hsl(120, 100%, 40%);">+ * We also decided to make struct osmo_stat_item opaque to allow future changes of the struct without API breakage.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} */</span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index 3c589e6..328d2c7 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -58,7 +58,13 @@</span><br><span> endif</span><br><span> </span><br><span> BUILT_SOURCES = crc8gen.c crc16gen.c crc32gen.c crc64gen.c</span><br><span style="color: hsl(0, 100%, 40%);">-EXTRA_DIST = conv_acc_sse_impl.h conv_acc_neon_impl.h crcXXgen.c.tpl</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+EXTRA_DIST = \</span><br><span style="color: hsl(120, 100%, 40%);">+        conv_acc_sse_impl.h \</span><br><span style="color: hsl(120, 100%, 40%);">+ conv_acc_neon_impl.h \</span><br><span style="color: hsl(120, 100%, 40%);">+        crcXXgen.c.tpl \</span><br><span style="color: hsl(120, 100%, 40%);">+      stat_item_internal.h \</span><br><span style="color: hsl(120, 100%, 40%);">+        $(NULL)</span><br><span> </span><br><span> libosmocore_la_LDFLAGS = -version-info $(LIBVERSION) -no-undefined</span><br><span> </span><br><span>diff --git a/src/stat_item.c b/src/stat_item.c</span><br><span>index 1788746..59f5935 100644</span><br><span>--- a/src/stat_item.c</span><br><span>+++ b/src/stat_item.c</span><br><span>@@ -54,20 +54,105 @@</span><br><span>  */</span><br><span> </span><br><span> /* Struct overview:</span><br><span style="color: hsl(0, 100%, 40%);">- * Each osmo_stat_item is attached to an osmo_stat_item_group, which is</span><br><span style="color: hsl(0, 100%, 40%);">- * attached to the global osmo_stat_item_groups list.</span><br><span>  *</span><br><span style="color: hsl(0, 100%, 40%);">- *                       osmo_stat_item_groups</span><br><span style="color: hsl(0, 100%, 40%);">- *                           /           \</span><br><span style="color: hsl(0, 100%, 40%);">- *                        group1        group2</span><br><span style="color: hsl(0, 100%, 40%);">- *                       /      \</span><br><span style="color: hsl(0, 100%, 40%);">- *                    item1    item2</span><br><span style="color: hsl(0, 100%, 40%);">- *                      |</span><br><span style="color: hsl(0, 100%, 40%);">- *                   values</span><br><span style="color: hsl(0, 100%, 40%);">- *                  /      \</span><br><span style="color: hsl(0, 100%, 40%);">- *                 1        2</span><br><span style="color: hsl(0, 100%, 40%);">- *                 |-id     |-id</span><br><span style="color: hsl(0, 100%, 40%);">- *                 '-value  '-value</span><br><span style="color: hsl(120, 100%, 40%);">+ * Group and item descriptions:</span><br><span style="color: hsl(120, 100%, 40%);">+ * Each group description exists once as osmo_stat_item_group_desc,</span><br><span style="color: hsl(120, 100%, 40%);">+ * each such group description lists N osmo_stat_item_desc, i.e. describes N stat items.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Actual stats:</span><br><span style="color: hsl(120, 100%, 40%);">+ * The global osmo_stat_item_groups llist contains all group instances, each points at a group description.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This list mixes all types of groups in a single llist, where each instance points at its group desc and has an index.</span><br><span style="color: hsl(120, 100%, 40%);">+ * There are one or more instances of each group, each storing stats for a distinct object (for example, one description</span><br><span style="color: hsl(120, 100%, 40%);">+ * for a BTS group, and any number of BTS instances with independent stats). A group is identified by a group index nr</span><br><span style="color: hsl(120, 100%, 40%);">+ * and possibly also a given name for that particular index (e.g. in osmo-mgw, a group instance is named</span><br><span style="color: hsl(120, 100%, 40%);">+ * "virtual-trunk-0" and can be looked up by that name instead of its more or less arbitrary group index number).</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Each group instance contains one osmo_stat_item instance per global stat item description.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Each osmo_stat_item keeps track of the values for the current reporting period (min, last, max, sum, n),</span><br><span style="color: hsl(120, 100%, 40%);">+ * and also stores the set of values reported at the end of the previous reporting period.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  const osmo_stat_item_group_desc foo</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                   +-- group_name_prefix = "foo"</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                   +-- item_desc[] (array of osmo_stat_item_desc)</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        +-- osmo_stat_item_desc bar</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |    +-- name = "bar"</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |    +-- description</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |    +-- unit</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |    +-- default_value</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        +-- osmo_stat_item_desc: baz</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                             +-- ...</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  const osmo_stat_item_group_desc moo</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                   +-- group_name_prefix = "moo"</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                   +-- item_desc[]</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        +-- osmo_stat_item_desc goo</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |    +-- name = "goo"</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |    +-- description</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |    +-- unit</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |    +-- default_value</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        |</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                        +-- osmo_stat_item_desc: loo</span><br><span style="color: hsl(120, 100%, 40%);">+ *                                             +-- ...</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ *  osmo_stat_item_groups (llist of osmo_stat_item_group)</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   +-- group: foo[0]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- desc --> osmo_stat_item_group_desc foo</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- idx = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- name = NULL (no given name for this group instance)</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- items[]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |        [0] --> osmo_stat_item instance for "bar"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- desc --> osmo_stat_item_desc bar (see above)</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- value.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- reported.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |        [1] --> osmo_stat_item instance for "baz"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- desc --> osmo_stat_item_desc baz</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- value.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- reported.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         .</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         :</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   +-- group: foo[1]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- desc --> osmo_stat_item_group_desc foo</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- idx = 1</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- name = "special-foo" (instance can be looked up by this index-name)</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- items[]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |        [0] --> osmo_stat_item instance for "bar"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- desc --> osmo_stat_item_desc bar</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- value.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- reported.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |        [1] --> osmo_stat_item instance for "baz"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- desc --> osmo_stat_item_desc baz</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- value.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- reported.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         .</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         :</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   +-- group: moo[0]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- desc --> osmo_stat_item_group_desc moo</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- idx = 0</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- name = NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |    +-- items[]</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |        [0] --> osmo_stat_item instance for "goo"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- desc --> osmo_stat_item_desc goo</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- value.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- reported.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |        [1] --> osmo_stat_item instance for "loo"</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- desc --> osmo_stat_item_desc loo</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- value.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         |       +-- reported.{min, last, max, n, sum}</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         .</span><br><span style="color: hsl(120, 100%, 40%);">+ *   |         :</span><br><span style="color: hsl(120, 100%, 40%);">+ *   .</span><br><span style="color: hsl(120, 100%, 40%);">+ *   :</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span>  */</span><br><span> </span><br><span> #include <stdint.h></span><br><span>@@ -80,6 +165,8 @@</span><br><span> #include <osmocom/core/timer.h></span><br><span> #include <osmocom/core/stat_item.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <stat_item_internal.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! global list of stat_item groups */</span><br><span> static LLIST_HEAD(osmo_stat_item_groups);</span><br><span> </span><br><span>@@ -98,9 +185,8 @@</span><br><span>                                             unsigned int idx)</span><br><span> {</span><br><span>   unsigned int group_size;</span><br><span style="color: hsl(0, 100%, 40%);">-        unsigned long items_size = 0;</span><br><span>        unsigned int item_idx;</span><br><span style="color: hsl(0, 100%, 40%);">-  void *items;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_stat_item *items;</span><br><span> </span><br><span>    struct osmo_stat_item_group *group;</span><br><span> </span><br><span>@@ -117,46 +203,25 @@</span><br><span>      group->desc = group_desc;</span><br><span>         group->idx = idx;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        /* Get combined size of all items */</span><br><span style="color: hsl(120, 100%, 40%);">+  items = talloc_array(group, struct osmo_stat_item, group_desc->num_items);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(items);</span><br><span>  for (item_idx = 0; item_idx < group_desc->num_items; item_idx++) {</span><br><span style="color: hsl(0, 100%, 40%);">-                unsigned int size;</span><br><span style="color: hsl(0, 100%, 40%);">-              size = sizeof(struct osmo_stat_item) +</span><br><span style="color: hsl(0, 100%, 40%);">-                  sizeof(struct osmo_stat_item_value) *</span><br><span style="color: hsl(0, 100%, 40%);">-                   group_desc->item_desc[item_idx].num_values;</span><br><span style="color: hsl(0, 100%, 40%);">-          /* Align to pointer size */</span><br><span style="color: hsl(0, 100%, 40%);">-             size = (size + sizeof(void *) - 1) & ~(sizeof(void *) - 1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-         /* Store offsets into the item array */</span><br><span style="color: hsl(0, 100%, 40%);">-         group->items[item_idx] = (void *)items_size;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-         items_size += size;</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       items = talloc_zero_size(group, items_size);</span><br><span style="color: hsl(0, 100%, 40%);">-    if (!items) {</span><br><span style="color: hsl(0, 100%, 40%);">-           talloc_free(group);</span><br><span style="color: hsl(0, 100%, 40%);">-             return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* Update item pointers */</span><br><span style="color: hsl(0, 100%, 40%);">-      for (item_idx = 0; item_idx < group_desc->num_items; item_idx++) {</span><br><span style="color: hsl(0, 100%, 40%);">-                struct osmo_stat_item *item = (struct osmo_stat_item *)</span><br><span style="color: hsl(0, 100%, 40%);">-                 ((uint8_t *)items + (unsigned long)group->items[item_idx]);</span><br><span style="color: hsl(0, 100%, 40%);">-          unsigned int i;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+               struct osmo_stat_item *item = &items[item_idx];</span><br><span style="color: hsl(120, 100%, 40%);">+           const struct osmo_stat_item_desc *item_desc = &group_desc->item_desc[item_idx];</span><br><span>               group->items[item_idx] = item;</span><br><span style="color: hsl(0, 100%, 40%);">-               item->last_offs = group_desc->item_desc[item_idx].num_values - 1;</span><br><span style="color: hsl(0, 100%, 40%);">-         item->stats_next_id = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-             item->desc = &group_desc->item_desc[item_idx];</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                for (i = 0; i <= item->last_offs; i++) {</span><br><span style="color: hsl(0, 100%, 40%);">-                  item->values[i].value = group_desc->item_desc[item_idx].default_value;</span><br><span style="color: hsl(0, 100%, 40%);">-                    item->values[i].id = OSMO_STAT_ITEM_NOVALUE_ID;</span><br><span style="color: hsl(0, 100%, 40%);">-              }</span><br><span style="color: hsl(120, 100%, 40%);">+             *item = (struct osmo_stat_item){</span><br><span style="color: hsl(120, 100%, 40%);">+                      .desc = item_desc,</span><br><span style="color: hsl(120, 100%, 40%);">+                    .value = {</span><br><span style="color: hsl(120, 100%, 40%);">+                            .n = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+                               .last = item_desc->default_value,</span><br><span style="color: hsl(120, 100%, 40%);">+                          .min = item_desc->default_value,</span><br><span style="color: hsl(120, 100%, 40%);">+                           .max = item_desc->default_value,</span><br><span style="color: hsl(120, 100%, 40%);">+                           .sum = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+                     },</span><br><span style="color: hsl(120, 100%, 40%);">+            };</span><br><span>   }</span><br><span> </span><br><span>        llist_add(&group->list, &osmo_stat_item_groups);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>  return group;</span><br><span> }</span><br><span> </span><br><span>@@ -195,8 +260,7 @@</span><br><span>  */</span><br><span> void osmo_stat_item_inc(struct osmo_stat_item *item, int32_t value)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- int32_t oldvalue = item->values[item->last_offs].value;</span><br><span style="color: hsl(0, 100%, 40%);">-   osmo_stat_item_set(item, oldvalue + value);</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_stat_item_set(item, item->value.last + value);</span><br><span> }</span><br><span> </span><br><span> /*! Descrease the stat_item to the given value.</span><br><span>@@ -207,8 +271,7 @@</span><br><span>  */</span><br><span> void osmo_stat_item_dec(struct osmo_stat_item *item, int32_t value)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-     int32_t oldvalue = item->values[item->last_offs].value;</span><br><span style="color: hsl(0, 100%, 40%);">-   osmo_stat_item_set(item, oldvalue - value);</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_stat_item_set(item, item->value.last - value);</span><br><span> }</span><br><span> </span><br><span> /*! Set the a given stat_item to the given value.</span><br><span>@@ -219,89 +282,37 @@</span><br><span>  */</span><br><span> void osmo_stat_item_set(struct osmo_stat_item *item, int32_t value)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- int32_t id = item->values[item->last_offs].id + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-        if (id == OSMO_STAT_ITEM_NOVALUE_ID)</span><br><span style="color: hsl(0, 100%, 40%);">-            id++;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   item->last_offs += 1;</span><br><span style="color: hsl(0, 100%, 40%);">-        if (item->last_offs >= item->desc->num_values)</span><br><span style="color: hsl(0, 100%, 40%);">-              item->last_offs = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- item->values[item->last_offs].value = value;</span><br><span style="color: hsl(0, 100%, 40%);">-      item->values[item->last_offs].id    = id;</span><br><span style="color: hsl(120, 100%, 40%);">+       item->value.last = value;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (item->value.n == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+          /* No values recorded yet, clamp min and max to this first value. */</span><br><span style="color: hsl(120, 100%, 40%);">+          item->value.min = item->value.max = value;</span><br><span style="color: hsl(120, 100%, 40%);">+              /* Overwrite any cruft remaining in value.sum */</span><br><span style="color: hsl(120, 100%, 40%);">+              item->value.sum = value;</span><br><span style="color: hsl(120, 100%, 40%);">+           item->value.n = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              item->value.min = OSMO_MIN(item->value.min, value);</span><br><span style="color: hsl(120, 100%, 40%);">+             item->value.max = OSMO_MAX(item->value.max, value);</span><br><span style="color: hsl(120, 100%, 40%);">+             item->value.sum += value;</span><br><span style="color: hsl(120, 100%, 40%);">+          item->value.n++;</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/*! Retrieve the next value from the osmo_stat_item object.</span><br><span style="color: hsl(0, 100%, 40%);">- * If a new value has been set, it is returned. The next_id is used to decide</span><br><span style="color: hsl(0, 100%, 40%);">- * which value to return.</span><br><span style="color: hsl(0, 100%, 40%);">- * On success, *next_id is updated to refer to the next unread value. If</span><br><span style="color: hsl(0, 100%, 40%);">- * values have been missed due to FIFO overflow, *next_id is incremented by</span><br><span style="color: hsl(0, 100%, 40%);">- * (1 + num_lost).</span><br><span style="color: hsl(0, 100%, 40%);">- * This way, the osmo_stat_item object can be kept stateless from the reader's</span><br><span style="color: hsl(0, 100%, 40%);">- * perspective and therefore be used by several backends simultaneously.</span><br><span style="color: hsl(0, 100%, 40%);">- *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param val     the osmo_stat_item object</span><br><span style="color: hsl(0, 100%, 40%);">- * \param next_id identifies the next value to be read</span><br><span style="color: hsl(0, 100%, 40%);">- * \param value   a pointer to store the value</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns  the increment of the index (0: no value has been read,</span><br><span style="color: hsl(0, 100%, 40%);">- *           1: one value has been taken,</span><br><span style="color: hsl(0, 100%, 40%);">- *           (1+n): n values have been skipped, one has been taken)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Indicate that a reporting period has elapsed, and prepare the stat item for a new period of collecting min/max/avg.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param item  Stat item to flush.</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_stat_item_get_next(const struct osmo_stat_item *item, int32_t *next_id,</span><br><span style="color: hsl(0, 100%, 40%);">- int32_t *value)</span><br><span style="color: hsl(120, 100%, 40%);">+void osmo_stat_item_flush(struct osmo_stat_item *item)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-    const struct osmo_stat_item_value *next_value;</span><br><span style="color: hsl(0, 100%, 40%);">-  const struct osmo_stat_item_value *item_value = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-   int id_delta;</span><br><span style="color: hsl(0, 100%, 40%);">-   int next_offs;</span><br><span style="color: hsl(120, 100%, 40%);">+        item->reported = item->value;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- next_offs = item->last_offs;</span><br><span style="color: hsl(0, 100%, 40%);">- next_value = &item->values[next_offs];</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Indicate a new reporting period: no values have been received, but the previous value.last remains unchanged</span><br><span style="color: hsl(120, 100%, 40%);">+        * for the case that an entire period elapses without a new value appearing. */</span><br><span style="color: hsl(120, 100%, 40%);">+       item->value.n = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ item->value.sum = 0;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     while (next_value->id - *next_id >= 0 &&</span><br><span style="color: hsl(0, 100%, 40%);">-          next_value->id != OSMO_STAT_ITEM_NOVALUE_ID)</span><br><span style="color: hsl(0, 100%, 40%);">- {</span><br><span style="color: hsl(0, 100%, 40%);">-               item_value = next_value;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                next_offs -= 1;</span><br><span style="color: hsl(0, 100%, 40%);">-         if (next_offs < 0)</span><br><span style="color: hsl(0, 100%, 40%);">-                   next_offs = item->desc->num_values - 1;</span><br><span style="color: hsl(0, 100%, 40%);">-           if (next_offs == item->last_offs)</span><br><span style="color: hsl(0, 100%, 40%);">-                    break;</span><br><span style="color: hsl(0, 100%, 40%);">-          next_value = &item->values[next_offs];</span><br><span style="color: hsl(0, 100%, 40%);">-   }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!item_value)</span><br><span style="color: hsl(0, 100%, 40%);">-                /* All items have been read */</span><br><span style="color: hsl(0, 100%, 40%);">-          return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       *value = item_value->value;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  id_delta = item_value->id + 1 - *next_id;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    *next_id = item_value->id + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (id_delta > 1) {</span><br><span style="color: hsl(0, 100%, 40%);">-          LOGP(DLSTATS, LOGL_ERROR, "%s: %d stats values skipped\n", item->desc->name, id_delta - 1);</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return id_delta;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Skip/discard all values of this item and update \a next_id accordingly */</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_stat_item_discard(const struct osmo_stat_item *item, int32_t *next_id)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  int discarded = item->values[item->last_offs].id + 1 - *next_id;</span><br><span style="color: hsl(0, 100%, 40%);">-  *next_id = item->values[item->last_offs].id + 1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  return discarded;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Skip all values of all items and update \a next_id accordingly */</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_stat_item_discard_all(int32_t *next_id)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        LOGP(DLSTATS, LOGL_ERROR, "osmo_stat_item_discard_all is deprecated (no-op)\n");</span><br><span style="color: hsl(0, 100%, 40%);">-      return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Also for the case that an entire period elapses without any osmo_stat_item_set(), put the min and max to the</span><br><span style="color: hsl(120, 100%, 40%);">+        * last value. As soon as one osmo_stat_item_set() occurs, these are both set to the new value (when n is still</span><br><span style="color: hsl(120, 100%, 40%);">+        * zero from above). */</span><br><span style="color: hsl(120, 100%, 40%);">+       item->value.min = item->value.max = item->value.last;</span><br><span> }</span><br><span> </span><br><span> /*! Initialize the stat item module. Call this once from your program.</span><br><span>@@ -415,21 +426,20 @@</span><br><span>    return rc;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Get the last (freshest) value. */</span><br><span style="color: hsl(120, 100%, 40%);">+int32_t osmo_stat_item_get_last(const struct osmo_stat_item *item)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     return item->value.last;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span> </span><br><span> /*! Remove all values of a stat item</span><br><span>  *  \param[in] item stat item to reset</span><br><span>  */</span><br><span> void osmo_stat_item_reset(struct osmo_stat_item *item)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-      unsigned int i;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- item->last_offs = item->desc->num_values - 1;</span><br><span style="color: hsl(0, 100%, 40%);">-  item->stats_next_id = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     for (i = 0; i <= item->last_offs; i++) {</span><br><span style="color: hsl(0, 100%, 40%);">-          item->values[i].value = item->desc->default_value;</span><br><span style="color: hsl(0, 100%, 40%);">-             item->values[i].id = OSMO_STAT_ITEM_NOVALUE_ID;</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(120, 100%, 40%);">+     item->value.sum = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+       item->value.n = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ item->value.last = item->value.min = item->value.max = item->desc->default_value;</span><br><span> }</span><br><span> </span><br><span> /*! Reset all osmo stat items in a group</span><br><span>@@ -444,4 +454,11 @@</span><br><span>                 osmo_stat_item_reset(item);</span><br><span>  }</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return the description for an osmo_stat_item. */</span><br><span style="color: hsl(120, 100%, 40%);">+const struct osmo_stat_item_desc *osmo_stat_item_get_desc(struct osmo_stat_item *item)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     return item->desc;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} */</span><br><span>diff --git a/src/stat_item_internal.h b/src/stat_item_internal.h</span><br><span>new file mode 100644</span><br><span>index 0000000..9ede8c4</span><br><span>--- /dev/null</span><br><span>+++ b/src/stat_item_internal.h</span><br><span>@@ -0,0 +1,35 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file stat_item_internal.h</span><br><span style="color: hsl(120, 100%, 40%);">+ * internal definitions for the osmo_stat_item API */</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \addtogroup osmo_stat_item</span><br><span style="color: hsl(120, 100%, 40%);">+ *  @{</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_stat_item_period {</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! Number of osmo_stat_item_set() that occurred during the reporting period, zero if none. */</span><br><span style="color: hsl(120, 100%, 40%);">+        uint32_t n;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! Smallest value seen in a reporting period. */</span><br><span style="color: hsl(120, 100%, 40%);">+     int32_t min;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! Most recent value passed to osmo_stat_item_set(), or the item->desc->default_value if none. */</span><br><span style="color: hsl(120, 100%, 40%);">+      int32_t last;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Largest value seen in a reporting period. */</span><br><span style="color: hsl(120, 100%, 40%);">+      int32_t max;</span><br><span style="color: hsl(120, 100%, 40%);">+  /*! Sum of all values passed to osmo_stat_item_set() in the reporting period. */</span><br><span style="color: hsl(120, 100%, 40%);">+      int64_t sum;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! data we keep for each actual item */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_stat_item {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! back-reference to the item description */</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct osmo_stat_item_desc *desc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! Current reporting period / current value. */</span><br><span style="color: hsl(120, 100%, 40%);">+      struct osmo_stat_item_period value;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The results of the previous reporting period. According to these, the stats reporter decides whether to</span><br><span style="color: hsl(120, 100%, 40%);">+    * re-send values or omit an unchanged value from a report. */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_stat_item_period reported;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/src/stats.c b/src/stats.c</span><br><span>index f06515d..5dac242 100644</span><br><span>--- a/src/stats.c</span><br><span>+++ b/src/stats.c</span><br><span>@@ -101,6 +101,8 @@</span><br><span> #define TRACE_ENABLED(probe) (0)</span><br><span> #endif /* HAVE_SYSTEMTAP */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <stat_item_internal.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define STATS_DEFAULT_INTERVAL 5 /* secs */</span><br><span> #define STATS_DEFAULT_BUFLEN 256</span><br><span> </span><br><span>@@ -235,28 +237,11 @@</span><br><span>        talloc_free(srep);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static int osmo_stats_discard_item(struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    return osmo_stat_item_discard(item, &item->stats_next_id);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int osmo_stats_discard_group(struct osmo_stat_item_group *statg, void *sctx_)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- return osmo_stat_item_for_each_item(statg, &osmo_stats_discard_item, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int osmo_stats_discard_all()</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    return osmo_stat_item_for_each_group(&osmo_stats_discard_group, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Initilize the stats reporting module; call this once in your program</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Initialize the stats reporting module; call this once in your program.</span><br><span>  *  \param[in] ctx Talloc context from which stats related memory is allocated */</span><br><span> void osmo_stats_init(void *ctx)</span><br><span> {</span><br><span>         osmo_stats_ctx = ctx;</span><br><span style="color: hsl(0, 100%, 40%);">-   osmo_stats_discard_all();</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    is_initialised = 1;</span><br><span>  start_timer();</span><br><span> }</span><br><span>@@ -708,43 +693,33 @@</span><br><span>  struct osmo_stat_item_group *statg, struct osmo_stat_item *item, void *sctx_)</span><br><span> {</span><br><span>   struct osmo_stats_reporter *srep;</span><br><span style="color: hsl(0, 100%, 40%);">-       int32_t value;</span><br><span style="color: hsl(0, 100%, 40%);">-  bool have_value;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        have_value = osmo_stat_item_get_next(item, &item->stats_next_id, &value) > 0;</span><br><span style="color: hsl(0, 100%, 40%);">-     if (!have_value) {</span><br><span style="color: hsl(0, 100%, 40%);">-              /* Send the last value in case a flush is requested */</span><br><span style="color: hsl(0, 100%, 40%);">-          value = osmo_stat_item_get_last(item);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-          /* Also send it in case a different max value was sent</span><br><span style="color: hsl(0, 100%, 40%);">-           * previously (OS#5215) */</span><br><span style="color: hsl(0, 100%, 40%);">-              if (!item->stats_last_sent_was_max)</span><br><span style="color: hsl(0, 100%, 40%);">-                  have_value = 1;</span><br><span style="color: hsl(0, 100%, 40%);">- } else {</span><br><span style="color: hsl(0, 100%, 40%);">-                int32_t next_val;</span><br><span style="color: hsl(0, 100%, 40%);">-               /* If we have multiple values only send the max */</span><br><span style="color: hsl(0, 100%, 40%);">-              while (osmo_stat_item_get_next(item, &item->stats_next_id, &next_val) > 0)</span><br><span style="color: hsl(0, 100%, 40%);">-                        value = OSMO_MAX(value, next_val);</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       item->stats_last_sent_was_max = (osmo_stat_item_get_last(item) == value);</span><br><span style="color: hsl(120, 100%, 40%);">+  int32_t prev_reported_value = item->reported.max;</span><br><span style="color: hsl(120, 100%, 40%);">+  int32_t new_value = item->value.max;</span><br><span> </span><br><span>  llist_for_each_entry(srep, &osmo_stats_reporter_list, list) {</span><br><span>            if (!srep->running)</span><br><span>                       continue;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           if (!have_value && !srep->force_single_flush)</span><br><span style="color: hsl(120, 100%, 40%);">+              /* If no new stat values have been set in the current reporting period, skip resending the value.</span><br><span style="color: hsl(120, 100%, 40%);">+              * However, if the previously sent value is not the same as the current value, then do send the changed</span><br><span style="color: hsl(120, 100%, 40%);">+                * value (while n == 0, the last value from the previous reporting period is in item->value.max == .last</span><br><span style="color: hsl(120, 100%, 40%);">+            * == .min, which was put in new_value above).</span><br><span style="color: hsl(120, 100%, 40%);">+                 * Also if the stats reporter is set to resend all values, also do resend the current value regardless</span><br><span style="color: hsl(120, 100%, 40%);">+                 * of repetitions.</span><br><span style="color: hsl(120, 100%, 40%);">+             */</span><br><span style="color: hsl(120, 100%, 40%);">+           if ((!item->value.n && new_value == prev_reported_value)</span><br><span style="color: hsl(120, 100%, 40%);">+               && !srep->force_single_flush)</span><br><span>                         continue;</span><br><span> </span><br><span>                if (!osmo_stats_reporter_check_config(srep,</span><br><span>                          statg->idx, statg->desc->class_id))</span><br><span>                         continue;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           osmo_stats_reporter_send_item(srep, statg,</span><br><span style="color: hsl(0, 100%, 40%);">-                      item->desc, value);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_stats_reporter_send_item(srep, statg, item->desc, new_value);</span><br><span>        }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_flush(item);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -818,7 +793,6 @@</span><br><span>     osmo_stat_item_for_each_group(osmo_stat_item_group_handler, NULL);</span><br><span> </span><br><span>       /* global actions */</span><br><span style="color: hsl(0, 100%, 40%);">-    osmo_stats_discard_all();</span><br><span>    flush_all_reporters();</span><br><span>       TRACE(LIBOSMOCORE_STATS_DONE());</span><br><span> </span><br><span>diff --git a/src/vty/stats_vty.c b/src/vty/stats_vty.c</span><br><span>index c9ae0fb..17e7190 100644</span><br><span>--- a/src/vty/stats_vty.c</span><br><span>+++ b/src/vty/stats_vty.c</span><br><span>@@ -475,10 +475,11 @@</span><br><span> {</span><br><span>   struct vty *vty = sctx_;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    char *name = osmo_asciidoc_escape(item->desc->name);</span><br><span style="color: hsl(0, 100%, 40%);">-      char *description = osmo_asciidoc_escape(item->desc->description);</span><br><span style="color: hsl(120, 100%, 40%);">+      const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item);</span><br><span style="color: hsl(120, 100%, 40%);">+       char *name = osmo_asciidoc_escape(desc->name);</span><br><span style="color: hsl(120, 100%, 40%);">+     char *description = osmo_asciidoc_escape(desc->description);</span><br><span>      char *group_name_prefix = osmo_asciidoc_escape(statg->desc->group_name_prefix);</span><br><span style="color: hsl(0, 100%, 40%);">-   char *unit = osmo_asciidoc_escape(item->desc->unit);</span><br><span style="color: hsl(120, 100%, 40%);">+    char *unit = osmo_asciidoc_escape(desc->unit);</span><br><span> </span><br><span>        /* | Name | Reference | Description | Unit | */</span><br><span>      vty_out(vty, "| %s | <<%s_%s>> | %s | %s%s",</span><br><span>diff --git a/src/vty/utils.c b/src/vty/utils.c</span><br><span>index 1137f2b..4501c66 100644</span><br><span>--- a/src/vty/utils.c</span><br><span>+++ b/src/vty/utils.c</span><br><span>@@ -247,12 +247,11 @@</span><br><span> {</span><br><span>     struct vty_out_context *vctx = vctx_;</span><br><span>        struct vty *vty = vctx->vty;</span><br><span style="color: hsl(0, 100%, 40%);">- const char *unit =</span><br><span style="color: hsl(0, 100%, 40%);">-              item->desc->unit != OSMO_STAT_ITEM_NO_UNIT ?</span><br><span style="color: hsl(0, 100%, 40%);">-              item->desc->unit : "";</span><br><span style="color: hsl(120, 100%, 40%);">+        const struct osmo_stat_item_desc *desc = osmo_stat_item_get_desc(item);</span><br><span style="color: hsl(120, 100%, 40%);">+       const char *unit = (desc->unit != OSMO_STAT_ITEM_NO_UNIT) ? desc->unit : "";</span><br><span> </span><br><span>     vty_out(vty, " %s%s: %8" PRIi32 " %s%s",</span><br><span style="color: hsl(0, 100%, 40%);">-            vctx->prefix, item->desc->description,</span><br><span style="color: hsl(120, 100%, 40%);">+               vctx->prefix, desc->description,</span><br><span>               osmo_stat_item_get_last(item),</span><br><span>               unit, VTY_NEWLINE);</span><br><span> </span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index 22591fb..0880561 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -87,6 +87,7 @@</span><br><span> </span><br><span> stats_stats_test_SOURCES = stats/stats_test.c</span><br><span> stats_stats_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la</span><br><span style="color: hsl(120, 100%, 40%);">+stats_stats_test_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src</span><br><span> </span><br><span> a5_a5_test_SOURCES = a5/a5_test.c</span><br><span> a5_a5_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libgsmint.la</span><br><span>diff --git a/tests/stats/stats_test.c b/tests/stats/stats_test.c</span><br><span>index 9489e60..dea2bf7 100644</span><br><span>--- a/tests/stats/stats_test.c</span><br><span>+++ b/tests/stats/stats_test.c</span><br><span>@@ -29,6 +29,8 @@</span><br><span> #include <osmocom/core/rate_ctr.h></span><br><span> #include <osmocom/core/stats.h></span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#include <stat_item_internal.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #include <stdio.h></span><br><span> #include <inttypes.h></span><br><span> </span><br><span>@@ -88,11 +90,10 @@</span><br><span> </span><br><span>        struct osmo_stat_item_group *sgrp2;</span><br><span>  const struct osmo_stat_item *sitem1, *sitem2;</span><br><span style="color: hsl(0, 100%, 40%);">-   int rc;</span><br><span>      int32_t value;</span><br><span style="color: hsl(0, 100%, 40%);">-  int32_t next_id_a = 1;</span><br><span style="color: hsl(0, 100%, 40%);">-  int32_t next_id_b = 1;</span><br><span>       int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        int64_t sum1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int64_t sum2;</span><br><span> </span><br><span>    OSMO_ASSERT(statg != NULL);</span><br><span> </span><br><span>@@ -117,124 +118,144 @@</span><br><span>    OSMO_ASSERT(sitem2 != sitem1);</span><br><span>       OSMO_ASSERT(sitem2 == osmo_stat_item_group_get_item(statg, TEST_B_ITEM));</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* No value set yet, expecting default value from osmo_stat_item_desc definition above. */</span><br><span>   value = osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));</span><br><span>  OSMO_ASSERT(value == -1);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 0);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No value set yet, expecting new value in all fields */</span><br><span>    osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+     sum1 = 1;</span><br><span>    value = osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));</span><br><span>  OSMO_ASSERT(value == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.n == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(sitem1->value.min == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(sitem1->value.last == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.max == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(sitem1->value.sum == 1);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_ASSERT(value == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 0);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+ sum2 = 0;</span><br><span>    for (i = 2; i <= 32; i++) {</span><br><span>               osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), i);</span><br><span style="color: hsl(120, 100%, 40%);">+             sum1 += i;</span><br><span>           osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), 1000 + i);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-            OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-           OSMO_ASSERT(value == i);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-                rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), &next_id_b, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-            OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-           OSMO_ASSERT(value == 1000 + i);</span><br><span style="color: hsl(120, 100%, 40%);">+               sum2 += 1000 + i;</span><br><span>    }</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(sitem1->value.n == 32);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.min == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(sitem1->value.last == 32);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(sitem1->value.max == 32);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == sum1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(sitem2->value.n == 31);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem2->value.min == 1002);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(sitem2->value.last == 1032);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem2->value.max == 1032);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(sitem2->value.sum == sum2);</span><br><span> </span><br><span>       /* check if dec & inc is working */</span><br><span>      osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 42);</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_ASSERT(value == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+     sum1 += 42;</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->value.n == 33);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.min == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(sitem1->value.last == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.max == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == sum1);</span><br><span> </span><br><span>       osmo_stat_item_dec(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 21);</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_ASSERT(value == 21);</span><br><span style="color: hsl(120, 100%, 40%);">+     sum1 += 42 - 21;</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.n == 34);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.min == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(sitem1->value.last == 21);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 21);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.max == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == sum1);</span><br><span> </span><br><span>       osmo_stat_item_inc(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 21);</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_ASSERT(value == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+     sum1 += 42;</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->value.n == 35);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.min == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(sitem1->value.last == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.max == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == sum1);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  /* Keep 2 in FIFO */</span><br><span style="color: hsl(0, 100%, 40%);">-    osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 33);</span><br><span style="color: hsl(0, 100%, 40%);">-      osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), 1000 + 33);</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Test item flush, reporting period elapsing */</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.n == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(sitem1->value.min == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.last == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.max == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == 0);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     for (i = 34; i <= 64; i++) {</span><br><span style="color: hsl(0, 100%, 40%);">-         osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), i);</span><br><span style="color: hsl(0, 100%, 40%);">-               osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), 1000 + i);</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Still see the previous reporting period in reported.* */</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.n == 35);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(sitem1->reported.min == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(sitem1->reported.last == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(sitem1->reported.max == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.sum == sum1);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-               rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-            OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-           OSMO_ASSERT(value == i-1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-              rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), &next_id_b, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-            OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-           OSMO_ASSERT(value == 1000 + i-1);</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_ASSERT(value == 64);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), &next_id_b, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_ASSERT(value == 1000 + 64);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        /* Overrun FIFOs */</span><br><span style="color: hsl(0, 100%, 40%);">-     for (i = 65; i <= 96; i++) {</span><br><span style="color: hsl(0, 100%, 40%);">-         osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), i);</span><br><span style="color: hsl(0, 100%, 40%);">-               osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), 1000 + i);</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       fprintf(stderr, "Skipping %d values\n", 93 - 65);</span><br><span style="color: hsl(0, 100%, 40%);">-     rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 93 - 65 + 1);</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_ASSERT(value == 93);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       for (i = 94; i <= 96; i++) {</span><br><span style="color: hsl(0, 100%, 40%);">-         rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-            OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-           OSMO_ASSERT(value == i);</span><br><span style="color: hsl(0, 100%, 40%);">-        }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       fprintf(stderr, "Skipping %d values\n", 90 - 65);</span><br><span style="color: hsl(0, 100%, 40%);">-     rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), &next_id_b, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 90 - 65 + 1);</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_ASSERT(value == 1000 + 90);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        for (i = 91; i <= 96; i++) {</span><br><span style="color: hsl(0, 100%, 40%);">-         rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_B_ITEM), &next_id_b, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-            OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-           OSMO_ASSERT(value == 1000 + i);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* Test Discard (single item) */</span><br><span style="color: hsl(120, 100%, 40%);">+      /* After a flush, the first item replaces the last, min and max */</span><br><span>   osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 97);</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = osmo_stat_item_discard(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a);</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(sitem1->value.n == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(sitem1->value.min == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.last == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.max == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == 97);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    rc = osmo_stat_item_discard(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a);</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_ASSERT(rc == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ...and still see the previous reporting period in reported.* */</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(sitem1->reported.n == 35);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(sitem1->reported.min == 1);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(sitem1->reported.last == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(sitem1->reported.max == 42);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.sum == sum1);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If an entire reporting period elapses without a new value, the last seen value remains. */</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));</span><br><span style="color: hsl(120, 100%, 40%);">+      osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.n == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(sitem1->value.min == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.last == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.max == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == 0);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     osmo_stat_item_set(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), 98);</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 1);</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_ASSERT(value == 98);</span><br><span style="color: hsl(120, 100%, 40%);">+     /* now the previous reporting period got turned around */</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(sitem1->reported.n == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->reported.min == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.last == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(sitem1->reported.max == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.sum == 0);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-  rc = osmo_stat_item_get_next(osmo_stat_item_group_get_item(statg, TEST_A_ITEM), &next_id_a, &value);</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_ASSERT(rc == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Another empty reporting period, everything remained the same. */</span><br><span style="color: hsl(120, 100%, 40%);">+   osmo_stat_item_flush(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.n == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(sitem1->value.min == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.last == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.max == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(sitem1->reported.n == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->reported.min == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.last == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(sitem1->reported.max == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.sum == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Test Reset, place back to default value. The previously reported value remains the same. */</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_stat_item_reset(osmo_stat_item_group_get_item(statg, TEST_A_ITEM));</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.n == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_ASSERT(sitem1->value.min == -1);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.last == -1);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(osmo_stat_item_get_last(osmo_stat_item_group_get_item(statg, TEST_A_ITEM)) == -1);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(sitem1->value.max == -1);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->value.sum == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_ASSERT(sitem1->reported.n == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(sitem1->reported.min == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.last == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_ASSERT(sitem1->reported.max == 97);</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(sitem1->reported.sum == 0);</span><br><span> </span><br><span>       osmo_stat_item_group_free(statg);</span><br><span> </span><br><span>diff --git a/tests/stats/stats_test.err b/tests/stats/stats_test.err</span><br><span>index 92d6ce1..a890e0f 100644</span><br><span>--- a/tests/stats/stats_test.err</span><br><span>+++ b/tests/stats/stats_test.err</span><br><span>@@ -1,7 +1,3 @@</span><br><span style="color: hsl(0, 100%, 40%);">-Skipping 28 values</span><br><span style="color: hsl(0, 100%, 40%);">-DLSTATS ERROR item.a: 28 stats values skipped</span><br><span style="color: hsl(0, 100%, 40%);">-Skipping 25 values</span><br><span style="color: hsl(0, 100%, 40%);">-DLSTATS ERROR item.b: 25 stats values skipped</span><br><span> Start test: test_reporting</span><br><span> DLGLOBAL ERROR counter group 'ctr-test:one' already exists for index 2, instead using index 3. This is a software bug that needs fixing.</span><br><span> DLGLOBAL ERROR 'ctr-test.one_dot' is not a valid counter group identifier</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/25464">change 25464</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/25464"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I137992a5479fc39bbceb6c6c2af9c227bd33b39b </div>
<div style="display:none"> Gerrit-Change-Number: 25464 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: daniel <dwillmann@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: pespin <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-CC: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-CC: osmith <osmith@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>