ESPHome 2026.5.1
Loading...
Searching...
No Matches
runtime_stats.h
Go to the documentation of this file.
1#pragma once
2
4
5#ifdef USE_RUNTIME_STATS
6
7#include <cstdint>
8#include "esphome/core/hal.h"
10#include "esphome/core/log.h"
11
12namespace esphome {
13
14class Component; // Forward declaration
15
16namespace runtime_stats {
17
18static const char *const TAG = "runtime_stats";
19
21 public:
23
24 void set_log_interval(uint32_t log_interval) {
25 this->log_interval_ = log_interval;
26 this->next_log_time_ = millis() + log_interval;
27 }
28 uint32_t get_log_interval() const { return this->log_interval_; }
29
30 // Process any pending stats printing. Called on every Application::loop()
31 // tick, so the common "not yet time to log" path must be cheap — inline
32 // the gate check and keep the actual logging work out-of-line.
33 void ESPHOME_ALWAYS_INLINE process_pending_stats(uint32_t current_time) {
34 if ((int32_t) (current_time - this->next_log_time_) >= 0) [[unlikely]] {
35 this->process_pending_stats_slow_(current_time);
36 }
37 }
38
39 // Record the wall time of one main loop iteration excluding the yield/sleep.
40 // Called once per loop from Application::loop().
41 // active_us = total time between loop start and just before yield.
42 // before_us = time spent in Phase A (scheduler tick) excluding time
43 // already attributed to per-component stats.
44 // tail_us = time spent in after_component_phase_ + the trailing record/stats
45 // prefix. Only meaningful on component-phase ticks; reported
46 // as 0 on Phase A-only ticks (no component phase ran, so any
47 // overhead between Phase A and stats belongs to "residual").
48 // Residual overhead at log time = active − Σ(component) − before − tail,
49 // which captures per-iteration inter-component bookkeeping (set_current_component,
50 // WarnIfComponentBlockingGuard construction/destruction, feed_wdt_with_time calls,
51 // the for-loop itself).
52 void record_loop_active(uint32_t active_us, uint32_t before_us, uint32_t tail_us) {
54 this->period_active_time_us_ += active_us;
55 if (active_us > this->period_active_max_us_)
56 this->period_active_max_us_ = active_us;
57 this->total_active_count_++;
58 this->total_active_time_us_ += active_us;
59 if (active_us > this->total_active_max_us_)
60 this->total_active_max_us_ = active_us;
61
62 this->period_before_time_us_ += before_us;
63 this->total_before_time_us_ += before_us;
64 this->period_tail_time_us_ += tail_us;
65 this->total_tail_time_us_ += tail_us;
66 }
67
68 protected:
69 void process_pending_stats_slow_(uint32_t current_time);
70 void log_stats_();
71 // Static comparators — member functions have friend access, lambdas do not
72 static bool compare_period_time(Component *a, Component *b);
73 static bool compare_total_time(Component *a, Component *b);
74
77
78 // Main loop active-time stats (wall time per iteration, excluding yield/sleep).
79 // Counters are uint64_t — at sub-millisecond loop times a uint32_t can wrap in
80 // a few weeks of uptime, which is well within ESPHome device lifetimes.
87
88 // Split of overhead sections — accumulated per iteration.
93};
94
95} // namespace runtime_stats
96
97extern runtime_stats::RuntimeStatsCollector
98 *global_runtime_stats; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
99
100} // namespace esphome
101
102#endif // USE_RUNTIME_STATS
void set_log_interval(uint32_t log_interval)
static bool compare_total_time(Component *a, Component *b)
void process_pending_stats_slow_(uint32_t current_time)
static bool compare_period_time(Component *a, Component *b)
void ESPHOME_ALWAYS_INLINE process_pending_stats(uint32_t current_time)
void record_loop_active(uint32_t active_us, uint32_t before_us, uint32_t tail_us)
runtime_stats::RuntimeStatsCollector * global_runtime_stats
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
static void uint32_t