diff --git a/esphome/components/api/__init__.py b/esphome/components/api/__init__.py index 2f67d020a3..3569803e7e 100644 --- a/esphome/components/api/__init__.py +++ b/esphome/components/api/__init__.py @@ -56,6 +56,8 @@ CONF_ENCRYPTION = "encryption" CONF_HEAP_TRACING = "heap_tracing" CONF_HEAP_TRACING_STANDALONE = "standalone" # vs SYSTEM CONF_HEAP_TRACING_RECORDS = "num_records" +CONF_HEAP_TASK_TRACKING = "task_tracking" +CONF_HEAP_TASK_MAX = "max_tasks" def validate_encryption_key(value): @@ -106,6 +108,8 @@ HEAP_TRACING_SCHEMA = cv.Schema( { cv.Optional(CONF_HEAP_TRACING_STANDALONE, default=True): cv.boolean, cv.Optional(CONF_HEAP_TRACING_RECORDS, default=100): cv.positive_int, + cv.Optional(CONF_HEAP_TASK_TRACKING, default=True): cv.boolean, + cv.Optional(CONF_HEAP_TASK_MAX, default=10): cv.positive_int, } ) @@ -212,6 +216,10 @@ async def to_code(config): else: add_idf_sdkconfig_option("CONFIG_HEAP_TRACING_SYSTEM", True) + # Enable heap task tracking if requested + if heap_tracing_config[CONF_HEAP_TASK_TRACKING]: + add_idf_sdkconfig_option("CONFIG_HEAP_TASK_TRACKING", True) + # Generate code to implement heap tracing cg.add_global(cg.RawStatement('#include "esp_heap_trace.h"')) @@ -223,6 +231,12 @@ async def to_code(config): ) ) + # If task tracking is enabled, add the task tracking code + if heap_tracing_config[CONF_HEAP_TASK_TRACKING]: + max_tasks = heap_tracing_config[CONF_HEAP_TASK_MAX] + # Add the global define to update the max tasks value in the implementation + cg.add_define(f"MAX_HEAP_TASKS {max_tasks}") + # Add helper functions for heap tracing with extern "C" to make them globally accessible cg.add_global( cg.RawStatement( diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 0d03f15211..b0563725cb 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -16,10 +16,48 @@ #ifdef USE_API_HEAP_TRACE #include "esp_heap_trace.h" +#include "esp_heap_task_info.h" // Forward declare heap tracing functions that will be used in the API class extern "C" void start_heap_trace(); extern "C" void stop_and_dump_heap_trace(); + +// Maximum number of tasks we expect to track +#define MAX_HEAP_TASKS 10 + +// Global storage for task heap info +static heap_task_info_t task_info[MAX_HEAP_TASKS]; +static size_t tcb_info_size = 0; +static heap_task_totals_t heap_totals[1] = {{.caps = MALLOC_CAP_DEFAULT}}; + +// Dump task heap information +extern "C" void dump_task_heap_info() { + heap_task_info_params_t heap_info = { + .task_info = task_info, + .size = MAX_HEAP_TASKS, + .totals = heap_totals, + .totals_size = 1, + }; + + esp_err_t err = heap_caps_get_per_task_info(&heap_info, &tcb_info_size); + if (err != ESP_OK) { + ESP_LOGE("HEAP", "Failed to get per-task heap info: %d", err); + return; + } + + ESP_LOGI("HEAP", "Task Heap Information (%d tasks):", tcb_info_size); + ESP_LOGI("HEAP", "-------------------------------------"); + ESP_LOGI("HEAP", "%-20s %10s", "Task", "Heap Usage"); + ESP_LOGI("HEAP", "-------------------------------------"); + + for (size_t i = 0; i < tcb_info_size; i++) { + ESP_LOGI("HEAP", "%-20s %10d bytes", task_info[i].task_name, task_info[i].caps[0]); + } + + ESP_LOGI("HEAP", "-------------------------------------"); + ESP_LOGI("HEAP", "Total heap allocated: %d bytes", heap_totals[0].size); + ESP_LOGI("HEAP", "-------------------------------------"); +} #endif #include @@ -176,6 +214,9 @@ void APIServer::loop() { ESP_LOGI(TAG, "Dumping heap trace information"); stop_and_dump_heap_trace(); + // Also dump task-specific heap information + dump_task_heap_info(); + // Start a new trace for the next period start_heap_trace(); @@ -499,6 +540,10 @@ void APIServer::on_shutdown() { // Make sure to stop tracing on shutdown to get final results ESP_LOGI(TAG, "Final heap trace dump on shutdown"); stop_and_dump_heap_trace(); + + // Dump final task heap information + ESP_LOGI(TAG, "Final task heap information dump on shutdown"); + dump_task_heap_info(); #endif }