mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add guppy3 memory profile to Profiler integration (#42435)
* add guppy memory profile to profiler integration * add output path to notification * create new service for memory profile * address review comments
This commit is contained in:
parent
3fb091c4ea
commit
2a795c0397
@ -3,6 +3,7 @@ import asyncio
|
|||||||
import cProfile
|
import cProfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
|
from guppy import hpy
|
||||||
from pyprof2calltree import convert
|
from pyprof2calltree import convert
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ from homeassistant.helpers.typing import ConfigType
|
|||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
|
|
||||||
SERVICE_START = "start"
|
SERVICE_START = "start"
|
||||||
|
SERVICE_MEMORY = "memory"
|
||||||
CONF_SECONDS = "seconds"
|
CONF_SECONDS = "seconds"
|
||||||
|
|
||||||
|
|
||||||
@ -31,6 +33,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
async with lock:
|
async with lock:
|
||||||
await _async_generate_profile(hass, call)
|
await _async_generate_profile(hass, call)
|
||||||
|
|
||||||
|
async def _async_run_memory_profile(call: ServiceCall):
|
||||||
|
async with lock:
|
||||||
|
await _async_generate_memory_profile(hass, call)
|
||||||
|
|
||||||
async_register_admin_service(
|
async_register_admin_service(
|
||||||
hass,
|
hass,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
@ -41,6 +47,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
async_register_admin_service(
|
||||||
|
hass,
|
||||||
|
DOMAIN,
|
||||||
|
SERVICE_MEMORY,
|
||||||
|
_async_run_memory_profile,
|
||||||
|
schema=vol.Schema(
|
||||||
|
{vol.Optional(CONF_SECONDS, default=60.0): vol.Coerce(float)}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -53,7 +69,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
|||||||
async def _async_generate_profile(hass: HomeAssistant, call: ServiceCall):
|
async def _async_generate_profile(hass: HomeAssistant, call: ServiceCall):
|
||||||
start_time = int(time.time() * 1000000)
|
start_time = int(time.time() * 1000000)
|
||||||
hass.components.persistent_notification.async_create(
|
hass.components.persistent_notification.async_create(
|
||||||
"The profile started. This notification will be updated when it is complete.",
|
"The profile has started. This notification will be updated when it is complete.",
|
||||||
title="Profile Started",
|
title="Profile Started",
|
||||||
notification_id=f"profiler_{start_time}",
|
notification_id=f"profiler_{start_time}",
|
||||||
)
|
)
|
||||||
@ -74,7 +90,32 @@ async def _async_generate_profile(hass: HomeAssistant, call: ServiceCall):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _async_generate_memory_profile(hass: HomeAssistant, call: ServiceCall):
|
||||||
|
start_time = int(time.time() * 1000000)
|
||||||
|
hass.components.persistent_notification.async_create(
|
||||||
|
"The memory profile has started. This notification will be updated when it is complete.",
|
||||||
|
title="Profile Started",
|
||||||
|
notification_id=f"memory_profiler_{start_time}",
|
||||||
|
)
|
||||||
|
heap_profiler = hpy()
|
||||||
|
heap_profiler.setref()
|
||||||
|
await asyncio.sleep(float(call.data[CONF_SECONDS]))
|
||||||
|
heap = heap_profiler.heap()
|
||||||
|
|
||||||
|
heap_path = hass.config.path(f"heap_profile.{start_time}.hpy")
|
||||||
|
await hass.async_add_executor_job(_write_memory_profile, heap, heap_path)
|
||||||
|
hass.components.persistent_notification.async_create(
|
||||||
|
f"Wrote heapy memory profile to {heap_path}",
|
||||||
|
title="Profile Complete",
|
||||||
|
notification_id=f"memory_profiler_{start_time}",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _write_profile(profiler, cprofile_path, callgrind_path):
|
def _write_profile(profiler, cprofile_path, callgrind_path):
|
||||||
profiler.create_stats()
|
profiler.create_stats()
|
||||||
profiler.dump_stats(cprofile_path)
|
profiler.dump_stats(cprofile_path)
|
||||||
convert(profiler.getstats(), callgrind_path)
|
convert(profiler.getstats(), callgrind_path)
|
||||||
|
|
||||||
|
|
||||||
|
def _write_memory_profile(heap, heap_path):
|
||||||
|
heap.byrcs.dump(heap_path)
|
||||||
|
@ -2,12 +2,8 @@
|
|||||||
"domain": "profiler",
|
"domain": "profiler",
|
||||||
"name": "Profiler",
|
"name": "Profiler",
|
||||||
"documentation": "https://www.home-assistant.io/integrations/profiler",
|
"documentation": "https://www.home-assistant.io/integrations/profiler",
|
||||||
"requirements": [
|
"requirements": ["pyprof2calltree==1.4.5", "guppy3==3.1.0"],
|
||||||
"pyprof2calltree==1.4.5"
|
"codeowners": ["@bdraco"],
|
||||||
],
|
|
||||||
"codeowners": [
|
|
||||||
"@bdraco"
|
|
||||||
],
|
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"config_flow": true
|
"config_flow": true
|
||||||
}
|
}
|
@ -4,3 +4,9 @@ start:
|
|||||||
seconds:
|
seconds:
|
||||||
description: The number of seconds to run the profiler.
|
description: The number of seconds to run the profiler.
|
||||||
example: 60.0
|
example: 60.0
|
||||||
|
memory:
|
||||||
|
description: Start the Memory Profiler
|
||||||
|
fields:
|
||||||
|
seconds:
|
||||||
|
description: The number of seconds to run the memory profiler.
|
||||||
|
example: 60.0
|
||||||
|
@ -716,6 +716,9 @@ growattServer==0.1.1
|
|||||||
# homeassistant.components.gstreamer
|
# homeassistant.components.gstreamer
|
||||||
gstreamer-player==1.1.2
|
gstreamer-player==1.1.2
|
||||||
|
|
||||||
|
# homeassistant.components.profiler
|
||||||
|
guppy3==3.1.0
|
||||||
|
|
||||||
# homeassistant.components.ffmpeg
|
# homeassistant.components.ffmpeg
|
||||||
ha-ffmpeg==2.0
|
ha-ffmpeg==2.0
|
||||||
|
|
||||||
|
@ -360,6 +360,9 @@ greeclimate==0.9.0
|
|||||||
# homeassistant.components.griddy
|
# homeassistant.components.griddy
|
||||||
griddypower==0.1.0
|
griddypower==0.1.0
|
||||||
|
|
||||||
|
# homeassistant.components.profiler
|
||||||
|
guppy3==3.1.0
|
||||||
|
|
||||||
# homeassistant.components.ffmpeg
|
# homeassistant.components.ffmpeg
|
||||||
ha-ffmpeg==2.0
|
ha-ffmpeg==2.0
|
||||||
|
|
||||||
|
@ -2,7 +2,11 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from homeassistant import setup
|
from homeassistant import setup
|
||||||
from homeassistant.components.profiler import CONF_SECONDS, SERVICE_START
|
from homeassistant.components.profiler import (
|
||||||
|
CONF_SECONDS,
|
||||||
|
SERVICE_MEMORY,
|
||||||
|
SERVICE_START,
|
||||||
|
)
|
||||||
from homeassistant.components.profiler.const import DOMAIN
|
from homeassistant.components.profiler.const import DOMAIN
|
||||||
|
|
||||||
from tests.async_mock import patch
|
from tests.async_mock import patch
|
||||||
@ -39,3 +43,35 @@ async def test_basic_usage(hass, tmpdir):
|
|||||||
|
|
||||||
assert await hass.config_entries.async_unload(entry.entry_id)
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
|
||||||
|
async def test_memory_usage(hass, tmpdir):
|
||||||
|
"""Test we can setup and the service is registered."""
|
||||||
|
test_dir = tmpdir.mkdir("profiles")
|
||||||
|
|
||||||
|
await setup.async_setup_component(hass, "persistent_notification", {})
|
||||||
|
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()
|
||||||
|
|
||||||
|
assert hass.services.has_service(DOMAIN, SERVICE_MEMORY)
|
||||||
|
|
||||||
|
last_filename = None
|
||||||
|
|
||||||
|
def _mock_path(filename):
|
||||||
|
nonlocal last_filename
|
||||||
|
last_filename = f"{test_dir}/{filename}"
|
||||||
|
return last_filename
|
||||||
|
|
||||||
|
with patch("homeassistant.components.profiler.hpy") as mock_hpy, patch.object(
|
||||||
|
hass.config, "path", _mock_path
|
||||||
|
):
|
||||||
|
await hass.services.async_call(DOMAIN, SERVICE_MEMORY, {CONF_SECONDS: 0.000001})
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
mock_hpy.assert_called_once()
|
||||||
|
|
||||||
|
assert await hass.config_entries.async_unload(entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user