You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by GitBox <gi...@apache.org> on 2018/11/16 22:33:24 UTC

[GitHub] ccollins476ad closed pull request #1514: sys/stats/full: Persistent stats

ccollins476ad closed pull request #1514: sys/stats/full: Persistent stats
URL: https://github.com/apache/mynewt-core/pull/1514
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/sys/stats/full/include/stats/stats.h b/sys/stats/full/include/stats/stats.h
index f5fa41a802..929e08bb9d 100644
--- a/sys/stats/full/include/stats/stats.h
+++ b/sys/stats/full/include/stats/stats.h
@@ -27,6 +27,9 @@
 extern "C" {
 #endif
 
+/** The stat group is periodically written to sys/config. */
+#define STATS_HDR_F_PERSIST             0x01
+
 struct stats_name_map {
     uint16_t snm_off;
     char *snm_name;
@@ -36,7 +39,7 @@ struct stats_hdr {
     const char *s_name;
     uint8_t s_size;
     uint8_t s_cnt;
-    uint16_t s_pad1;
+    uint16_t s_flags;
 #if MYNEWT_VAL(STATS_NAMES)
     const struct stats_name_map *s_map;
     int s_map_cnt;
@@ -44,6 +47,17 @@ struct stats_hdr {
     STAILQ_ENTRY(stats_hdr) s_next;
 };
 
+/**
+ * Header describing a persistent stat group.  A pointer to a regular
+ * `stats_hdr` can be safely cast to a pointer to `stats_persisted_hdr` (and
+ * vice-versa) if the `STATS_HDR_F_PERSIST` flag is set.
+ */
+struct stats_persisted_hdr {
+    struct stats_hdr sp_hdr;
+    struct os_callout sp_persist_timer;
+    os_time_t sp_persist_delay;
+};
+
 #define STATS_SECT_DECL(__name)             \
     struct stats_ ## __name
 
@@ -66,25 +80,91 @@ STATS_SECT_DECL(__name) {                   \
 #define STATS_SECT_ENTRY16(__var) uint16_t STATS_SECT_VAR(__var);
 #define STATS_SECT_ENTRY32(__var) uint32_t STATS_SECT_VAR(__var);
 #define STATS_SECT_ENTRY64(__var) uint64_t STATS_SECT_VAR(__var);
-#define STATS_RESET(__var)                                              \
-    memset((uint8_t *)&__var + sizeof(struct stats_hdr), 0,             \
-           sizeof(__var) - sizeof(struct stats_hdr))
+
+/**
+ * @brief Resets all stats in the provided group to 0.
+ *
+ * NOTE: This must only be used with non-persistent stat groups.
+ */
+#define STATS_RESET(__sectvarname)                                      \
+    memset((uint8_t *)&__sectvarname + sizeof(struct stats_hdr), 0,     \
+           sizeof(__sectvarname) - sizeof(struct stats_hdr))
 
 #define STATS_SIZE_INIT_PARMS(__sectvarname, __size)                        \
     (__size),                                                               \
-    ((sizeof (__sectvarname)) - sizeof (struct stats_hdr)) / (__size)
+    ((sizeof (__sectvarname)) - sizeof (__sectvarname.s_hdr)) / (__size)
 
-#define STATS_GET(__sectvarname, __var)        \
+#define STATS_GET(__sectvarname, __var)             \
     ((__sectvarname).STATS_SECT_VAR(__var))
 
-#define STATS_INC(__sectvarname, __var)        \
-    (STATS_GET(__sectvarname, __var)++)
+#define STATS_SET_RAW(__sectvarname, __var, __val)  \
+    (STATS_GET(__sectvarname, __var) = (__val))
 
-#define STATS_INCN(__sectvarname, __var, __n)  \
-    (STATS_GET(__sectvarname, __var) += (__n))
+#define STATS_SET(__sectvarname, __var, __val) do               \
+{                                                               \
+    STATS_SET_RAW(__sectvarname, __var, __val);                 \
+    STATS_PERSIST_SCHED((struct stats_hdr *)&__sectvarname);    \
+} while (0)
 
-#define STATS_CLEAR(__sectvarname, __var)        \
-    (STATS_GET(__sectvarname, __var) = 0)
+/**
+ * @brief Adjusts a stat's in-RAM value by the specified delta.
+ *
+ * For non-persistent stats, this is more efficient than `STATS_INCN()`.  This
+ * must only be used with non-persistent stats; for persistent stats the
+ * behavior is undefined.
+ *
+ * @param __sectvarname         The name of the stat group containing the stat
+ *                                  to modify.
+ * @param __var                 The name of the individual stat to modify.
+ * @param __n                   The amount to add to the specified stat.
+ */
+#define STATS_INCN_RAW(__sectvarname, __var, __n)   \
+    (STATS_SET_RAW(__sectvarname, __var,            \
+                   STATS_GET(__sectvarname, __var) + (__n))
+
+/**
+ * @brief Increments a stat's in-RAM value.
+ *
+ * For non-persistent stats, this is more efficient than `STATS_INCN()`.  This
+ * must only be used with non-persistent stats; for persistent stats the
+ * behavior is undefined.
+ *
+ * @param __sectvarname         The name of the stat group containing the stat
+ *                                  to modify.
+ * @param __var                 The name of the individual stat to modify.
+ */
+#define STATS_INC_RAW(__sectvarname, __var)       \
+    STATS_INCN_RAW(__sectvarname, __var, 1)
+
+/**
+ * @brief Adjusts a stat's value by the specified delta.
+ *
+ * If the specified stat group is persistent, this also schedules the group to
+ * be flushed to disk.
+ *
+ * @param __sectvarname         The name of the stat group containing the stat
+ *                                  to modify.
+ * @param __var                 The name of the individual stat to modify.
+ * @param __n                   The amount to add to the specified stat.
+ */
+#define STATS_INCN(__sectvarname, __var, __n)       \
+    STATS_SET(__sectvarname, __var, STATS_GET(__sectvarname, __var) + (__n))
+
+/**
+ * @brief Increments a stat's value.
+ *
+ * If the specified stat group is persistent, this also schedules the group to
+ * be flushed to disk.
+ *
+ * @param __sectvarname         The name of the stat group containing the stat
+ *                                  to modify.
+ * @param __var                 The name of the individual stat to modify.
+ */
+#define STATS_INC(__sectvarname, __var)             \
+    STATS_INCN(__sectvarname, __var, 1)
+
+#define STATS_CLEAR(__sectvarname, __var)           \
+    STATS_SET(__sectvarname, __var, 0)
 
 #if MYNEWT_VAL(STATS_NAMES)
 
@@ -138,6 +218,100 @@ int stats_nmgr_register_group(void);
 int stats_shell_register(void);
 #endif
 
+#if MYNEWT_VAL(STATS_PERSIST)
+
+/**
+ * @brief Starts the definition of a peristed stat group.
+ *
+ * o Follow with invocations of the `STATS_SECT_ENTRY[...]` macros to define
+ *   individual stats.
+ * o Use `STATS_SECT_END` to complete the group definition.
+ */
+#define STATS_PERSISTED_SECT_START(__name)  \
+STATS_SECT_DECL(__name) {                   \
+    struct stats_persisted_hdr s_hdr;
+
+#define STATS_PERSISTED_HDR(__sectname) &(__sectname).s_hdr.sp_hdr
+
+/**
+ * @brief (private) Starts the provided stat group's persistence timer if it is
+ * a persistent group.
+ *
+ * This should be used whenever a statistic's value changes.  This is a no-op
+ * for non-persistent stat groups.
+ */
+#define STATS_PERSIST_SCHED(hdrp_) stats_persist_sched(hdrp_)
+
+/**
+ * @brief (private) Starts the provided stat group's persistence timer.
+ *
+ * This should be used whenever a statistic's value changes.  This is a no-op
+ * for non-persistent stat groups.
+ */
+void stats_persist_sched(struct stats_hdr *hdr);
+
+/**
+ * @brief Flushes to disk all persisted stat groups with pending writes.
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
+int stats_persist_flush(void);
+
+/**
+ * @brief Initializes a persistent stat group.
+ *
+ * This function must be called before any other stats API functions are
+ * applied to the specified stat group.  This is typically done during system
+ * startup.
+ *
+ * Example usage:
+ *     STATS_PERSISTED_SECT_START(my_stats)
+ *         STATS_SECT_ENTRY(stat1)
+ *     STATS_SECT_END(my_stats)
+ *
+ *     STATS_NAME_START(my_stats)
+ *         STATS_SECT_ENTRY(my_stats, stat1)
+ *     STATS_NAME_END(my_stats)
+ *
+ *     rc = stats_persist_init(STATS_PERSISTED_HDR(my_stats),
+ *                             STATS_SIZE_INIT_PARMS(my_stats, STATS_SIZE_32),
+ *                             STATS_NAME_INIT_PARMS(my_stats),
+ *                             1 * OS_TICKS_PER_SEC);   // One second.
+ * 
+ *
+ * @param hdr                   The header of the stat group to initialize.
+ *                                  Use the `STATS_PERSISTED_HDR()` macro to
+ *                                  generate this argument.
+ * @param size                  The size, in bytes, of each statistic.  Use the
+ *                                  `STATS_SIZE_INIT_PARMS()` macro to generate
+ *                                  this and the `cnt` arguments.
+ * @param cnt                   The number of statistics in the group.  Use the
+ *                                  `STATS_SIZE_INIT_PARMS()` macro to generate
+ *                                  this and the `size` arguments.
+ * @param map                   Maps each stat to a human-readable name.  Use
+ *                                  the `STATS_NAME_INIT_PARMS()` macro to
+ *                                  generate this and the `map_cnt` arguments.
+ * @param map_cnt               The number of names in `map.  Use the the
+ *                                  `STATS_NAME_INIT_PARMS()` macro to generate
+ *                                  this and the `map` arguments.
+ * @param persist_delay         The delay, in OS ticks, before the stat group
+ *                                  is flushed to disk after modification.
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
+int stats_persist_init(struct stats_hdr *hdr, uint8_t size,
+                       uint8_t cnt, const struct stats_name_map *map,
+                       uint8_t map_cnt, os_time_t persist_delay);
+
+#else /* MYNEWT_VAL(STATS_PERSIST) */
+
+#define STATS_PERSISTED_SECT_START(__name) \
+    _Static_assert(0, "You must enable STATS_PERSIST to use persistent stats");
+
+#define STATS_PERSIST_SCHED(hdrp_)
+
+#endif /* MYNEWT_VAL(STATS_PERSIST) */
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/sys/stats/full/pkg.yml b/sys/stats/full/pkg.yml
index 988f8fdee6..df7588401c 100644
--- a/sys/stats/full/pkg.yml
+++ b/sys/stats/full/pkg.yml
@@ -35,3 +35,9 @@ pkg.deps.STATS_NEWTMGR:
 
 pkg.init:
     stats_module_init: 10
+
+pkg.init.STATS_PERSIST:
+    stats_conf_init: 'MYNEWT_VAL(STATS_SYSINIT_STAGE_CONF)'
+
+pkg.down.STATS_PERSIST:
+    stats_persist_sysdown: 'MYNEWT_VAL(STATS_SYSDOWN_STAGE)'
diff --git a/sys/stats/full/src/stats.c b/sys/stats/full/src/stats.c
index 2d601a93d1..7c4838b192 100644
--- a/sys/stats/full/src/stats.c
+++ b/sys/stats/full/src/stats.c
@@ -23,6 +23,7 @@
 
 #include "os/mynewt.h"
 #include "stats/stats.h"
+#include "stats_priv.h"
 
 /**
  * Declare an example statistics section, which is fittingly, the number
@@ -79,6 +80,30 @@ STATS_NAME_END(stats)
 STAILQ_HEAD(, stats_hdr) g_stats_registry =
     STAILQ_HEAD_INITIALIZER(g_stats_registry);
 
+static size_t
+stats_offset(const struct stats_hdr *hdr)
+{
+    if (hdr->s_flags & STATS_HDR_F_PERSIST) {
+        return sizeof (struct stats_persisted_hdr);
+    } else {
+        return sizeof (struct stats_hdr);
+    }
+}
+
+size_t
+stats_size(const struct stats_hdr *hdr)
+{
+    return hdr->s_cnt * hdr->s_size;
+}
+
+void *
+stats_data(const struct stats_hdr *hdr)
+{
+    size_t offset;
+
+    offset = stats_offset(hdr);
+    return (uint8_t *)hdr + offset;
+}
 
 /**
  * Walk a specific statistic entry, and call walk_func with arg for
@@ -100,6 +125,7 @@ stats_walk(struct stats_hdr *hdr, stats_walk_func_t walk_func, void *arg)
 {
     char *name;
     char name_buf[12];
+    uint16_t start;
     uint16_t cur;
     uint16_t end;
     int ent_n;
@@ -109,8 +135,9 @@ stats_walk(struct stats_hdr *hdr, stats_walk_func_t walk_func, void *arg)
     int i;
 #endif
 
-    cur = sizeof(*hdr);
-    end = sizeof(*hdr) + (hdr->s_size * hdr->s_cnt);
+    start = stats_offset(hdr);
+    cur = start;
+    end = start + stats_size(hdr);
 
     while (cur < end) {
         /*
@@ -136,7 +163,7 @@ stats_walk(struct stats_hdr *hdr, stats_walk_func_t walk_func, void *arg)
          * structure.
          */
         if (name == NULL) {
-            ent_n = (cur - sizeof(*hdr)) / hdr->s_size;
+            ent_n = (cur - start) / hdr->s_size;
             len = snprintf(name_buf, sizeof(name_buf), "s%d", ent_n);
             name_buf[len] = '\0';
             name = name_buf;
@@ -194,7 +221,6 @@ stats_module_init(void)
     SYSINIT_PANIC_ASSERT(rc == 0);
 }
 
-
 /**
  * Initialize a statistics structure, pointed to by hdr.
  *
@@ -213,10 +239,14 @@ int
 stats_init(struct stats_hdr *shdr, uint8_t size, uint8_t cnt,
         const struct stats_name_map *map, uint8_t map_cnt)
 {
-    memset((uint8_t *) shdr+sizeof(*shdr), 0, size * cnt);
+    size_t offset;
+
+    offset = stats_offset(shdr);
+    memset((uint8_t *)shdr + offset, 0, size * cnt);
 
     shdr->s_size = size;
     shdr->s_cnt = cnt;
+    shdr->s_flags = 0;
 #if MYNEWT_VAL(STATS_NAMES)
     shdr->s_map = map;
     shdr->s_map_cnt = map_cnt;
@@ -306,6 +336,12 @@ stats_register(const char *name, struct stats_hdr *shdr)
 
     shdr->s_name = name;
 
+#if MYNEWT_VAL(STATS_PERSIST)
+    if (shdr->s_flags & STATS_HDR_F_PERSIST) {
+        stats_conf_assert_valid(shdr);
+    }
+#endif
+
     STAILQ_INSERT_TAIL(&g_stats_registry, shdr, s_next);
 
     STATS_INC(g_stats_stats, num_registered);
@@ -362,7 +398,7 @@ stats_reset(struct stats_hdr *hdr)
     void *stat_val;
 
     cur = sizeof(*hdr);
-    end = sizeof(*hdr) + (hdr->s_size * hdr->s_cnt);
+    end = sizeof(*hdr) + stats_size(hdr);
 
     while (cur < end) {
         stat_val = (uint8_t*)hdr + cur;
diff --git a/sys/stats/full/src/stats_conf.c b/sys/stats/full/src/stats_conf.c
new file mode 100644
index 0000000000..335d5fcd24
--- /dev/null
+++ b/sys/stats/full/src/stats_conf.c
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "os/mynewt.h"
+
+#if MYNEWT_VAL(STATS_PERSIST)
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "base64/base64.h"
+#include "config/config.h"
+#include "stats/stats.h"
+#include "stats_priv.h"
+
+static char *stats_conf_get(int argc, char **argv, char *buf, int max_len);
+static int stats_conf_set(int argc, char **argv, char *val);
+static int stats_conf_commit(void);
+static int stats_conf_export(void (*func)(char *name, char *val),
+                             enum conf_export_tgt tgt);
+
+static struct conf_handler stats_conf_handler = {
+    .ch_name = "stat",
+    .ch_get = stats_conf_get,
+    .ch_set = stats_conf_set,
+    .ch_commit = stats_conf_commit,
+    .ch_export = stats_conf_export
+};
+
+static int
+stats_conf_snprintf_name(const struct stats_hdr *hdr, size_t max_len,
+                         char *buf)
+{
+    return snprintf(buf, max_len, "stat/%s", hdr->s_name);
+}
+
+static void
+stats_conf_name(const struct stats_hdr *hdr, char *buf)
+{
+    stats_conf_snprintf_name(hdr, MYNEWT_VAL(STATS_PERSIST_MAX_NAME_SIZE),
+                             buf);
+}
+
+static void
+stats_conf_serialize(const struct stats_hdr *hdr, void *buf)
+{
+    size_t rawlen;
+    void *data;
+
+    rawlen = stats_size(hdr);
+    data = stats_data(hdr);
+
+    conf_str_from_bytes(data, rawlen, buf, MYNEWT_VAL(STATS_PERSIST_BUF_SIZE));
+}
+
+/** Converts in-RAM setting to a config-friendly string. */
+static char *
+stats_conf_get(int argc, char **argv, char *buf, int max_len)
+{
+    const struct stats_hdr *hdr;
+
+    if (argc == 1) {
+        hdr = stats_group_find(argv[0]);
+        if (hdr != NULL) {
+            stats_conf_serialize(hdr, buf);
+        }
+    }
+    return NULL;
+}
+
+/** Converts config string to binary in-RAM value. */
+static int
+stats_conf_set(int argc, char **argv, char *val)
+{
+    struct stats_hdr *hdr;
+    size_t size;
+    void *data;
+    int decode_len;
+
+    if (argc == 1) {
+        hdr = stats_group_find(argv[0]);
+        if (hdr != NULL) {
+            size = stats_size(hdr);
+            data = stats_data(hdr);
+
+            decode_len = base64_decode_len(val);
+            if (decode_len > size) {
+                DEBUG_PANIC();
+                return OS_ENOMEM;
+            }
+
+            memset(data, 0, size);
+            base64_decode(val, data);
+
+            return 0;
+        }
+    }
+    return OS_ENOENT;
+}
+
+static int
+stats_conf_commit(void)
+{
+    return 0;
+}
+
+/**
+ * This structure just holds a pointer to walk callback.  It is undefined
+ * behavior to cast a function pointer to `void *`, so we wrap it with
+ * something that can be safely converted.
+ */
+struct stats_conf_export_walk_arg {
+    void (*func)(char *name, char *val);
+};
+
+static int
+stats_conf_export_walk(struct stats_hdr *hdr, void *arg)
+{
+    char name[MYNEWT_VAL(STATS_PERSIST_MAX_NAME_SIZE)];
+    char data[MYNEWT_VAL(STATS_PERSIST_BUF_SIZE)];
+    struct stats_conf_export_walk_arg *walk_arg;
+
+    walk_arg = arg;
+
+    if (!(hdr->s_flags & STATS_HDR_F_PERSIST)) {
+        return 0;
+    }
+
+    stats_conf_name(hdr, name);
+    stats_conf_serialize(hdr, data);
+
+    walk_arg->func(name, data);
+
+    return 0;
+}
+
+static int
+stats_conf_export(void (*func)(char *name, char *val),
+        enum conf_export_tgt tgt)
+{
+    struct stats_conf_export_walk_arg arg = { func };
+    int rc;
+
+    rc = stats_group_walk(stats_conf_export_walk, &arg);
+    return rc;
+}
+
+int
+stats_conf_save_group(const struct stats_hdr *hdr)
+{
+    char name[MYNEWT_VAL(STATS_PERSIST_MAX_NAME_SIZE)];
+    char data[MYNEWT_VAL(STATS_PERSIST_BUF_SIZE)];
+
+    stats_conf_name(hdr, name);
+    stats_conf_serialize(hdr, data);
+
+    return conf_save_one(name, data);
+}
+
+void
+stats_conf_assert_valid(const struct stats_hdr *hdr)
+{
+    size_t rawlen;
+    size_t enclen;
+
+    rawlen = stats_size(hdr);
+    enclen = BASE64_ENCODE_SIZE(rawlen);
+    assert(enclen <= MYNEWT_VAL(STATS_PERSIST_BUF_SIZE));
+
+    rawlen = stats_conf_snprintf_name(hdr, 0, NULL);
+    assert(rawlen < MYNEWT_VAL(STATS_PERSIST_MAX_NAME_SIZE));
+}
+
+void
+stats_conf_init(void)
+{
+    int rc;
+
+    rc = conf_register(&stats_conf_handler);
+    SYSINIT_PANIC_ASSERT(rc == 0);
+}
+
+#endif
diff --git a/sys/stats/full/src/stats_persist.c b/sys/stats/full/src/stats_persist.c
new file mode 100644
index 0000000000..2e5313cdf5
--- /dev/null
+++ b/sys/stats/full/src/stats_persist.c
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "os/mynewt.h"
+
+#if MYNEWT_VAL(STATS_PERSIST)
+
+#include <assert.h>
+#include "stats/stats.h"
+#include "stats_priv.h"
+
+static void
+stats_persist_timer_exp(struct os_event *ev)
+{
+    const struct stats_hdr *hdr;
+    int rc;
+
+    hdr = ev->ev_arg;
+
+    rc = stats_conf_save_group(hdr);
+    if (rc != 0) {
+        /* XXX: Trigger a system fault if configured to (requres fault feature
+         * to be merged).
+         */
+    }
+}
+
+void
+stats_persist_sched(struct stats_hdr *hdr)
+{
+    struct stats_persisted_hdr *sphdr;
+    int rc;
+
+    if (!(hdr->s_flags & STATS_HDR_F_PERSIST)) {
+        return;
+    }
+
+    sphdr = (void *)hdr;
+
+    if (!os_callout_queued(&sphdr->sp_persist_timer)) {
+        rc = os_callout_reset(&sphdr->sp_persist_timer,
+                              sphdr->sp_persist_delay);
+        assert(rc == 0);
+    }
+}
+
+/**
+ * Passed to `stats_group_walk`; flushes the specified stat group to disk if a
+ * write is pending.
+ */
+static int
+stats_persist_flush_walk(struct stats_hdr *hdr, void *arg)
+{
+    struct stats_persisted_hdr *sphdr;
+
+    if (!(hdr->s_flags & STATS_HDR_F_PERSIST)) {
+        return 0;
+    }
+
+    sphdr = (void *)hdr;
+    if (!os_callout_queued(&sphdr->sp_persist_timer)) {
+        return 0;
+    }
+
+    os_callout_stop(&sphdr->sp_persist_timer);
+    return stats_conf_save_group(hdr);
+}
+
+int
+stats_persist_flush(void)
+{
+    return stats_group_walk(stats_persist_flush_walk, NULL);
+}
+
+/**
+ * Called on system shutdown.  Flushes to disk all persisted stat groups with
+ * pending writes.
+ */
+int
+stats_persist_sysdown(int reason)
+{
+    stats_persist_flush();
+    return 0;
+}
+
+int
+stats_persist_init(struct stats_hdr *hdr, uint8_t size,
+        uint8_t cnt, const struct stats_name_map *map, uint8_t map_cnt,
+        os_time_t persist_delay)
+{
+    struct stats_persisted_hdr *sphdr;
+    int rc;
+
+    rc = stats_init(hdr, size, cnt, map, map_cnt);
+    if (rc != 0) {
+        return rc;
+    }
+    hdr->s_flags |= STATS_HDR_F_PERSIST;
+
+    sphdr = (void *)hdr;
+
+    sphdr->sp_persist_delay = persist_delay;
+    os_callout_init(&sphdr->sp_persist_timer, os_eventq_dflt_get(),
+            stats_persist_timer_exp, hdr);
+
+    return 0;
+}
+
+#endif
diff --git a/sys/stats/full/src/stats_priv.h b/sys/stats/full/src/stats_priv.h
new file mode 100644
index 0000000000..456bd294cb
--- /dev/null
+++ b/sys/stats/full/src/stats_priv.h
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef H_STATS_PRIV_
+#define H_STATS_PRIV_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct stats_hdr;
+
+/**
+ * @brief Calculates the total size, in bytes, of all stats within the
+ * specified group.
+ *
+ * The result only includes the statistics; it does not include the stats
+ * header.
+ *
+ * @param hdr                   The stat group to examine.
+ *
+ * @return                      The aggregate stat group size.
+ */
+size_t stats_size(const struct stats_hdr *hdr);
+
+/**
+ * @brief Retrieves the address of the first stat in the specified group.
+ *
+ * Stats within a group are contiguous, so the result of a call to this
+ * function points to the entire block of stats.
+ *
+ * @param hdr                   The stat group to pull data from.
+ */
+void *stats_data(const struct stats_hdr *hdr);
+
+/**
+ * @brief Writes the specified stat group to sys/config.
+ *
+ * If the provided stat group is non-persistent, this function is a no-op.
+ *
+ * @param hdr                   The stat group to persist.
+ */
+int stats_conf_save_group(const struct stats_hdr *hdr);
+
+/**
+ * @brief Performs a sanity check on the provided persistent stat group.
+ *
+ * Verifies that the buffer sizes configured in syscfg are sufficient for the
+ * specified stat group.  This function asserts that an attempt to persist the
+ * stat group would not fail due to buffer exhaustion.
+ *
+ * @param hdr                   The stat group to verify.
+ */
+void stats_conf_assert_valid(const struct stats_hdr *hdr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/sys/stats/full/syscfg.yml b/sys/stats/full/syscfg.yml
index 995db2cb3c..97c2556b9b 100644
--- a/sys/stats/full/syscfg.yml
+++ b/sys/stats/full/syscfg.yml
@@ -28,3 +28,36 @@ syscfg.defs:
     STATS_NEWTMGR:
         description: 'Expose the "stat" newtmgr command.'
         value: 0
+    STATS_PERSIST:
+        description: >
+            Enables persistent statistics.  Regardless of this setting's value,
+            statistics are not persistent by default.  Enabling this setting
+            just exposes the persistent statistics API.
+        value: 0
+    STATS_PERSIST_BUF_SIZE:
+        description: >
+            The size of the buffer that holds each stat group during
+            persistence.  Before a stat group is persisted, it must be
+            base64-encoded in this buffer.  The buffer is allocated on the
+            stack.  If the buffer is too small for a persistent stat group, the
+            system detects the problem at startup and triggers a failed
+            assertion.
+        value: 128
+    STATS_PERSIST_MAX_NAME_SIZE:
+        description: >
+            The size of the buffer that holds each stat group name during
+            persistence.  The buffer is allocated on the
+            stack.  If the buffer is too small for a persistent stat group
+            name, the system detects the problem at startup and triggers a
+            failed assertion.
+        value: 32
+
+    STATS_SYSINIT_STAGE_CONF:
+        description: >
+            Sysinit stage for persistent stat config.
+        value: 51
+    STATS_SYSDOWN_STAGE:
+        description: >
+            Sysdown stage for persistent stat config.  During system shutdown,
+            pending stat writes get flushed to disk.
+        value: 500


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services