The variables 'first' and 'next' in function 'stats_buffer_list_first()' and 'stats_buffer_list_next()' were potentially used uninitialized. Depending on the compiler and target architecture, this can lead to different behavior, including warnings or errors when using strict warning flags. By initializing these pointers to 'NULL', we ensure consistent and expected behavior across all toolchains and configurations. Signed-off-by: Fabian Kainka <kainka@cognid.de>
166 lines
3.8 KiB
C
166 lines
3.8 KiB
C
/*
|
|
* Copyright (c) 2024 Trackunit Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#ifdef _POSIX_VERSION
|
|
#undef _POSIX_VERSION
|
|
#endif
|
|
#define _POSIX_VERSION 200809L
|
|
|
|
#include <zephyr/modem/stats.h>
|
|
#include <zephyr/shell/shell.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(modem_stats);
|
|
|
|
static struct k_spinlock stats_buffer_lock;
|
|
static sys_slist_t stats_buffer_list;
|
|
|
|
static struct modem_stats_buffer *stats_buffer_from_node(sys_snode_t *node)
|
|
{
|
|
return (struct modem_stats_buffer *)node;
|
|
}
|
|
|
|
static void stats_buffer_list_append(struct modem_stats_buffer *buffer)
|
|
{
|
|
K_SPINLOCK(&stats_buffer_lock) {
|
|
sys_slist_append(&stats_buffer_list, &buffer->node);
|
|
}
|
|
}
|
|
|
|
static struct modem_stats_buffer *stats_buffer_list_first(void)
|
|
{
|
|
struct modem_stats_buffer *first = NULL;
|
|
|
|
K_SPINLOCK(&stats_buffer_lock) {
|
|
first = stats_buffer_from_node(sys_slist_peek_head(&stats_buffer_list));
|
|
}
|
|
|
|
return first;
|
|
}
|
|
|
|
static struct modem_stats_buffer *stats_buffer_list_next(struct modem_stats_buffer *buffer)
|
|
{
|
|
struct modem_stats_buffer *next = NULL;
|
|
|
|
K_SPINLOCK(&stats_buffer_lock) {
|
|
next = stats_buffer_from_node(sys_slist_peek_next(&buffer->node));
|
|
}
|
|
|
|
return next;
|
|
}
|
|
|
|
static uint8_t percent_used(uint32_t max_used, uint32_t cap)
|
|
{
|
|
uint64_t percent;
|
|
|
|
if (max_used == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (max_used == cap) {
|
|
return 100;
|
|
}
|
|
|
|
percent = 100;
|
|
percent *= max_used;
|
|
percent /= cap;
|
|
|
|
return (uint8_t)percent;
|
|
}
|
|
|
|
static void stats_buffer_get_and_clear_max_used(struct modem_stats_buffer *buffer,
|
|
uint32_t *max_used)
|
|
{
|
|
K_SPINLOCK(&stats_buffer_lock) {
|
|
*max_used = buffer->max_used;
|
|
buffer->max_used = 0;
|
|
}
|
|
}
|
|
|
|
static bool stats_buffer_length_is_valid(const struct modem_stats_buffer *buffer, uint32_t length)
|
|
{
|
|
return length <= buffer->size;
|
|
}
|
|
|
|
static void stats_buffer_log_invalid_length(const struct modem_stats_buffer *buffer,
|
|
uint32_t length)
|
|
{
|
|
LOG_ERR("%s: length (%u) exceeds size (%u)", buffer->name, length, buffer->size);
|
|
}
|
|
|
|
static void stats_buffer_update_max_used(struct modem_stats_buffer *buffer, uint32_t length)
|
|
{
|
|
K_SPINLOCK(&stats_buffer_lock) {
|
|
if (buffer->max_used < length) {
|
|
buffer->max_used = length;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void stats_buffer_print_to_shell(const struct shell *sh,
|
|
const struct modem_stats_buffer *buffer,
|
|
uint32_t max_used)
|
|
{
|
|
shell_print(sh, "%s: used at most: %u of %u (%u%%)", buffer->name, max_used,
|
|
buffer->size, percent_used(max_used, buffer->size));
|
|
}
|
|
|
|
static int stats_buffer_shell_cmd_handler(const struct shell *sh, size_t argc, char **argv)
|
|
{
|
|
struct modem_stats_buffer *buffer;
|
|
uint32_t max_used;
|
|
|
|
ARG_UNUSED(argc);
|
|
ARG_UNUSED(argv);
|
|
|
|
buffer = stats_buffer_list_first();
|
|
|
|
if (buffer == NULL) {
|
|
shell_print(sh, "no buffers exist");
|
|
return 0;
|
|
}
|
|
|
|
while (buffer != NULL) {
|
|
stats_buffer_get_and_clear_max_used(buffer, &max_used);
|
|
stats_buffer_print_to_shell(sh, buffer, max_used);
|
|
buffer = stats_buffer_list_next(buffer);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SHELL_STATIC_SUBCMD_SET_CREATE(
|
|
sub_stats_cmds,
|
|
SHELL_CMD(buffer, NULL, "Get buffer statistics", stats_buffer_shell_cmd_handler),
|
|
SHELL_SUBCMD_SET_END
|
|
);
|
|
|
|
SHELL_CMD_REGISTER(modem_stats, &sub_stats_cmds, "Modem statistics commands", NULL);
|
|
|
|
static void stats_buffer_set_name(struct modem_stats_buffer *buffer, const char *name)
|
|
{
|
|
buffer->name[sizeof(buffer->name) - 1] = '\0';
|
|
strncpy(buffer->name, name, sizeof(buffer->name) - 1);
|
|
}
|
|
|
|
void modem_stats_buffer_init(struct modem_stats_buffer *buffer,
|
|
const char *name, uint32_t size)
|
|
{
|
|
stats_buffer_set_name(buffer, name);
|
|
buffer->max_used = 0;
|
|
buffer->size = size;
|
|
stats_buffer_list_append(buffer);
|
|
}
|
|
|
|
void modem_stats_buffer_advertise_length(struct modem_stats_buffer *buffer, uint32_t length)
|
|
{
|
|
if (!stats_buffer_length_is_valid(buffer, length)) {
|
|
stats_buffer_log_invalid_length(buffer, length);
|
|
return;
|
|
}
|
|
|
|
stats_buffer_update_max_used(buffer, length);
|
|
}
|