blob: 32aebc4d5f69708f9018aefa3665aebbd3f347cf [file] [log] [blame]
//! @file
//!
//! Copyright (c) Memfault, Inc.
//! See LICENSE for details
//! @brief Metrics for the application
#include "metrics.h"
#include <malloc.h>
#include "FreeRTOS.h"
#include "memfault/components.h"
#include "memfault/ports/freertos/metrics.h"
#include "memfault/ports/freertos/thread_metrics.h"
#include "task.h"
#define EXAMPLE_TASK_STACKS 300
#define DEBUG_ 0
#if DEBUG_
#include <stdio.h>
#define DEBUG_PRINTF(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define DEBUG_PRINTF(fmt, ...)
#endif
#if !defined(MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE)
#define MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE 1
#endif
static StackType_t metrics_task_stack[EXAMPLE_TASK_STACKS];
static StaticTask_t metrics_task_tcb;
#if MEMFAULT_EVENT_STORAGE_RESTORE_STATE
// Just a stub, not implemented in this example
bool memfault_event_storage_restore_state(sMfltEventStorageSaveState *state) {
(void)state;
return false;
}
#endif
#if MEMFAULT_METRICS_RESTORE_STATE
// Just a stub, not implemented in this example
bool memfault_metrics_restore_state(void *state) {
(void)state;
return false;
}
#endif
#if MEMFAULT_LOG_RESTORE_STATE
// Just a stub, not implemented in this example
bool memfault_log_restore_state(sMfltLogSaveState *state) {
(void)state;
return false;
}
#endif
// This is incompatible with cstd=gnu11 and gcc < 5
#if (__STDC_VERSION__ < 201100L) || (__GNUC__ >= 5)
MEMFAULT_METRICS_DEFINE_THREAD_METRICS(
{
.thread_name = "IDLE",
.stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_idle_pct_max),
},
{
.thread_name = "Tmr Svc",
.stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_tmr_svc_pct_max),
},
{
.thread_name = "Console Input",
.stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_console_input_pct_max),
},
{
.thread_name = "📊 Metrics",
.stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_metrics_pct_max),
},
{
.thread_name = "Heap Task",
.stack_usage_metric_key = MEMFAULT_METRICS_KEY(memory_heap_task_pct_max),
});
#endif
static void prv_run_metrics_task(MEMFAULT_UNUSED void *pvParameters) {
while (true) {
HeapStats_t stats = { 0 };
vPortGetHeapStats(&stats);
MEMFAULT_METRIC_SET_UNSIGNED(FreeRTOS_HeapFreeBytes, stats.xAvailableHeapSpaceInBytes);
MEMFAULT_METRIC_SET_UNSIGNED(FreeRTOS_HeapMinFreeBytes, stats.xMinimumEverFreeBytesRemaining);
// per-mille (1/1000) percentage, scaled on ingestion
const uint32_t heap_pct_max =
(10000 * (configTOTAL_HEAP_SIZE - stats.xMinimumEverFreeBytesRemaining)) /
configTOTAL_HEAP_SIZE;
MEMFAULT_METRIC_SET_UNSIGNED(FreeRTOS_Heap_pct_max, heap_pct_max);
vTaskDelay((1000 * 10) / portTICK_PERIOD_MS);
}
}
void metrics_task_init(void) {
xTaskCreateStatic(
prv_run_metrics_task, /* Function that implements the task. */
"📊 Metrics", /* Text name for the task. */
MEMFAULT_ARRAY_SIZE(metrics_task_stack), /* Number of indexes in the xStack array. */
NULL, /* Parameter passed into the task. */
tskIDLE_PRIORITY, /* Priority at which the task is created. */
metrics_task_stack, /* Array to use as the task's stack. */
&metrics_task_tcb);
}
static void prv_collect_libc_heap_usage_metrics(void) {
// use mallinfo to compute libc heap utilization percentage
struct mallinfo info = mallinfo();
extern uint32_t _heap_bottom;
extern uint32_t _heap_top;
const uint32_t heap_size = (uint32_t)(&_heap_top - &_heap_bottom);
const uint32_t heap_used = info.uordblks;
const uint32_t heap_pct = (10000 * heap_used) / heap_size;
DEBUG_PRINTF("Heap Usage: %lu/%lu (%lu.%02lu%%)\n", heap_used, heap_size, heap_pct / 100,
heap_pct % 100);
MEMFAULT_METRIC_SET_UNSIGNED(memory_pct_max, heap_pct);
}
#if MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE
static void prv_daily_heartbeat(void) {
// Generate a daily_heartbeat session report once a day, based on
// MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS. This demonstrates a simple way to
// track metrics over a 24 hour interval.
MEMFAULT_STATIC_ASSERT((24 * 60 * 60) % MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS == 0,
"Heartbeat interval must be an even divisor of a day");
// For testing, set MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS to a low value (1 second), and
// uncomment the below line
// #define DAILY_HEARTBEAT_INTERVAL_COUNT (24)
#define DAILY_HEARTBEAT_INTERVAL_COUNT ((24 * 60 * 60) / MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS)
static uint64_t s_last_daily_heartbeat_interval = 0;
if (++s_last_daily_heartbeat_interval % DAILY_HEARTBEAT_INTERVAL_COUNT == 0) {
// The first time this is called on boot will be a no-op, since the session
// has not been started yet
uint32_t uptime_s = memfault_platform_get_time_since_boot_ms() / 1000;
if (uptime_s > MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS) {
MEMFAULT_LOG_INFO("📆 Triggering daily heartbeat");
}
// Record a sample metric into the daily session
MEMFAULT_METRIC_SESSION_SET_UNSIGNED(uptime_s, daily_heartbeat, uptime_s);
// End the session
MEMFAULT_METRICS_SESSION_END(daily_heartbeat);
// Start a new session for the next daily interval
MEMFAULT_METRICS_SESSION_START(daily_heartbeat);
}
}
#endif // MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE
void memfault_metrics_heartbeat_collect_data(void) {
MEMFAULT_LOG_INFO("💓 Heartbeat callback triggered");
MEMFAULT_STATIC_ASSERT(
MEMFAULT_METRICS_HEARTBEAT_INTERVAL_SECS <= (60 * 60),
"Heartbeat must be an hour or less for runtime metrics to mitigate counter overflow");
#if MEMFAULT_TEST_USE_PORT_TEMPLATE != 1
memfault_freertos_port_task_runtime_metrics();
#endif
prv_collect_libc_heap_usage_metrics();
#if MEMFAULT_EXAMPLE_DAILY_HEARTBEAT_ENABLE
prv_daily_heartbeat();
#endif
// For demonstration purposes, print the current values. This is not
// recommended for production.
memfault_metrics_heartbeat_debug_print();
}