blob: efff11e36252d611a2836678a8ab41f10cfc7349 [file] [log] [blame]
/*
* 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 <assert.h>
#include <string.h>
#include <stdio.h>
#include "os/mynewt.h"
#include "stats/stats.h"
/**
* Declare an example statistics section, which is fittingly, the number
* statistics registered in the system. There are two, largely duplicated,
* statistics sections here, in order to provide the optional ability to
* name statistics.
*
* STATS_SECT_START/END actually declare the statistics structure definition,
* STATS_SECT_DECL() creates the structure declaration so you can declare
* these statistics as a global structure, and STATS_NAME_START/END are how
* you name the statistics themselves.
*
* Statistics entries can be declared as any of the following values, however,
* all statistics in a given structure must be of the same size, and they are
* all unsigned.
*
* - STATS_SECT_ENTRY(): default statistic entry, 32-bits. This
* is the good stuff. Two factors to consider:
* - With 16-bit numbers, rollovers happen, frequently. Whether its
* testing a pathological condition, or just a long time since you've
* collected a statistic: it really sucks to not have a crucial piece
* of information.
* - 64-bit numbers are wonderful things. However, the gods did not see
* fit to bless us with unlimited memory. 64-bit statistics are useful
* when you want to store non-statistics in a statistics entry (i.e. time),
* because its convenient...
*
* - STATS_SECT_ENTRY16(): 16-bits. Smaller statistics if you need to fit into
* specific RAM or code size numbers.
*
* - STATS_SECT_ENTRY32(): 32-bits, if you want to force it.
*
* - STATS_SECT_ENTRY64(): 64-bits. Useful for storing chunks of data.
*
* Following the statics entry declaration is the statistic names declaration.
* This is compiled out when STATS_NAME_ENABLE is set to 0. This declaration
* is const, and therefore can be located in .text, not .data.
*
* In cases where the system configuration variable STATS_NAME_ENABLE is set
* to 1, the statistics names are stored and returned to both the console
* and management APIs. Whereas, when STATS_NAME_ENABLE = 0, these statistics
* are numbered, s0, s1, etc.
*/
STATS_SECT_START(stats)
STATS_SECT_ENTRY(num_registered)
STATS_SECT_END
STATS_SECT_DECL(stats) g_stats_stats;
STATS_NAME_START(stats)
STATS_NAME(stats, num_registered)
STATS_NAME_END(stats)
STAILQ_HEAD(, stats_hdr) g_stats_registry =
STAILQ_HEAD_INITIALIZER(g_stats_registry);
/**
* Walk a specific statistic entry, and call walk_func with arg for
* each field within that entry.
*
* Walk func takes the following parameters:
*
* - The header of the statistics section (stats_hdr)
* - The user supplied argument
* - The name of the statistic (if STATS_NAME_ENABLE = 0, this is
* ("s%d", n), where n is the number of the statistic in the structure.
* - A pointer to the current entry.
*
* @return 0 on success, the return code of the walk_func on abort.
*
*/
int
stats_walk(struct stats_hdr *hdr, stats_walk_func_t walk_func, void *arg)
{
char *name;
char name_buf[12];
uint16_t cur;
uint16_t end;
int ent_n;
int len;
int rc;
#if MYNEWT_VAL(STATS_NAMES)
int i;
#endif
cur = sizeof(*hdr);
end = sizeof(*hdr) + (hdr->s_size * hdr->s_cnt);
while (cur < end) {
/*
* Access and display the statistic name. Pass that to the
* walk function
*/
name = NULL;
#if MYNEWT_VAL(STATS_NAMES)
/* The stats name map contains two elements, an offset into the
* statistics entry structure, and the name corresponding with that
* offset. This annotation allows for naming only certain statistics,
* and doesn't enforce ordering restrictions on the stats name map.
*/
for (i = 0; i < hdr->s_map_cnt; ++i) {
if (hdr->s_map[i].snm_off == cur) {
name = hdr->s_map[i].snm_name;
break;
}
}
#endif
/* Do this check irrespective of whether MYNEWT_VALUE(STATS_NAMES)
* is set. Users may only partially name elements in the statistics
* structure.
*/
if (name == NULL) {
ent_n = (cur - sizeof(*hdr)) / hdr->s_size;
len = snprintf(name_buf, sizeof(name_buf), "s%d", ent_n);
name_buf[len] = '\0';
name = name_buf;
}
rc = walk_func(hdr, arg, name, cur);
if (rc != 0) {
goto err;
}
/* Statistics are variable sized, move forward either 16, 32 or 64
* bits in the structure.
*/
cur += hdr->s_size;
}
return (0);
err:
return (rc);
}
/**
* Initialize the stastics module. Called before any of the statistics get
* registered to initialize global structures, and register the default
* statistics "stat."
*
* ASSERT's if it fails, as something is likely h0rked system wide.
*/
void
stats_module_init(void)
{
int rc;
/* Ensure this function only gets called by sysinit. */
SYSINIT_ASSERT_ACTIVE();
STAILQ_INIT(&g_stats_registry);
#if MYNEWT_VAL(STATS_CLI)
rc = stats_shell_register();
SYSINIT_PANIC_ASSERT(rc == 0);
#endif
#if MYNEWT_VAL(STATS_NEWTMGR)
rc = stats_nmgr_register_group();
SYSINIT_PANIC_ASSERT(rc == 0);
#endif
rc = stats_init(STATS_HDR(g_stats_stats),
STATS_SIZE_INIT_PARMS(g_stats_stats, STATS_SIZE_32),
STATS_NAME_INIT_PARMS(stats));
SYSINIT_PANIC_ASSERT(rc == 0);
rc = stats_register("stat", STATS_HDR(g_stats_stats));
SYSINIT_PANIC_ASSERT(rc == 0);
}
/**
* Initialize a statistics structure, pointed to by hdr.
*
* @param hdr The header of the statistics structure, contains things
* like statistic section name, size of statistics entries,
* number of statistics, etc.
* @param size The size of the individual statistics elements, either
* 2 (16-bits), 4 (32-bits) or 8 (64-bits).
* @param cnt The number of elements in the statistics structure
* @param map The mapping of statistics name to statistic entry
* @param map_cnt The number of items in the statistics map
*
* @return 0 on success, non-zero error code on failure.
*/
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);
shdr->s_size = size;
shdr->s_cnt = cnt;
#if MYNEWT_VAL(STATS_NAMES)
shdr->s_map = map;
shdr->s_map_cnt = map_cnt;
#endif
return (0);
}
/**
* Walk the group of registered statistics and call walk_func() for
* each element in the list. This function _DOES NOT_ lock the statistics
* list, and assumes that the list is not being changed by another task.
* (assumption: all statistics are registered prior to OS start.)
*
* @param walk_func The walk function to call, with a statistics header
* and arg.
* @param arg The argument to call the walk function with.
*
* @return 0 on success, non-zero error code on failure
*/
int
stats_group_walk(stats_group_walk_func_t walk_func, void *arg)
{
struct stats_hdr *hdr;
int rc;
STAILQ_FOREACH(hdr, &g_stats_registry, s_next) {
rc = walk_func(hdr, arg);
if (rc != 0) {
goto err;
}
}
return (0);
err:
return (rc);
}
/**
* Find a statistics structure by name, this is not thread-safe.
* (assumption: all statistics are registered prior ot OS start.)
*
* @param name The statistic structure name to find
*
* @return statistic structure if found, NULL if not found.
*/
struct stats_hdr *
stats_group_find(char *name)
{
struct stats_hdr *cur;
cur = NULL;
STAILQ_FOREACH(cur, &g_stats_registry, s_next) {
if (!strcmp(cur->s_name, name)) {
break;
}
}
return (cur);
}
/**
* Register the statistics pointed to by shdr, with the name of "name."
*
* @param name The name of the statistic to register. This name is guaranteed
* unique in the statistics map. If already exists, this function
* will return an error.
* @param shdr The statistics header to register into the statistic map under
* name.
*
* @return 0 on success, non-zero error code on failure.
*/
int
stats_register(char *name, struct stats_hdr *shdr)
{
struct stats_hdr *cur;
int rc;
/* Don't allow duplicate entries, return an error if this stat
* is already registered.
*/
STAILQ_FOREACH(cur, &g_stats_registry, s_next) {
if (!strcmp(cur->s_name, name)) {
rc = -1;
goto err;
}
}
shdr->s_name = name;
STAILQ_INSERT_TAIL(&g_stats_registry, shdr, s_next);
STATS_INC(g_stats_stats, num_registered);
return (0);
err:
return (rc);
}
/**
* Initializes and registers the specified statistics section.
*
* @param shdr The statistics header to register
* @param size The entry size of the statistics to register either 2 (16-bit),
* 4 (32-bit) or 8 (64-bit).
* @param cnt The number of statistics entries in the statistics structure.
* @param map The map of statistics entry to statistics name, only used when
* MYNEWT_VAL(STATS_NAME) = 1.
* @param map_cnt The number of elements in the statistics name map.
* @param name The name of the statistics element to register with the system.
*
* @return 0 on success, non-zero error code on failure.
*/
int
stats_init_and_reg(struct stats_hdr *shdr, uint8_t size, uint8_t cnt,
const struct stats_name_map *map, uint8_t map_cnt,
char *name)
{
int rc;
rc = stats_init(shdr, size, cnt, map, map_cnt);
if (rc != 0) {
return rc;
}
rc = stats_register(name, shdr);
if (rc != 0) {
return rc;
}
return rc;
}
/**
* Resets and zeroes the specified statistics section.
*
* @param shdr The statistics header to zero
*/
void
stats_reset(struct stats_hdr *hdr)
{
uint16_t cur;
uint16_t end;
void *stat_val;
cur = sizeof(*hdr);
end = sizeof(*hdr) + (hdr->s_size * hdr->s_cnt);
while (cur < end) {
stat_val = (uint8_t*)hdr + cur;
switch (hdr->s_size) {
case sizeof(uint16_t):
*(uint16_t *)stat_val = 0;
break;
case sizeof(uint32_t):
*(unsigned long *)stat_val = 0;
break;
case sizeof(uint64_t):
*(uint64_t *)stat_val = 0;
break;
}
/*
* Statistics are variable sized, move forward either 16, 32 or 64
* bits in the structure.
*/
cur += hdr->s_size;
}
return;
}