mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +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 time
|
||||
|
||||
from guppy import hpy
|
||||
from pyprof2calltree import convert
|
||||
import voluptuous as vol
|
||||
|
||||
@ -14,6 +15,7 @@ from homeassistant.helpers.typing import ConfigType
|
||||
from .const import DOMAIN
|
||||
|
||||
SERVICE_START = "start"
|
||||
SERVICE_MEMORY = "memory"
|
||||
CONF_SECONDS = "seconds"
|
||||
|
||||
|
||||
@ -31,6 +33,10 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async with lock:
|
||||
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(
|
||||
hass,
|
||||
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
|
||||
|
||||
|
||||
@ -53,7 +69,7 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async def _async_generate_profile(hass: HomeAssistant, call: ServiceCall):
|
||||
start_time = int(time.time() * 1000000)
|
||||
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",
|
||||
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):
|
||||
profiler.create_stats()
|
||||
profiler.dump_stats(cprofile_path)
|
||||
convert(profiler.getstats(), callgrind_path)
|
||||
|
||||
|
||||
def _write_memory_profile(heap, heap_path):
|
||||
heap.byrcs.dump(heap_path)
|
||||
|
@ -2,12 +2,8 @@
|
||||
"domain": "profiler",
|
||||
"name": "Profiler",
|
||||
"documentation": "https://www.home-assistant.io/integrations/profiler",
|
||||
"requirements": [
|
||||
"pyprof2calltree==1.4.5"
|
||||
],
|
||||
"codeowners": [
|
||||
"@bdraco"
|
||||
],
|
||||
"requirements": ["pyprof2calltree==1.4.5", "guppy3==3.1.0"],
|
||||
"codeowners": ["@bdraco"],
|
||||
"quality_scale": "internal",
|
||||
"config_flow": true
|
||||
}
|
||||
}
|
||||
|
@ -4,3 +4,9 @@ start:
|
||||
seconds:
|
||||
description: The number of seconds to run the profiler.
|
||||
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
|
||||
gstreamer-player==1.1.2
|
||||
|
||||
# homeassistant.components.profiler
|
||||
guppy3==3.1.0
|
||||
|
||||
# homeassistant.components.ffmpeg
|
||||
ha-ffmpeg==2.0
|
||||
|
||||
|
@ -360,6 +360,9 @@ greeclimate==0.9.0
|
||||
# homeassistant.components.griddy
|
||||
griddypower==0.1.0
|
||||
|
||||
# homeassistant.components.profiler
|
||||
guppy3==3.1.0
|
||||
|
||||
# homeassistant.components.ffmpeg
|
||||
ha-ffmpeg==2.0
|
||||
|
||||
|
@ -2,7 +2,11 @@
|
||||
import os
|
||||
|
||||
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 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)
|
||||
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