Skip to content

Commit 3f0cfc8

Browse files
authored
Merge pull request #230 from maurycy/tachyon
2 parents ddb748a + 5791a4e commit 3f0cfc8

3 files changed

Lines changed: 85 additions & 0 deletions

File tree

pyperf/_hooks.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55

66
import abc
77
import importlib.metadata
8+
import os
89
import os.path
910
import shlex
11+
import signal
1012
import subprocess
1113
import sys
1214
import tempfile
@@ -168,3 +170,84 @@ def exec_perf_cmd(self, cmd):
168170
self.ctl_fd.write(f"{cmd}\n")
169171
self.ctl_fd.flush()
170172
self.ack_fd.readline()
173+
174+
175+
class tachyon(HookBase):
176+
"""Profile the benchmark using sampling profiler (Tachyon).
177+
178+
The value of the `PYPERF_TACHYON_OPTS` environment variable is
179+
appended to the `profiling.sampling attach` command line.
180+
181+
This hook does not generate output filenames. Use -o or --output in
182+
`PYPERF_TACHYON_OPTS` to control output. For most formats, -o can
183+
point at an existing directory and the profiler will auto-generate a
184+
filename inside it.
185+
186+
Configuration environment variables:
187+
PYPERF_TACHYON_OPTS: Extra arguments passed to
188+
`python -m profiling.sampling attach`.
189+
"""
190+
191+
def __init__(self):
192+
if sys.platform == "win32":
193+
raise HookError("tachyon hook is not supported on Windows")
194+
195+
if sys.version_info < (3, 15):
196+
raise HookError(
197+
"tachyon hook requires Python 3.15+, "
198+
"current version: %s.%s"
199+
% (sys.version_info.major, sys.version_info.minor)
200+
)
201+
202+
try:
203+
import profiling.sampling # noqa: F401
204+
except ImportError:
205+
raise HookError("profiling.sampling module not available")
206+
207+
self.extra_opts = os.environ.get("PYPERF_TACHYON_OPTS", "")
208+
209+
self._proc = None
210+
211+
def __enter__(self):
212+
if self._proc is not None:
213+
self._stop_profiler()
214+
215+
cmd = [
216+
sys.executable,
217+
"-m", "profiling.sampling",
218+
"attach",
219+
str(os.getpid()),
220+
]
221+
cmd += shlex.split(self.extra_opts)
222+
223+
self._proc = subprocess.Popen(
224+
cmd,
225+
stdout=subprocess.DEVNULL,
226+
stderr=subprocess.DEVNULL,
227+
)
228+
229+
def __exit__(self, _exc_type, _exc_value, _traceback):
230+
self._stop_profiler()
231+
232+
def _stop_profiler(self):
233+
if not self._proc:
234+
return
235+
236+
if self._proc.poll() is None:
237+
self._proc.send_signal(signal.SIGINT)
238+
try:
239+
self._proc.wait(timeout=30)
240+
except subprocess.TimeoutExpired:
241+
self._proc.terminate()
242+
try:
243+
self._proc.wait(timeout=5)
244+
except subprocess.TimeoutExpired:
245+
self._proc.kill()
246+
self._proc.wait()
247+
248+
self._proc = None
249+
250+
def teardown(self, metadata):
251+
self._stop_profiler()
252+
if self.extra_opts:
253+
metadata["tachyon_extra_opts"] = self.extra_opts

pyperf/_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ def create_environ(inherit_environ, locale, copy_all):
264264
"PYTHONPATH", "PYTHON_CPU_COUNT", "PYTHON_GIL",
265265
# Pyperf specific variables
266266
"PYPERF_PERF_RECORD_DATA_DIR", "PYPERF_PERF_RECORD_EXTRA_OPTS",
267+
"PYPERF_TACHYON_OPTS",
267268
]
268269
if locale:
269270
copy_env.extend(('LANG', 'LC_ADDRESS', 'LC_ALL', 'LC_COLLATE',

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ pyperf = "pyperf.__main__:main"
5959
[project.entry-points."pyperf.hook"]
6060
perf_record = "pyperf._hooks:perf_record"
6161
pystats = "pyperf._hooks:pystats"
62+
tachyon = "pyperf._hooks:tachyon"
6263
_test_hook = "pyperf._hooks:_test_hook"
6364

6465
[tool.setuptools]

0 commit comments

Comments
 (0)