Fix profiler dumping object when repr fails (#67674)

* Fix profiler dumping object when repr fails

* cover
This commit is contained in:
J. Nick Koston 2022-03-08 10:33:08 +01:00 committed by GitHub
parent ee38203e1d
commit bfe94f1cc5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 6 deletions

View File

@ -8,6 +8,7 @@ import sys
import threading
import time
import traceback
from typing import Any
from guppy import hpy
import objgraph
@ -87,13 +88,24 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
persistent_notification.async_dismiss(hass, "profile_object_logging")
domain_data.pop(LOG_INTERVAL_SUB)()
def _safe_repr(obj: Any) -> str:
"""Get the repr of an object but keep going if there is an exception.
We wrap repr to ensure if one object cannot be serialized, we can
still get the rest.
"""
try:
return repr(obj)
except Exception: # pylint: disable=broad-except
return f"Failed to serialize {type(obj)}"
def _dump_log_objects(call: ServiceCall) -> None:
obj_type = call.data[CONF_TYPE]
_LOGGER.critical(
"%s objects in memory: %s",
obj_type,
objgraph.by_type(obj_type),
[_safe_repr(obj) for obj in objgraph.by_type(obj_type)],
)
persistent_notification.create(

View File

@ -131,19 +131,31 @@ async def test_dump_log_object(hass, caplog):
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
class DumpLogDummy:
def __init__(self, fail):
self.fail = fail
def __repr__(self):
if self.fail:
raise Exception("failed")
return "<DumpLogDummy success>"
obj1 = DumpLogDummy(False)
obj2 = DumpLogDummy(True)
assert hass.services.has_service(DOMAIN, SERVICE_DUMP_LOG_OBJECTS)
await hass.services.async_call(
DOMAIN, SERVICE_DUMP_LOG_OBJECTS, {CONF_TYPE: "MockConfigEntry"}
DOMAIN, SERVICE_DUMP_LOG_OBJECTS, {CONF_TYPE: "DumpLogDummy"}
)
await hass.async_block_till_done()
assert "MockConfigEntry" in caplog.text
assert "<DumpLogDummy success>" in caplog.text
assert "Failed to serialize" in caplog.text
del obj1
del obj2
caplog.clear()
assert await hass.config_entries.async_unload(entry.entry_id)
await hass.async_block_till_done()
async def test_log_thread_frames(hass, caplog):
"""Test we can log thread frames."""