This commit is contained in:
J. Nick Koston 2025-07-08 09:25:00 -06:00
parent a3c8f667a7
commit f2ac6b0af6
No known key found for this signature in database
6 changed files with 39 additions and 22 deletions

View File

@ -4,13 +4,18 @@ Runtime statistics component for ESPHome.
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.const import CONF_ID
DEPENDENCIES = []
CONF_LOG_INTERVAL = "log_interval"
runtime_stats_ns = cg.esphome_ns.namespace("runtime_stats")
RuntimeStatsCollector = runtime_stats_ns.class_("RuntimeStatsCollector")
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.declare_id(RuntimeStatsCollector),
cv.Optional(
CONF_LOG_INTERVAL, default=60000
): cv.positive_time_period_milliseconds,
@ -32,4 +37,7 @@ async def to_code(config):
# Define USE_RUNTIME_STATS when this component is used
cg.add_define("USE_RUNTIME_STATS")
cg.add(cg.App.set_runtime_stats_log_interval(config[CONF_LOG_INTERVAL]))
# Create the runtime stats instance (constructor sets global_runtime_stats)
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_log_interval(config[CONF_LOG_INTERVAL]))

View File

@ -7,7 +7,11 @@
namespace esphome {
RuntimeStatsCollector runtime_stats;
namespace runtime_stats {
RuntimeStatsCollector::RuntimeStatsCollector() : log_interval_(60000), next_log_time_(0) {
global_runtime_stats = this;
}
void RuntimeStatsCollector::record_component_time(Component *component, uint32_t duration_ms, uint32_t current_time) {
if (component == nullptr)
@ -35,8 +39,8 @@ void RuntimeStatsCollector::record_component_time(Component *component, uint32_t
}
void RuntimeStatsCollector::log_stats_() {
ESP_LOGI(RUNTIME_TAG, "Component Runtime Statistics");
ESP_LOGI(RUNTIME_TAG, "Period stats (last %" PRIu32 "ms):", this->log_interval_);
ESP_LOGI(TAG, "Component Runtime Statistics");
ESP_LOGI(TAG, "Period stats (last %" PRIu32 "ms):", this->log_interval_);
// First collect stats we want to display
std::vector<ComponentStatPair> stats_to_display;
@ -57,13 +61,13 @@ void RuntimeStatsCollector::log_stats_() {
const std::string &source = it.name;
const ComponentRuntimeStats *stats = it.stats;
ESP_LOGI(RUNTIME_TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms", source.c_str(),
ESP_LOGI(TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms", source.c_str(),
stats->get_period_count(), stats->get_period_avg_time_ms(), stats->get_period_max_time_ms(),
stats->get_period_time_ms());
}
// Log total stats since boot
ESP_LOGI(RUNTIME_TAG, "Total stats (since boot):");
ESP_LOGI(TAG, "Total stats (since boot):");
// Re-sort by total runtime for all-time stats
std::sort(stats_to_display.begin(), stats_to_display.end(),
@ -75,7 +79,7 @@ void RuntimeStatsCollector::log_stats_() {
const std::string &source = it.name;
const ComponentRuntimeStats *stats = it.stats;
ESP_LOGI(RUNTIME_TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms", source.c_str(),
ESP_LOGI(TAG, " %s: count=%" PRIu32 ", avg=%.2fms, max=%" PRIu32 "ms, total=%" PRIu32 "ms", source.c_str(),
stats->get_total_count(), stats->get_total_avg_time_ms(), stats->get_total_max_time_ms(),
stats->get_total_time_ms());
}
@ -92,6 +96,11 @@ void RuntimeStatsCollector::process_pending_stats(uint32_t current_time) {
}
}
} // namespace runtime_stats
runtime_stats::RuntimeStatsCollector *global_runtime_stats =
nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace esphome
#endif // USE_RUNTIME_STATS

View File

@ -12,10 +12,12 @@
namespace esphome {
static const char *const RUNTIME_TAG = "runtime";
class Component; // Forward declaration
namespace runtime_stats {
static const char *const TAG = "runtime_stats";
class ComponentRuntimeStats {
public:
ComponentRuntimeStats()
@ -87,7 +89,7 @@ struct ComponentStatPair {
class RuntimeStatsCollector {
public:
RuntimeStatsCollector() : log_interval_(60000), next_log_time_(0) {}
RuntimeStatsCollector();
void set_log_interval(uint32_t log_interval) { this->log_interval_ = log_interval; }
uint32_t get_log_interval() const { return this->log_interval_; }
@ -113,8 +115,10 @@ class RuntimeStatsCollector {
uint32_t next_log_time_;
};
// Global instance for runtime stats collection
extern RuntimeStatsCollector runtime_stats;
} // namespace runtime_stats
extern runtime_stats::RuntimeStatsCollector
*global_runtime_stats; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
} // namespace esphome

View File

@ -144,7 +144,9 @@ void Application::loop() {
#ifdef USE_RUNTIME_STATS
// Process any pending runtime stats printing after all components have run
// This ensures stats printing doesn't affect component timing measurements
runtime_stats.process_pending_stats(last_op_end_time);
if (global_runtime_stats != nullptr) {
global_runtime_stats->process_pending_stats(last_op_end_time);
}
#endif
// Use the last component's end time instead of calling millis() again

View File

@ -351,14 +351,6 @@ class Application {
uint32_t get_loop_interval() const { return static_cast<uint32_t>(this->loop_interval_); }
#ifdef USE_RUNTIME_STATS
/** Set the interval at which runtime statistics are logged.
*
* @param interval The interval in milliseconds between logging of runtime statistics.
*/
void set_runtime_stats_log_interval(uint32_t interval) { runtime_stats.set_log_interval(interval); }
#endif
void schedule_dump_config() { this->dump_config_at_ = 0; }
void feed_wdt(uint32_t time = 0);

View File

@ -398,7 +398,9 @@ uint32_t WarnIfComponentBlockingGuard::finish() {
#ifdef USE_RUNTIME_STATS
// Record component runtime stats
runtime_stats.record_component_time(this->component_, blocking_time, curr_time);
if (global_runtime_stats != nullptr) {
global_runtime_stats->record_component_time(this->component_, blocking_time, curr_time);
}
#endif
bool should_warn;
if (this->component_ != nullptr) {