Add heap tracing capability to API component

- Add heap tracing configuration options to API component
- Implement periodic heap trace dumping (every 30 seconds)
- Configure ESP-IDF settings for heap tracing
- Add sample YAML configuration
- Useful for debugging memory reallocation overhead issues
This commit is contained in:
J. Nick Koston 2025-05-08 15:25:06 -05:00
parent 8e29437900
commit 35238c1437
No known key found for this signature in database
3 changed files with 135 additions and 1 deletions

View File

@ -1,8 +1,10 @@
import base64 import base64
import logging
from esphome import automation from esphome import automation
from esphome.automation import Condition from esphome.automation import Condition
import esphome.codegen as cg import esphome.codegen as cg
from esphome.components.esp32 import add_idf_sdkconfig_option
import esphome.config_validation as cv import esphome.config_validation as cv
from esphome.const import ( from esphome.const import (
CONF_ACTION, CONF_ACTION,
@ -23,12 +25,14 @@ from esphome.const import (
CONF_TRIGGER_ID, CONF_TRIGGER_ID,
CONF_VARIABLES, CONF_VARIABLES,
) )
from esphome.core import coroutine_with_priority from esphome.core import CORE, coroutine_with_priority
DEPENDENCIES = ["network"] DEPENDENCIES = ["network"]
AUTO_LOAD = ["socket"] AUTO_LOAD = ["socket"]
CODEOWNERS = ["@OttoWinter"] CODEOWNERS = ["@OttoWinter"]
_LOGGER = logging.getLogger(__name__)
api_ns = cg.esphome_ns.namespace("api") api_ns = cg.esphome_ns.namespace("api")
APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller) APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller)
HomeAssistantServiceCallAction = api_ns.class_( HomeAssistantServiceCallAction = api_ns.class_(
@ -49,6 +53,9 @@ SERVICE_ARG_NATIVE_TYPES = {
"string[]": cg.std_vector.template(cg.std_string), "string[]": cg.std_vector.template(cg.std_string),
} }
CONF_ENCRYPTION = "encryption" CONF_ENCRYPTION = "encryption"
CONF_HEAP_TRACING = "heap_tracing"
CONF_HEAP_TRACING_STANDALONE = "standalone" # vs SYSTEM
CONF_HEAP_TRACING_RECORDS = "num_records"
def validate_encryption_key(value): def validate_encryption_key(value):
@ -95,6 +102,20 @@ def _encryption_schema(config):
return ENCRYPTION_SCHEMA(config) return ENCRYPTION_SCHEMA(config)
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,
}
)
def _heap_tracing_schema(config):
if config is None:
config = {}
return HEAP_TRACING_SCHEMA(config)
CONFIG_SCHEMA = cv.All( CONFIG_SCHEMA = cv.All(
cv.Schema( cv.Schema(
{ {
@ -109,6 +130,7 @@ CONFIG_SCHEMA = cv.All(
): ACTIONS_SCHEMA, ): ACTIONS_SCHEMA,
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA, cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
cv.Optional(CONF_ENCRYPTION): _encryption_schema, cv.Optional(CONF_ENCRYPTION): _encryption_schema,
cv.Optional(CONF_HEAP_TRACING): _heap_tracing_schema,
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation( cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
single=True single=True
), ),
@ -176,6 +198,59 @@ async def to_code(config):
else: else:
cg.add_define("USE_API_PLAINTEXT") cg.add_define("USE_API_PLAINTEXT")
# Handle heap tracing configuration if ESP32 platform and using ESP-IDF
if (heap_tracing_config := config.get(CONF_HEAP_TRACING, None)) is not None:
if CORE.using_esp_idf:
# Enable heap tracing in sdkconfig
add_idf_sdkconfig_option("CONFIG_HEAP_TRACING", True)
# Set tracing mode (standalone or system)
if heap_tracing_config[CONF_HEAP_TRACING_STANDALONE]:
add_idf_sdkconfig_option("CONFIG_HEAP_TRACING_STANDALONE", True)
else:
add_idf_sdkconfig_option("CONFIG_HEAP_TRACING_SYSTEM", True)
# Generate code to implement heap tracing
cg.add_global(cg.RawStatement('#include "esp_heap_trace.h"'))
# Define the trace record buffer
num_records = heap_tracing_config[CONF_HEAP_TRACING_RECORDS]
cg.add_global(
cg.RawStatement(
f"static heap_trace_record_t trace_record[{num_records}];"
)
)
# Add helper functions for heap tracing
cg.add_global(
cg.RawStatement(
"""
void start_heap_trace() {
heap_trace_init_standalone(trace_record, """
+ str(num_records)
+ """);
heap_trace_start(HEAP_TRACE_LEAKS);
}
void stop_and_dump_heap_trace() {
heap_trace_stop();
heap_trace_dump();
}
"""
)
)
# Add periodic heap trace dumping to the api_server.cpp file
# This will be added in C++ code
cg.add_define("USE_API_HEAP_TRACE")
else:
# Not using ESP-IDF, so we can't use heap tracing
_LOGGER.warning(
"Heap tracing is only available when using ESP-IDF. "
"Disabling heap tracing configuration."
)
cg.add_define("USE_API") cg.add_define("USE_API")
cg.add_global(api_ns.using) cg.add_global(api_ns.using)

View File

@ -30,6 +30,11 @@ void APIServer::setup() {
ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server..."); ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
this->setup_controller(); this->setup_controller();
#ifdef USE_API_HEAP_TRACE
ESP_LOGI(TAG, "Initializing heap tracing");
start_heap_trace();
#endif
#ifdef USE_API_NOISE #ifdef USE_API_NOISE
uint32_t hash = 88491486UL; uint32_t hash = 88491486UL;
@ -154,6 +159,21 @@ void APIServer::loop() {
this->status_clear_warning(); this->status_clear_warning();
} }
} }
#ifdef USE_API_HEAP_TRACE
// Periodically dump heap trace information (every 30 seconds)
static uint32_t last_heap_trace_dump = 0;
const uint32_t now = millis();
if (now - last_heap_trace_dump > 30000) { // 30 seconds
ESP_LOGI(TAG, "Dumping heap trace information");
stop_and_dump_heap_trace();
// Start a new trace for the next period
start_heap_trace();
last_heap_trace_dump = now;
}
#endif
} }
void APIServer::dump_config() { void APIServer::dump_config() {
@ -466,6 +486,12 @@ void APIServer::on_shutdown() {
c->send_disconnect_request(DisconnectRequest()); c->send_disconnect_request(DisconnectRequest());
} }
delay(10); delay(10);
#ifdef USE_API_HEAP_TRACE
// 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();
#endif
} }
} // namespace api } // namespace api

33
heap_trace_example.yaml Normal file
View File

@ -0,0 +1,33 @@
esphome:
name: esp32-heap-trace
platform: ESP32
board: esp32dev
# Use ESP-IDF framework which is required for heap tracing
framework:
type: esp-idf
version: recommended
# Enable logging
logger:
level: INFO
# Enable Home Assistant API
api:
# Enable heap tracing with the following configuration
heap_tracing:
# Use standalone tracing (vs system tracing)
standalone: true
# Number of trace records to keep (more records = more memory usage)
num_records: 100
# Enable OTA updates
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot in case wifi connection fails
ap:
ssid: "Esp32-Heap-Trace"
password: "12345678"