Add profiler service for dumping sockets used by HA (#152440)

Co-authored-by: Stefan Agner <stefan@agner.ch>
This commit is contained in:
Jan Čermák
2025-09-23 12:55:29 +02:00
committed by GitHub
parent ce363b3835
commit 21d4ed2837
5 changed files with 57 additions and 0 deletions

View File

@@ -35,6 +35,7 @@ SERVICE_STOP_LOG_OBJECTS = "stop_log_objects"
SERVICE_START_LOG_OBJECT_SOURCES = "start_log_object_sources"
SERVICE_STOP_LOG_OBJECT_SOURCES = "stop_log_object_sources"
SERVICE_DUMP_LOG_OBJECTS = "dump_log_objects"
SERVICE_DUMP_SOCKETS = "dump_sockets"
SERVICE_LRU_STATS = "lru_stats"
SERVICE_LOG_THREAD_FRAMES = "log_thread_frames"
SERVICE_LOG_EVENT_LOOP_SCHEDULED = "log_event_loop_scheduled"
@@ -231,6 +232,15 @@ async def async_setup_entry( # noqa: C901
notification_id="profile_lru_stats",
)
def _dump_sockets(call: ServiceCall) -> None:
"""Dump list of all currently existing sockets to the log."""
import objgraph # noqa: PLC0415
_LOGGER.critical(
"Sockets used by Home Assistant:\n%s",
"\n".join(repr(sock) for sock in objgraph.by_type("socket")),
)
async def _async_dump_thread_frames(call: ServiceCall) -> None:
"""Log all thread frames."""
frames = sys._current_frames() # noqa: SLF001
@@ -346,6 +356,13 @@ async def async_setup_entry( # noqa: C901
schema=vol.Schema({vol.Required(CONF_TYPE): str}),
)
async_register_admin_service(
hass,
DOMAIN,
SERVICE_DUMP_SOCKETS,
_dump_sockets,
)
async_register_admin_service(
hass,
DOMAIN,

View File

@@ -15,6 +15,9 @@
"dump_log_objects": {
"service": "mdi:invoice-export-outline"
},
"dump_sockets": {
"service": "mdi:pipe"
},
"start_log_object_sources": {
"service": "mdi:play"
},

View File

@@ -51,6 +51,7 @@ start_log_object_sources:
unit_of_measurement: objects
stop_log_object_sources:
lru_stats:
dump_sockets:
log_thread_frames:
log_event_loop_scheduled:
set_asyncio_debug:

View File

@@ -65,6 +65,10 @@
}
}
},
"dump_sockets": {
"name": "Dump used sockets",
"description": "Logs information about all currently used sockets."
},
"stop_log_object_sources": {
"name": "Stop logging object sources",
"description": "Stops logging sources of new objects in memory."

View File

@@ -5,6 +5,7 @@ from functools import lru_cache
import logging
import os
from pathlib import Path
import socket
from unittest.mock import patch
from freezegun.api import FrozenDateTimeFactory
@@ -18,6 +19,7 @@ from homeassistant.components.profiler import (
CONF_ENABLED,
CONF_SECONDS,
SERVICE_DUMP_LOG_OBJECTS,
SERVICE_DUMP_SOCKETS,
SERVICE_LOG_CURRENT_TASKS,
SERVICE_LOG_EVENT_LOOP_SCHEDULED,
SERVICE_LOG_THREAD_FRAMES,
@@ -271,6 +273,36 @@ async def test_log_scheduled(
await hass.async_block_till_done()
@pytest.mark.usefixtures("socket_enabled")
async def test_dump_sockets(
hass: HomeAssistant, caplog: pytest.LogCaptureFixture
) -> None:
"""Test dumping of sockets to the log."""
entry = MockConfigEntry(domain=DOMAIN)
entry.add_to_hass(hass)
assert await hass.config_entries.async_setup(entry.entry_id)
await hass.async_block_till_done()
caplog.clear()
sock = None
try:
# Try to bind ephemeral UDP port on localhost for testing
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("127.0.0.1", 0))
port = sock.getsockname()[1]
assert hass.services.has_service(DOMAIN, SERVICE_DUMP_SOCKETS)
await hass.services.async_call(DOMAIN, SERVICE_DUMP_SOCKETS, blocking=True)
finally:
if sock:
sock.close()
assert "Sockets used by Home Assistant" in caplog.text
assert f"laddr=('127.0.0.1', {port})" in caplog.text
async def test_lru_stats(hass: HomeAssistant, caplog: pytest.LogCaptureFixture) -> None:
"""Test logging lru stats."""