mirror of
https://github.com/esphome/esphome.git
synced 2024-12-22 13:34:54 +01:00
Store source package in Component for debugging (#2070)
This commit is contained in:
parent
1c1ad32610
commit
1b89174558
7 changed files with 91 additions and 14 deletions
|
@ -19,7 +19,7 @@ void Application::register_component_(Component *comp) {
|
||||||
|
|
||||||
for (auto *c : this->components_) {
|
for (auto *c : this->components_) {
|
||||||
if (comp == c) {
|
if (comp == c) {
|
||||||
ESP_LOGW(TAG, "Component already registered! (%p)", c);
|
ESP_LOGW(TAG, "Component %s already registered! (%p)", c->get_component_source(), c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,23 +66,19 @@ void Application::setup() {
|
||||||
}
|
}
|
||||||
void Application::loop() {
|
void Application::loop() {
|
||||||
uint32_t new_app_state = 0;
|
uint32_t new_app_state = 0;
|
||||||
const uint32_t start = millis();
|
|
||||||
|
|
||||||
this->scheduler.call();
|
this->scheduler.call();
|
||||||
for (Component *component : this->looping_components_) {
|
for (Component *component : this->looping_components_) {
|
||||||
|
{
|
||||||
|
WarnIfComponentBlockingGuard guard{component};
|
||||||
component->call();
|
component->call();
|
||||||
|
}
|
||||||
new_app_state |= component->get_component_state();
|
new_app_state |= component->get_component_state();
|
||||||
this->app_state_ |= new_app_state;
|
this->app_state_ |= new_app_state;
|
||||||
this->feed_wdt();
|
this->feed_wdt();
|
||||||
}
|
}
|
||||||
this->app_state_ = new_app_state;
|
this->app_state_ = new_app_state;
|
||||||
|
|
||||||
const uint32_t end = millis();
|
|
||||||
if (end - start > 200) {
|
|
||||||
ESP_LOGV(TAG, "A component took a long time in a loop() cycle (%.2f s).", (end - start) / 1e3f);
|
|
||||||
ESP_LOGV(TAG, "Components should block for at most 20-30ms in loop().");
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t now = millis();
|
const uint32_t now = millis();
|
||||||
|
|
||||||
if (HighFrequencyLoopRequester::is_high_frequency()) {
|
if (HighFrequencyLoopRequester::is_high_frequency()) {
|
||||||
|
|
|
@ -92,8 +92,13 @@ void Component::call() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const char *Component::get_component_source() const {
|
||||||
|
if (this->component_source_ == nullptr)
|
||||||
|
return "<unknown>";
|
||||||
|
return this->component_source_;
|
||||||
|
}
|
||||||
void Component::mark_failed() {
|
void Component::mark_failed() {
|
||||||
ESP_LOGE(TAG, "Component was marked as failed.");
|
ESP_LOGE(TAG, "Component %s was marked as failed.", this->get_component_source());
|
||||||
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
this->component_state_ &= ~COMPONENT_STATE_MASK;
|
||||||
this->component_state_ |= COMPONENT_STATE_FAILED;
|
this->component_state_ |= COMPONENT_STATE_FAILED;
|
||||||
this->status_set_error();
|
this->status_set_error();
|
||||||
|
@ -190,4 +195,18 @@ uint32_t Nameable::get_object_id_hash() { return this->object_id_hash_; }
|
||||||
bool Nameable::is_disabled_by_default() const { return this->disabled_by_default_; }
|
bool Nameable::is_disabled_by_default() const { return this->disabled_by_default_; }
|
||||||
void Nameable::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; }
|
void Nameable::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; }
|
||||||
|
|
||||||
|
WarnIfComponentBlockingGuard::WarnIfComponentBlockingGuard(Component *component) {
|
||||||
|
component_ = component;
|
||||||
|
started_ = millis();
|
||||||
|
}
|
||||||
|
WarnIfComponentBlockingGuard::~WarnIfComponentBlockingGuard() {
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (now - started_ > 50) {
|
||||||
|
const char *src = component_ == nullptr ? "<null>" : component_->get_component_source();
|
||||||
|
ESP_LOGV(TAG, "Component %s took a long time for an operation (%.2f s).", src, (now - started_) / 1e3f);
|
||||||
|
ESP_LOGV(TAG, "Components should block for at most 20-30ms.");
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -130,6 +130,17 @@ class Component {
|
||||||
|
|
||||||
bool has_overridden_loop() const;
|
bool has_overridden_loop() const;
|
||||||
|
|
||||||
|
/** Set where this component was loaded from for some debug messages.
|
||||||
|
*
|
||||||
|
* This is set by the ESPHome core, and should not be called manually.
|
||||||
|
*/
|
||||||
|
void set_component_source(const char *source) { component_source_ = source; }
|
||||||
|
/** Get the integration where this component was declared as a string.
|
||||||
|
*
|
||||||
|
* Returns "<unknown>" if source not set
|
||||||
|
*/
|
||||||
|
const char *get_component_source() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void call_loop();
|
virtual void call_loop();
|
||||||
virtual void call_setup();
|
virtual void call_setup();
|
||||||
|
@ -201,6 +212,7 @@ class Component {
|
||||||
|
|
||||||
uint32_t component_state_{0x0000}; ///< State of this component.
|
uint32_t component_state_{0x0000}; ///< State of this component.
|
||||||
float setup_priority_override_{NAN};
|
float setup_priority_override_{NAN};
|
||||||
|
const char *component_source_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** This class simplifies creating components that periodically check a state.
|
/** This class simplifies creating components that periodically check a state.
|
||||||
|
@ -276,4 +288,14 @@ class Nameable {
|
||||||
bool disabled_by_default_{false};
|
bool disabled_by_default_{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class WarnIfComponentBlockingGuard {
|
||||||
|
public:
|
||||||
|
WarnIfComponentBlockingGuard(Component *component);
|
||||||
|
~WarnIfComponentBlockingGuard();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t started_;
|
||||||
|
Component *component_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
|
@ -155,8 +155,11 @@ void ICACHE_RAM_ATTR HOT Scheduler::call() {
|
||||||
// Warning: During f(), a lot of stuff can happen, including:
|
// Warning: During f(), a lot of stuff can happen, including:
|
||||||
// - timeouts/intervals get added, potentially invalidating vector pointers
|
// - timeouts/intervals get added, potentially invalidating vector pointers
|
||||||
// - timeouts/intervals get cancelled
|
// - timeouts/intervals get cancelled
|
||||||
|
{
|
||||||
|
WarnIfComponentBlockingGuard guard{item->component};
|
||||||
item->f();
|
item->f();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// new scope, item from before might have been moved in the vector
|
// new scope, item from before might have been moved in the vector
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import logging
|
||||||
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_INVERTED,
|
CONF_INVERTED,
|
||||||
CONF_MODE,
|
CONF_MODE,
|
||||||
|
@ -15,6 +17,9 @@ from esphome.cpp_types import App, GPIOPin
|
||||||
from esphome.util import Registry, RegistryEntry
|
from esphome.util import Registry, RegistryEntry
|
||||||
|
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
async def gpio_pin_expression(conf):
|
async def gpio_pin_expression(conf):
|
||||||
"""Generate an expression for the given pin option.
|
"""Generate an expression for the given pin option.
|
||||||
|
|
||||||
|
@ -42,6 +47,8 @@ async def register_component(var, config):
|
||||||
:param var: The variable representing the component.
|
:param var: The variable representing the component.
|
||||||
:param config: The configuration for the component.
|
:param config: The configuration for the component.
|
||||||
"""
|
"""
|
||||||
|
import inspect
|
||||||
|
|
||||||
id_ = str(var.base)
|
id_ = str(var.base)
|
||||||
if id_ not in CORE.component_ids:
|
if id_ not in CORE.component_ids:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
@ -54,6 +61,32 @@ async def register_component(var, config):
|
||||||
add(var.set_setup_priority(config[CONF_SETUP_PRIORITY]))
|
add(var.set_setup_priority(config[CONF_SETUP_PRIORITY]))
|
||||||
if CONF_UPDATE_INTERVAL in config:
|
if CONF_UPDATE_INTERVAL in config:
|
||||||
add(var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
|
add(var.set_update_interval(config[CONF_UPDATE_INTERVAL]))
|
||||||
|
|
||||||
|
# Set component source by inspecting the stack and getting the callee module
|
||||||
|
# https://stackoverflow.com/a/1095621
|
||||||
|
name = None
|
||||||
|
try:
|
||||||
|
for frm in inspect.stack()[1:]:
|
||||||
|
mod = inspect.getmodule(frm[0])
|
||||||
|
if mod is None:
|
||||||
|
continue
|
||||||
|
name = mod.__name__
|
||||||
|
if name.startswith("esphome.components."):
|
||||||
|
name = name[len("esphome.components.") :]
|
||||||
|
break
|
||||||
|
if name == "esphome.automation":
|
||||||
|
name = "automation"
|
||||||
|
# continue looking further up in stack in case we find a better one
|
||||||
|
if name == "esphome.coroutine":
|
||||||
|
# Only works for async-await coroutine syntax
|
||||||
|
break
|
||||||
|
except (KeyError, AttributeError, IndexError) as e:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"Error while finding name of component, please report this", exc_info=e
|
||||||
|
)
|
||||||
|
if name is not None:
|
||||||
|
add(var.set_component_source(name))
|
||||||
|
|
||||||
add(App.register_component(var))
|
add(App.register_component(var))
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
|
@ -473,7 +473,11 @@ class TestLibrary:
|
||||||
("__eq__", core.Library(name="libfoo", version="1.2.3"), True),
|
("__eq__", core.Library(name="libfoo", version="1.2.3"), True),
|
||||||
("__eq__", core.Library(name="libfoo", version="1.2.4"), False),
|
("__eq__", core.Library(name="libfoo", version="1.2.4"), False),
|
||||||
("__eq__", core.Library(name="libbar", version="1.2.3"), False),
|
("__eq__", core.Library(name="libbar", version="1.2.3"), False),
|
||||||
("__eq__", core.Library(name="libbar", version=None, repository="file:///test"), False),
|
(
|
||||||
|
"__eq__",
|
||||||
|
core.Library(name="libbar", version=None, repository="file:///test"),
|
||||||
|
False,
|
||||||
|
),
|
||||||
("__eq__", 1000, NotImplemented),
|
("__eq__", 1000, NotImplemented),
|
||||||
("__eq__", "1000", NotImplemented),
|
("__eq__", "1000", NotImplemented),
|
||||||
("__eq__", True, NotImplemented),
|
("__eq__", True, NotImplemented),
|
||||||
|
|
|
@ -38,7 +38,7 @@ async def test_register_component(monkeypatch):
|
||||||
actual = await ch.register_component(var, {})
|
actual = await ch.register_component(var, {})
|
||||||
|
|
||||||
assert actual is var
|
assert actual is var
|
||||||
add_mock.assert_called_once()
|
assert add_mock.call_count == 2
|
||||||
app_mock.register_component.assert_called_with(var)
|
app_mock.register_component.assert_called_with(var)
|
||||||
assert core_mock.component_ids == []
|
assert core_mock.component_ids == []
|
||||||
|
|
||||||
|
@ -77,6 +77,6 @@ async def test_register_component__with_setup_priority(monkeypatch):
|
||||||
|
|
||||||
assert actual is var
|
assert actual is var
|
||||||
add_mock.assert_called()
|
add_mock.assert_called()
|
||||||
assert add_mock.call_count == 3
|
assert add_mock.call_count == 4
|
||||||
app_mock.register_component.assert_called_with(var)
|
app_mock.register_component.assert_called_with(var)
|
||||||
assert core_mock.component_ids == []
|
assert core_mock.component_ids == []
|
||||||
|
|
Loading…
Reference in a new issue