Adding Statistics

Full documentation on statistics is available here: https://mynewt.apache.org/latest/os/modules/stats/stats/

Mynewt includes an internal statistics system where numeric values can be declared and incremented over time, then accessed as a debug and analysis tool.

For example, every time a specific sensor is accessed, you can increment the sensor_reads statistic value by one (assigning whatever variable name you want), and when a specific bug or condition occurs you can check the value to see whether or how many times the sensor was accessed before the specific condition occurs.

You can also use statistics to verify the number of bytes sent and received over a specific peripheral to make sure sensitive interrupt driven code is working well without having to stop the entire system.

Configuring Your App for Statistics

To enable statistics in your application, the following dependency needs to be present in pkg.yml:

pkg.deps:
    - "@apache-mynewt-core/sys/stats"

To save flash space internally, statistics are referenced by number by default. If you wish to preserve the full name of the individual statistics for easier debugging, then the following flag also needs to be added to the syscfg.yml file:

syscfg.vals:
    # Include names for statistics.
    STATS_NAMES: 1

Optionally, if you wish to access statistics from the shell interface, you can also enable the 'stat' command with the following flag in syscfg.yml:

syscfg.vals:
    STATS_CLI: 1

Adding Stats to your main.c File:

Stats requires the following header be present:

#include <stats/stats.h>

This will enable the use of the STATS* macros to define the stats layout, following the example below:

STATS_SECT_START(my_stat_section)
    STATS_SECT_ENTRY(attempt_stat)
    STATS_SECT_ENTRY(error_stat)
STATS_SECT_END

At compile time, this will resolve to the following structure:

struct stats_my_stat_section {
    struct stats_hdr s_hdr;
    uint32_t sattempt_stat;
    uint32_t serror_stat;
};

You will also need to provide names for each field, regardless of WHETHER you have enabled naming support via STATS_NAMES or not:

Note that the field names need to match between the STATS_SECT_ENTRY name above and the STATS_NAME entry below!
/* Define a few stats for querying */
STATS_NAME_START(my_stat_section)
    STATS_NAME(my_stat_section, attempt_stat)
    STATS_NAME(my_stat_section, error_stat)
STATS_NAME_END(my_stat_section)

At compile time, this will resolve to the following structure:

struct stats_name_map g_stats_map_my_stat_section[] = {
    { __builtin_offsetof (struct stats_my_stat_section, sattempt_stat), "attempt_stat" },
    { __builtin_offsetof (struct stats_my_stat_section, serror_stat), "error_stat" },
};

Accessing the Stats in Your Code

You will need to declare a global variable somewhere to holds the stats data, using the model below:

STATS_SECT_DECL(my_stat_section) g_mystat;

If the global definition is is another file and you are referencing it elsewhere, you would declare this in the file where you will modify locally:

extern STATS_SECT_DECL(my_stat_section) g_mystat;

Initializing the Stats

Before your stats can be used or accessed, they need to be initialised and registered.

You can initialise your stats entry as follows:

The stats_init() and stats_register() function calls should occur AFTER the sysinit(); function!
rc = stats_init(
    STATS_HDR(g_mystat),
    STATS_SIZE_INIT_PARMS(g_mystat, STATS_SIZE_32),
    STATS_NAME_INIT_PARMS(my_stat_section));
assert(rc == 0);

For the stat size, you can use one of the following values:

  • STATS_SIZE_16 -- stats are 16 bits (wraps at 65536)
  • STATS_SIZE_32 -- stats are 32 bits (wraps at 4294967296)
  • STATS_SIZE_64 -- stats are 64-bits

You then need to register the stats entry so that you can access it, which is done via the following function call:

NOTE: The name below is the name that you will use when accessing the stats via the console or via the newtmgr stat command
rc = stats_register("my_stats", STATS_HDR(g_mystat));
assert(rc == 0);

Updating the Stats Values

Incrementing

To increment the stats values, you can use the STATS_INC or STATS_INCN macros, as shown below:

STATS_INC(g_mystat, attempt_stat);
rc = do_task();
if(rc == ERR) {
    STATS_INC(g_mystat, error_stat);        
}

To increment the value a specific number use STATIS_INCN:

STATS_INCN(g_mystat, attempt_stat, 5);

Accessing Stats with the Console or newtmgr

Console Access

