feat: Add PROFILE command — heap, stack watermarks, CPU runtime stats

- PROFILE command returns heap usage (free/min/dram/iram),
  per-task stack high watermark, and per-task CPU % (when
  CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is enabled)
- Enable FreeRTOS runtime stats in sdkconfig.defaults
- Enlarge cmd reply buffer to 1400 bytes for multi-line output
- Add esp_heap_caps.h include
This commit is contained in:
user
2026-02-04 17:59:30 +01:00
parent 3681568238
commit 7ec70a653d
2 changed files with 53 additions and 1 deletions

View File

@@ -31,6 +31,7 @@
#include "esp_now.h"
#include "esp_timer.h"
#include "esp_task_wdt.h"
#include "esp_heap_caps.h"
#include "esp_ota_ops.h"
#include "esp_https_ota.h"
#include "esp_http_client.h"
@@ -848,6 +849,55 @@ static int cmd_handle(const char *cmd, char *reply, size_t reply_size)
return strlen(reply);
}
/* PROFILE */
if (strcmp(cmd, "PROFILE") == 0) {
int pos = 0;
/* Heap info */
size_t free_heap = esp_get_free_heap_size();
size_t min_heap = esp_get_minimum_free_heap_size();
size_t free_dram = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t total_dram = heap_caps_get_total_size(MALLOC_CAP_8BIT);
size_t free_iram = heap_caps_get_free_size(MALLOC_CAP_IRAM_8BIT);
pos += snprintf(reply + pos, reply_size - pos,
"OK PROFILE\nHEAP free=%u min=%u dram=%u/%u iram=%u\n",
(unsigned)free_heap, (unsigned)min_heap,
(unsigned)free_dram, (unsigned)total_dram, (unsigned)free_iram);
/* Per-task stack watermarks */
pos += snprintf(reply + pos, reply_size - pos, "TASKS\n");
const char *task_names[] = {"led_task", "cmd_task", "adaptive", "ble_host", "main", NULL};
for (int i = 0; task_names[i] != NULL && pos < (int)reply_size - 60; i++) {
TaskHandle_t th = xTaskGetHandle(task_names[i]);
if (th) {
UBaseType_t hwm = uxTaskGetStackHighWaterMark(th);
pos += snprintf(reply + pos, reply_size - pos,
" %-12s stack_free=%u\n", task_names[i], (unsigned)(hwm * sizeof(StackType_t)));
}
}
#if defined(CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS) && CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS
/* CPU runtime stats */
UBaseType_t n = uxTaskGetNumberOfTasks();
TaskStatus_t *tasks = malloc(n * sizeof(TaskStatus_t));
if (tasks) {
uint32_t total_time;
n = uxTaskGetSystemState(tasks, n, &total_time);
if (total_time > 0) {
pos += snprintf(reply + pos, reply_size - pos, "CPU\n");
for (UBaseType_t i = 0; i < n && pos < (int)reply_size - 60; i++) {
uint32_t pct = (tasks[i].ulRunTimeCounter * 100) / total_time;
if (pct > 0 || tasks[i].ulRunTimeCounter > 0) {
pos += snprintf(reply + pos, reply_size - pos,
" %-12s %3lu%%\n", tasks[i].pcTaskName, (unsigned long)pct);
}
}
}
free(tasks);
}
#endif
return pos;
}
/* OTA <url> */
if (strncmp(cmd, "OTA ", 4) == 0) {
const char *url = cmd + 4;
@@ -899,7 +949,7 @@ static void cmd_task(void *arg)
ESP_LOGI(TAG, "Command listener on UDP port %d", CONFIG_CSI_CMD_PORT);
char rx_buf[128];
char reply_buf[256];
char reply_buf[1400];
struct sockaddr_in src_addr;
socklen_t src_len;

View File

@@ -35,6 +35,8 @@ CONFIG_COMPILER_OPTIMIZATION_PERF=y
# FreeRTOS
#
CONFIG_FREERTOS_HZ=1000
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
CONFIG_FREERTOS_USE_STATS_FORMATTING_FUNCTIONS=y
#
# ESP32-specific