mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-27 12:26:24 +00:00
Add FreeRTOS per-task mem usage reporting
This commit is contained in:
1 parent
76a9853b12
commit
bbdd24d7f8
7 files changed
+201
-10
No files matched your search
+87
-5
@@ -3641,6 +3641,92 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read,
|
||||
|
||||
|
||||
|
||||
#if MG_ENABLE_FREERTOS
|
||||
static size_t print_freertos_stacks(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
size_t len = 0;
|
||||
#if configUSE_TRACE_FACILITY == 1
|
||||
struct stack_stat {
|
||||
char name[configMAX_TASK_NAME_LEN];
|
||||
size_t total, free_min;
|
||||
};
|
||||
struct stack_stat *stacks = NULL;
|
||||
TaskStatus_t *tasks = NULL;
|
||||
size_t count;
|
||||
bool ok = false;
|
||||
|
||||
// Keep task handles and names valid until the snapshot has been copied
|
||||
vTaskSuspendAll();
|
||||
count = (size_t) uxTaskGetNumberOfTasks();
|
||||
if (count == 0) {
|
||||
ok = true;
|
||||
} else if (count <= SIZE_MAX / sizeof(*tasks) &&
|
||||
count <= SIZE_MAX / sizeof(*stacks)) {
|
||||
tasks = mg_calloc(count, sizeof(*tasks));
|
||||
stacks = mg_calloc(count, sizeof(*stacks));
|
||||
if (tasks != NULL && stacks != NULL &&
|
||||
(size_t) uxTaskGetSystemState(tasks, (UBaseType_t) count, NULL) == count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
mg_snprintf(stacks[i].name, sizeof(stacks[i].name), "%s",
|
||||
tasks[i].pcTaskName);
|
||||
stacks[i].total = (size_t) MG_HEALTH_TASK_STACK_TOTAL(tasks[i].xHandle);
|
||||
stacks[i].free_min =
|
||||
(size_t) tasks[i].usStackHighWaterMark * sizeof(StackType_t);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
(void) xTaskResumeAll();
|
||||
mg_free(tasks);
|
||||
|
||||
// Format after resuming the scheduler. No task-owned pointers are retained.
|
||||
if (ok) {
|
||||
len += mg_xprintf(fn, arg, "[");
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
char used[32] = "null", total[32] = "null";
|
||||
if (stacks[i].total > 0) {
|
||||
mg_snprintf(total, sizeof(total), "%zu", stacks[i].total);
|
||||
if (stacks[i].total >= stacks[i].free_min) {
|
||||
mg_snprintf(used, sizeof(used), "%zu",
|
||||
stacks[i].total - stacks[i].free_min);
|
||||
}
|
||||
}
|
||||
len += mg_xprintf(fn, arg, "%s{%m:%m,%m:%s,%m:%s}",
|
||||
i == 0 ? "" : ",", MG_ESC("name"),
|
||||
MG_ESC(stacks[i].name), MG_ESC("used"), used,
|
||||
MG_ESC("total"), total);
|
||||
}
|
||||
len += mg_xprintf(fn, arg, "]");
|
||||
} else {
|
||||
len += mg_xprintf(fn, arg, "null");
|
||||
}
|
||||
mg_free(stacks);
|
||||
#else
|
||||
len += mg_xprintf(fn, arg, "null");
|
||||
#endif
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t mg_print_mem_stats(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
size_t len;
|
||||
#if MG_ENABLE_FREERTOS
|
||||
size_t heap_used = (size_t) MG_HEALTH_HEAP_USED;
|
||||
size_t heap_total = (size_t) MG_HEALTH_HEAP_TOTAL;
|
||||
len = mg_xprintf(fn, arg, "{%m:%zu,%m:%zu,%m:%M}",
|
||||
MG_ESC("heap_used"), heap_used, MG_ESC("heap_total"),
|
||||
heap_total, MG_ESC("stacks"), print_freertos_stacks);
|
||||
#else
|
||||
len = mg_xprintf(fn, arg, "{%m:%zu,%m:%zu,%m:%zu,%m:%zu}",
|
||||
MG_ESC("stack_used"), (size_t) MG_HEALTH_STACK_USED,
|
||||
MG_ESC("stack_total"), (size_t) MG_HEALTH_STACK_TOTAL,
|
||||
MG_ESC("heap_used"), (size_t) MG_HEALTH_HEAP_USED,
|
||||
MG_ESC("heap_total"), (size_t) MG_HEALTH_HEAP_TOTAL);
|
||||
#endif
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
|
||||
// The one health record. Lives in RAM that survives a warm reset, see
|
||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||
@@ -7291,11 +7377,7 @@ static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
||||
}
|
||||
|
||||
static void mg_mdash_rpc_mem_stats(struct mg_rpc_req *r) {
|
||||
mg_rpc_ok(r, "{%m:%zu,%m:%zu,%m:%zu,%m:%zu}", //
|
||||
MG_ESC("stack_used"), (size_t) MG_HEALTH_STACK_USED, //
|
||||
MG_ESC("stack_total"), (size_t) MG_HEALTH_STACK_TOTAL, //
|
||||
MG_ESC("heap_used"), (size_t) MG_HEALTH_HEAP_USED, //
|
||||
MG_ESC("heap_total"), (size_t) MG_HEALTH_HEAP_TOTAL);
|
||||
mg_rpc_ok(r, "%M", mg_print_mem_stats);
|
||||
}
|
||||
|
||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
||||
|
||||
+11
@@ -4431,6 +4431,12 @@ void mg_ota_poll(struct mg_mgr *);
|
||||
#define MG_HEALTH_HEAP_TOTAL 0
|
||||
#endif
|
||||
|
||||
// FreeRTOS: return the task's configured stack size in bytes, or 0 if unknown
|
||||
// Override with an application lookup, e.g. app_task_stack_total(task)
|
||||
#ifndef MG_HEALTH_TASK_STACK_TOTAL
|
||||
#define MG_HEALTH_TASK_STACK_TOTAL(task) 0
|
||||
#endif
|
||||
|
||||
#ifndef MG_HEALTH_MAGIC
|
||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||
#endif
|
||||
@@ -4504,6 +4510,11 @@ static inline const char *mg_health_reason_str(int reason) {
|
||||
|
||||
#define MG_HEALTH_INIT() mg_health_init()
|
||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||
// JSON memory statistics, suitable for %M. Non-FreeRTOS builds retain scalar
|
||||
// stack_used/stack_total. FreeRTOS builds report a stacks array with name, used,
|
||||
// and total. Used represents peak usage, requires
|
||||
// configUSE_TRACE_FACILITY=1, stacks is null if unavailable or allocation fails.
|
||||
extern size_t mg_print_mem_stats(mg_pfn_t, void *, va_list *);
|
||||
extern void mg_health_init(void);
|
||||
// Cloud connector API to https://mdash.net - device management cloud
|
||||
|
||||
|
||||
@@ -1,6 +1,92 @@
|
||||
#include "config.h"
|
||||
#include "health.h"
|
||||
|
||||
#if MG_ENABLE_FREERTOS
|
||||
static size_t print_freertos_stacks(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
size_t len = 0;
|
||||
#if configUSE_TRACE_FACILITY == 1
|
||||
struct stack_stat {
|
||||
char name[configMAX_TASK_NAME_LEN];
|
||||
size_t total, free_min;
|
||||
};
|
||||
struct stack_stat *stacks = NULL;
|
||||
TaskStatus_t *tasks = NULL;
|
||||
size_t count;
|
||||
bool ok = false;
|
||||
|
||||
// Keep task handles and names valid until the snapshot has been copied
|
||||
vTaskSuspendAll();
|
||||
count = (size_t) uxTaskGetNumberOfTasks();
|
||||
if (count == 0) {
|
||||
ok = true;
|
||||
} else if (count <= SIZE_MAX / sizeof(*tasks) &&
|
||||
count <= SIZE_MAX / sizeof(*stacks)) {
|
||||
tasks = mg_calloc(count, sizeof(*tasks));
|
||||
stacks = mg_calloc(count, sizeof(*stacks));
|
||||
if (tasks != NULL && stacks != NULL &&
|
||||
(size_t) uxTaskGetSystemState(tasks, (UBaseType_t) count, NULL) == count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
mg_snprintf(stacks[i].name, sizeof(stacks[i].name), "%s",
|
||||
tasks[i].pcTaskName);
|
||||
stacks[i].total = (size_t) MG_HEALTH_TASK_STACK_TOTAL(tasks[i].xHandle);
|
||||
stacks[i].free_min =
|
||||
(size_t) tasks[i].usStackHighWaterMark * sizeof(StackType_t);
|
||||
}
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
(void) xTaskResumeAll();
|
||||
mg_free(tasks);
|
||||
|
||||
// Format after resuming the scheduler. No task-owned pointers are retained.
|
||||
if (ok) {
|
||||
len += mg_xprintf(fn, arg, "[");
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
char used[32] = "null", total[32] = "null";
|
||||
if (stacks[i].total > 0) {
|
||||
mg_snprintf(total, sizeof(total), "%zu", stacks[i].total);
|
||||
if (stacks[i].total >= stacks[i].free_min) {
|
||||
mg_snprintf(used, sizeof(used), "%zu",
|
||||
stacks[i].total - stacks[i].free_min);
|
||||
}
|
||||
}
|
||||
len += mg_xprintf(fn, arg, "%s{%m:%m,%m:%s,%m:%s}",
|
||||
i == 0 ? "" : ",", MG_ESC("name"),
|
||||
MG_ESC(stacks[i].name), MG_ESC("used"), used,
|
||||
MG_ESC("total"), total);
|
||||
}
|
||||
len += mg_xprintf(fn, arg, "]");
|
||||
} else {
|
||||
len += mg_xprintf(fn, arg, "null");
|
||||
}
|
||||
mg_free(stacks);
|
||||
#else
|
||||
len += mg_xprintf(fn, arg, "null");
|
||||
#endif
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t mg_print_mem_stats(mg_pfn_t fn, void *arg, va_list *ap) {
|
||||
size_t len;
|
||||
#if MG_ENABLE_FREERTOS
|
||||
size_t heap_used = (size_t) MG_HEALTH_HEAP_USED;
|
||||
size_t heap_total = (size_t) MG_HEALTH_HEAP_TOTAL;
|
||||
len = mg_xprintf(fn, arg, "{%m:%zu,%m:%zu,%m:%M}",
|
||||
MG_ESC("heap_used"), heap_used, MG_ESC("heap_total"),
|
||||
heap_total, MG_ESC("stacks"), print_freertos_stacks);
|
||||
#else
|
||||
len = mg_xprintf(fn, arg, "{%m:%zu,%m:%zu,%m:%zu,%m:%zu}",
|
||||
MG_ESC("stack_used"), (size_t) MG_HEALTH_STACK_USED,
|
||||
MG_ESC("stack_total"), (size_t) MG_HEALTH_STACK_TOTAL,
|
||||
MG_ESC("heap_used"), (size_t) MG_HEALTH_HEAP_USED,
|
||||
MG_ESC("heap_total"), (size_t) MG_HEALTH_HEAP_TOTAL);
|
||||
#endif
|
||||
(void) ap;
|
||||
return len;
|
||||
}
|
||||
|
||||
// The one health record. Lives in RAM that survives a warm reset, see
|
||||
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||
|
||||
@@ -22,6 +22,12 @@
|
||||
#define MG_HEALTH_HEAP_TOTAL 0
|
||||
#endif
|
||||
|
||||
// FreeRTOS: return the task's configured stack size in bytes, or 0 if unknown
|
||||
// Override with an application lookup, e.g. app_task_stack_total(task)
|
||||
#ifndef MG_HEALTH_TASK_STACK_TOTAL
|
||||
#define MG_HEALTH_TASK_STACK_TOTAL(task) 0
|
||||
#endif
|
||||
|
||||
#ifndef MG_HEALTH_MAGIC
|
||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||
#endif
|
||||
@@ -95,4 +101,9 @@ static inline const char *mg_health_reason_str(int reason) {
|
||||
|
||||
#define MG_HEALTH_INIT() mg_health_init()
|
||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
||||
// JSON memory statistics, suitable for %M. Non-FreeRTOS builds retain scalar
|
||||
// stack_used/stack_total. FreeRTOS builds report a stacks array with name, used,
|
||||
// and total. Used represents peak usage, requires
|
||||
// configUSE_TRACE_FACILITY=1, stacks is null if unavailable or allocation fails.
|
||||
extern size_t mg_print_mem_stats(mg_pfn_t, void *, va_list *);
|
||||
extern void mg_health_init(void);
|
||||
+1
-5
@@ -106,11 +106,7 @@ static void mg_mdash_rpc_get_info(struct mg_rpc_req *r) {
|
||||
}
|
||||
|
||||
static void mg_mdash_rpc_mem_stats(struct mg_rpc_req *r) {
|
||||
mg_rpc_ok(r, "{%m:%zu,%m:%zu,%m:%zu,%m:%zu}", //
|
||||
MG_ESC("stack_used"), (size_t) MG_HEALTH_STACK_USED, //
|
||||
MG_ESC("stack_total"), (size_t) MG_HEALTH_STACK_TOTAL, //
|
||||
MG_ESC("heap_used"), (size_t) MG_HEALTH_HEAP_USED, //
|
||||
MG_ESC("heap_total"), (size_t) MG_HEALTH_HEAP_TOTAL);
|
||||
mg_rpc_ok(r, "%M", mg_print_mem_stats);
|
||||
}
|
||||
|
||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
||||
|
||||
@@ -165,6 +165,7 @@ standard names. */
|
||||
#define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 0
|
||||
|
||||
/* USER CODE BEGIN Defines */
|
||||
#define INCLUDE_xTaskGetIdleTaskHandle 1
|
||||
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
|
||||
/* USER CODE END Defines */
|
||||
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
#define MG_HEALTH_HEAP_TOTAL ((size_t) configTOTAL_HEAP_SIZE)
|
||||
#define MG_HEALTH_HEAP_USED (MG_HEALTH_HEAP_TOTAL - xPortGetFreeHeapSize())
|
||||
|
||||
// Per-task totals in bytes
|
||||
size_t app_task_stack_total(TaskHandle_t task);
|
||||
#define MG_HEALTH_TASK_STACK_TOTAL(task) app_task_stack_total(task)
|
||||
|
||||
// Ethernet driver tunables:
|
||||
// #define MG_DRIVER_MDC_CR 4 // RMII MDC clock divider, from 0 to 5
|
||||
// #define MG_TCPIP_PHY_ADDR 0 // PHY address
|
||||
|
||||
Reference in new issue
Block a user