mirror of
https://github.com/cesanta/mongoose.git
synced 2026-11-10 16:31:44 +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
|
// 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
|
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
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) {
|
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_rpc_ok(r, "%M", mg_print_mem_stats);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
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
|
#define MG_HEALTH_HEAP_TOTAL 0
|
||||||
#endif
|
#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
|
#ifndef MG_HEALTH_MAGIC
|
||||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||||
#endif
|
#endif
|
||||||
@@ -4504,6 +4510,11 @@ static inline const char *mg_health_reason_str(int reason) {
|
|||||||
|
|
||||||
#define MG_HEALTH_INIT() mg_health_init()
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
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);
|
extern void mg_health_init(void);
|
||||||
// Cloud connector API to https://mdash.net - device management cloud
|
// Cloud connector API to https://mdash.net - device management cloud
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,92 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "health.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
|
// 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
|
// MG_HEALTH_RAM and the .mg_health region in the linker script
|
||||||
struct mg_health mg_health_record MG_HEALTH_RAM;
|
struct mg_health mg_health_record MG_HEALTH_RAM;
|
||||||
|
|||||||
@@ -22,6 +22,12 @@
|
|||||||
#define MG_HEALTH_HEAP_TOTAL 0
|
#define MG_HEALTH_HEAP_TOTAL 0
|
||||||
#endif
|
#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
|
#ifndef MG_HEALTH_MAGIC
|
||||||
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
#define MG_HEALTH_MAGIC {'M', 'G', 'H', '3'} // '3' is the layout version
|
||||||
#endif
|
#endif
|
||||||
@@ -95,4 +101,9 @@ static inline const char *mg_health_reason_str(int reason) {
|
|||||||
|
|
||||||
#define MG_HEALTH_INIT() mg_health_init()
|
#define MG_HEALTH_INIT() mg_health_init()
|
||||||
extern size_t mg_print_crash_record(mg_pfn_t, void *, va_list *);
|
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);
|
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) {
|
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_rpc_ok(r, "%M", mg_print_mem_stats);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mg_mdash_rpc_ota_begin(struct mg_rpc_req *r) {
|
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
|
#define USE_CUSTOM_SYSTICK_HANDLER_IMPLEMENTATION 0
|
||||||
|
|
||||||
/* USER CODE BEGIN Defines */
|
/* USER CODE BEGIN Defines */
|
||||||
|
#define INCLUDE_xTaskGetIdleTaskHandle 1
|
||||||
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
|
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
|
||||||
/* USER CODE END Defines */
|
/* USER CODE END Defines */
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,10 @@
|
|||||||
#define MG_HEALTH_HEAP_TOTAL ((size_t) configTOTAL_HEAP_SIZE)
|
#define MG_HEALTH_HEAP_TOTAL ((size_t) configTOTAL_HEAP_SIZE)
|
||||||
#define MG_HEALTH_HEAP_USED (MG_HEALTH_HEAP_TOTAL - xPortGetFreeHeapSize())
|
#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:
|
// Ethernet driver tunables:
|
||||||
// #define MG_DRIVER_MDC_CR 4 // RMII MDC clock divider, from 0 to 5
|
// #define MG_DRIVER_MDC_CR 4 // RMII MDC clock divider, from 0 to 5
|
||||||
// #define MG_TCPIP_PHY_ADDR 0 // PHY address
|
// #define MG_TCPIP_PHY_ADDR 0 // PHY address
|
||||||
|
|||||||
Reference in new issue
Block a user