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
#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:
/* 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:
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:
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 -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):
Page last edited March 08, 2024
Text editor powered by tinymce.