Skip to content

Commit 116a7ba

Browse files
committed
feat: add instrument-hooks native module
1 parent c92304c commit 116a7ba

File tree

6 files changed

+121
-5
lines changed

6 files changed

+121
-5
lines changed

.github/workflows/ci.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ jobs:
1515
runs-on: ubuntu-latest
1616
steps:
1717
- uses: actions/checkout@v4
18+
with:
19+
submodules: true
1820
- name: Set up Python 3.11
1921
uses: actions/setup-python@v5
2022
with:
@@ -44,6 +46,8 @@ jobs:
4446

4547
steps:
4648
- uses: actions/checkout@v4
49+
with:
50+
submodules: true
4751
- uses: astral-sh/setup-uv@v4
4852
with:
4953
version: "0.5.20"

.gitmodules

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
[submodule "tests/benchmarks/TheAlgorithms"]
22
path = tests/benchmarks/TheAlgorithms
33
url = [email protected]:TheAlgorithms/Python.git
4+
[submodule "src/pytest_codspeed/instruments/hooks/instrument-hooks"]
5+
path = src/pytest_codspeed/instruments/hooks/instrument-hooks
6+
url = https://github.com/CodSpeedHQ/instrument-hooks
7+
branch = cod-731-ipc-implementation-for-perf-and-other-tools

setup.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@
55

66
from setuptools import setup
77

8-
build_path = (
9-
Path(__file__).parent / "src/pytest_codspeed/instruments/valgrind/_wrapper/build.py"
10-
)
8+
build_path = Path(__file__).parent / "src/pytest_codspeed/instruments/hooks/build.py"
119

1210
spec = importlib.util.spec_from_file_location("build", build_path)
1311
assert spec is not None, "The spec should be initialized"
@@ -52,8 +50,8 @@
5250
setup(
5351
package_data={
5452
"pytest_codspeed": [
55-
"instruments/valgrind/_wrapper/*.h",
56-
"instruments/valgrind/_wrapper/*.c",
53+
"instruments/hooks/instrument-hooks/includes/*.h",
54+
"instruments/hooks/instrument-hooks/dist/*.c",
5755
]
5856
},
5957
ext_modules=(
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
from __future__ import annotations
2+
3+
import os
4+
from typing import TYPE_CHECKING
5+
6+
if TYPE_CHECKING:
7+
from .dist_instrument_hooks import lib as LibType
8+
9+
10+
class InstrumentHooks:
11+
"""Zig library wrapper class providing benchmark measurement functionality."""
12+
13+
lib: LibType | None
14+
instance: int | None
15+
16+
def __init__(self) -> None:
17+
try:
18+
from .dist_instrument_hooks import lib # type: ignore
19+
20+
instance = lib.instrument_hooks_init()
21+
if instance == 0:
22+
raise RuntimeError("Failed to initialize instrumentation library")
23+
24+
self.instance = instance
25+
self.lib = lib
26+
except Exception:
27+
self.lib = None
28+
self.instance = None
29+
30+
def __del__(self):
31+
if self.lib is None:
32+
return
33+
34+
self.lib.instrument_hooks_deinit(self.instance)
35+
36+
def start_benchmark(self) -> None:
37+
"""Start a new benchmark measurement."""
38+
if self.lib is None:
39+
return
40+
41+
self.lib.instrument_hooks_start_benchmark(self.instance)
42+
43+
def stop_benchmark(self) -> None:
44+
"""Stop the current benchmark measurement."""
45+
if self.lib is None:
46+
return
47+
48+
self.lib.instrument_hooks_stop_benchmark(self.instance)
49+
50+
def set_current_benchmark(self, uri: str, pid: int | None = None) -> None:
51+
"""Set the current benchmark URI and process ID.
52+
53+
Args:
54+
uri: The benchmark URI string identifier
55+
pid: Optional process ID (defaults to current process)
56+
"""
57+
if self.lib is None:
58+
return
59+
60+
if pid is None:
61+
pid = os.getpid()
62+
self.lib.instrument_hooks_current_benchmark(
63+
self.instance, pid, uri.encode("ascii")
64+
)
65+
66+
def set_integration(self, name: str, version: str) -> None:
67+
"""Set the integration name and version."""
68+
if self.lib is None:
69+
return
70+
71+
self.lib.instrument_hooks_set_integration(
72+
self.instance, name.encode("ascii"), version.encode("ascii")
73+
)
74+
75+
def is_instrumented(self) -> bool:
76+
"""Check if instrumentation is active."""
77+
if self.lib is None:
78+
return False
79+
80+
return self.lib.instrument_hooks_is_instrumented(self.instance)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
from pathlib import Path
2+
3+
from cffi import FFI # type: ignore
4+
5+
ffibuilder = FFI()
6+
7+
includes_dir = Path(__file__).parent.joinpath("instrument-hooks/includes")
8+
header_text = (includes_dir / "core.h").read_text()
9+
filtered_header = "\n".join(
10+
line for line in header_text.splitlines() if not line.strip().startswith("#")
11+
)
12+
ffibuilder.cdef(filtered_header)
13+
14+
ffibuilder.set_source(
15+
"pytest_codspeed.instruments.hooks.dist_instrument_hooks",
16+
"""
17+
#include "core.h"
18+
#include <quadmath.h>
19+
""",
20+
libraries=["m", "quadmath"],
21+
sources=[
22+
"src/pytest_codspeed/instruments/hooks/instrument-hooks/dist/core.c",
23+
],
24+
include_dirs=[str(includes_dir)],
25+
extra_compile_args=["-lm", "-lcs50"],
26+
)
27+
28+
if __name__ == "__main__":
29+
ffibuilder.compile(verbose=True)

0 commit comments

Comments
 (0)