Assuming that you have enabled named access to stats via STATS_NAME_ENABLE you can access your stats from the console via:

stat my_stats

This will give you something resembling the following output:

12274:attempt_stat: 3
12275:error_stat: 0

If you don't have names enabled via STATS_NAME_ENABLE you would see something like this:

stat my_stats
29149:s0: 3
29150:s1: 0

newtmgr Access

You can also access stats through newtmgr as follows:

$ newtmgr -c serial1 stat my_stats
Return Code = 0
Stats Name: my_stats
  attempt_stat: 0
  error_stat: 0

Example: Adding Stats to apps/first/src/main.c

You can extend apps/first with custom stats by updating apps/first/src/main.c with the following code:

#include <assert.h>
#include <string.h>

#include "os/os.h"
#include "bsp/bsp.h"
#include "hal/hal_gpio.h"
#include "sysinit/sysinit.h"
#include "console/console.h"
#include "shell/shell.h"
#include "stats/stats.h"

/* Define task stack and task object */
#define LED_TASK_PRIO        (100)  /* 1 = highest, 255 = lowest */
#define LED_STACK_SIZE       OS_STACK_ALIGN(64)
struct os_task led_task;
os_stack_t led_task_stack[LED_STACK_SIZE];

/* LED task handler prototype declaration */
static void led_task_func(void *arg);

/* Command handler prototype declaration */
static int shell_test_cmd(int argc, char **argv);

/* Shell command struct */
static struct shell_cmd shell_test_cmd_struct = {
    .sc_cmd = "test",
    .sc_cmd_func = shell_test_cmd
};

/* Define a custom stats group for querying */
STATS_SECT_START(led_stat_section)
    STATS_SECT_ENTRY(led_toggles)
STATS_SECT_END

/* Define a few stat name fields for querying */
STATS_NAME_START(led_stat_section)
    STATS_NAME(led_stat_section, led_toggles)
STATS_NAME_END(led_stat_section)

/* Add the global variable to access and increment stats */
STATS_SECT_DECL(led_stat_section) g_ledstats;

int
main(int argc, char **argv)
{
    int rc;

    /* Initialize the task */
    os_task_init(&led_task, "blinky", led_task_func, NULL,
                 LED_TASK_PRIO, OS_WAIT_FOREVER, led_task_stack,
                 LED_STACK_SIZE);

    /* Call this before sysinit to register the command */
    #if MYNEWT_VAL(SHELL_TASK)
        shell_cmd_register(&shell_test_cmd_struct);
    #endif

    /* Initialize the OS */
    sysinit();

    /* Initialise the custom stats section */
    rc = stats_init(
        STATS_HDR(g_ledstats),
        STATS_SIZE_INIT_PARMS(g_ledstats, STATS_SIZE_32),
        STATS_NAME_INIT_PARMS(led_stat_section));
    assert(rc == 0);

    /* Then register the custom section with the stats system */
    rc = stats_register("led_stats", STATS_HDR(g_ledstats));
    assert(rc == 0);

    while (1) {
        /* Run the event queue to process background events */
        os_eventq_run(os_eventq_dflt_get());
    }

    return rc;
}

/* Implement the 'test' command handler */
static int
shell_test_cmd(int argc, char **argv)
{
    console_printf("Test!\n");
    return 0;
}

static void
led_task_func(void *arg)
{
    /* Configure the LED GPIO as an output and HIGH (On) */
    hal_gpio_init_out(LED_BLINK_PIN, 1);

    while (1) {
        /* Wait one second */
        os_time_delay(OS_TICKS_PER_SEC * 1);

        /* Toggle the LED */
        hal_gpio_toggle(LED_BLINK_PIN);

        /* Increment the LED stat */
        STATS_INC(g_ledstats, led_toggles);
    }
}

Monitoring via netwmgr

You can monitor the led_toggles value via newtmgr with the following command (where we can see that the LED had been toggled 11 times to far in this case):

$ newtmgr -c serial1 stat led_stats
stat group: led_stats
        11 led_toggles

Monitoring via shell/console

Alternatively, you can connect to the shell (for example, via: $ minicom -D /dev/tty.USBtoUART) and run the stat led_stats command to get the same value(s):

Last updated on 2017-08-18 at 04.54.13 AM Published on 2017-08-18 at 03.37.04 PM