Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

313 lines
10 KiB
Python

# Copyright (c) Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""
Python tests.
"""
from __future__ import annotations
import pytest
from conftest import RocprofsysTest
from pathlib import Path
pytestmark = [pytest.mark.python]
# =============================================================================
# Python fixtures
# =============================================================================
@pytest.fixture
def python_rocpd_env() -> dict[str, str]:
return {}
@pytest.fixture
def python_source_rocpd_rules(validation_rules_dir: Path) -> list[Path]:
rules_dir = validation_rules_dir / "python"
return [
rules_dir / "python-source-rules.json",
]
@pytest.fixture
def python_builtin_rocpd_rules(validation_rules_dir: Path) -> list[Path]:
rules_dir = validation_rules_dir / "python"
return [
rules_dir / "python-builtin-rules.json",
]
@pytest.fixture
def python_builtin_annotated_rocpd_rules(validation_rules_dir: Path) -> list[Path]:
rules_dir = validation_rules_dir / "python"
return [
rules_dir / "python-builtin-annotated-rules.json",
]
@pytest.fixture(scope="session")
def get_cat_command() -> list[str]:
"""Get a command to concatenate files (like Unix cat).
Uses 'cmake -E cat' if available, otherwise falls back to system 'cat'.
"""
import shutil
import subprocess
# Try cmake -E cat first (available in CMake 3.18+)
cmake_exe = shutil.which("cmake")
if cmake_exe:
try:
result = subprocess.run(
[cmake_exe, "-E", "cat", "--help"], capture_output=True, timeout=5
)
# cmake -E cat returns 0 even for --help
if result.returncode == 0:
return [cmake_exe, "-E", "cat"]
except (subprocess.TimeoutExpired, subprocess.SubprocessError):
pass
# Fall back to system cat
cat_exe = shutil.which("cat")
if cat_exe:
return [cat_exe]
pytest.skip("No cat command available (neither 'cmake -E cat' nor 'cat')")
# =============================================================================
# Python tests
# =============================================================================
@pytest.mark.python_versions
class TestPython(RocprofsysTest):
PYTHON_SOURCE_GENERAL = {
"metric": "trip_count", # Timemory
"file": "trip_count.json", # Timemory
"categories": ["python", "user"], # Perfetto
"labels": [
"main_loop",
"run",
"fib",
"fib",
"fib",
"fib",
"fib",
"inefficient",
"_sum",
],
"counts": [5, 3, 3, 6, 12, 18, 6, 3, 3],
"depths": [0, 1, 2, 3, 4, 5, 6, 2, 3],
}
PYTHON_BUILTIN_GENERAL = {
"metric": "trip_count", # Timemory
"file": "trip_count.json", # Timemory
"categories": ["python"], # Perfetto
"labels": [
"[run][builtin.py:31]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[fib][builtin.py:13]",
"[inefficient][builtin.py:17]",
],
"counts": [5, 5, 10, 20, 40, 80, 160, 260, 220, 80, 10, 5],
"depths": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1],
}
# Per-function debug annotations that -a/--annotate-trace attaches to every
# profiled python region (see libpyrocprofsys.cpp's config::annotations).
PYTHON_ANNOTATE_DEBUG_KEYS = [
"file",
"line",
"lasti",
"argcount",
"nlocals",
"stacksize",
]
@pytest.mark.timeout(120)
@pytest.mark.parametrize(
"annotated, exclude",
[
pytest.param(False, False, id="base"),
pytest.param(True, False, id="annotated"),
pytest.param(False, True, id="inefficient"),
pytest.param(True, True, id="inefficient-annotated"),
],
)
def test_external(self, python_version, annotated, exclude):
if exclude:
profile_args = ["-E", "^inefficient$"]
else:
profile_args = ["--label", "file"]
result = self.run_test(
"python",
target="external.py",
profile_args=profile_args,
annotated=annotated,
python_version=python_version,
run_args=["-v", "10", "-n", "5"],
)
self.assert_regex(result)
if not annotated and not exclude:
file_pass_regex = [
r"(\[compile\]).*"
r"(\| \|0>>> \[run\]\[external.py\]).*"
r"(\| \|0>>> \|_\[fib\]\[external.py\]).*"
r"(\| \|0>>> \|_\[inefficient\]\[external.py\])"
]
self.assert_file_regex(
result.output_dir / "trip_count.txt",
pass_regex=file_pass_regex,
fail_regex=[r"(\|_inefficient).*(\|_sum)"],
)
elif not annotated and exclude:
self.assert_file_regex(
result.output_dir / "trip_count.txt",
fail_regex=[r"(\|_inefficient).*(\|_sum)"],
)
@pytest.mark.timeout(120)
@pytest.mark.parametrize(
"annotated",
[
pytest.param(False, marks=pytest.mark.rocpd("python_rocpd_env")),
pytest.param(
True, id="annotated", marks=pytest.mark.rocpd("python_rocpd_env")
),
],
)
def test_builtin(
self,
python_version,
annotated,
python_rocpd_env,
python_builtin_rocpd_rules,
python_builtin_annotated_rocpd_rules,
):
result = self.run_test(
"python",
target="builtin.py",
env=python_rocpd_env,
profile_args=["-b", "--label", "file", "line"],
annotated=annotated,
python_version=python_version,
run_args=["-v", "10", "-n", "5"],
)
self.assert_regex(result)
if not annotated:
self.assert_file_regex(
result.output_dir / "trip_count.txt",
pass_regex=[r"\[inefficient\]\[builtin.py:17\]"],
)
self.assert_timemory(
result,
file_name=self.PYTHON_BUILTIN_GENERAL["file"],
metric=self.PYTHON_BUILTIN_GENERAL["metric"],
labels=self.PYTHON_BUILTIN_GENERAL["labels"],
counts=self.PYTHON_BUILTIN_GENERAL["counts"],
depths=self.PYTHON_BUILTIN_GENERAL["depths"],
)
self.assert_perfetto(
result,
categories=self.PYTHON_BUILTIN_GENERAL["categories"],
labels=self.PYTHON_BUILTIN_GENERAL["labels"],
counts=self.PYTHON_BUILTIN_GENERAL["counts"],
depths=self.PYTHON_BUILTIN_GENERAL["depths"],
)
self.assert_rocpd(
result,
rules_files=python_builtin_rocpd_rules,
)
# regression: without -a, no per-function debug annotations should
# reach the trace (see test below for the annotated=True counterpart)
self.assert_perfetto(
result,
key_names=self.PYTHON_ANNOTATE_DEBUG_KEYS,
key_counts=[0] * len(self.PYTHON_ANNOTATE_DEBUG_KEYS),
)
else:
# regression: -a/--annotate-trace annotations were dropped by
# trace-cache replay (only debug.begin_ns/debug.corr_id ever reached
# the perfetto trace, identical with or without -a). Every profiled
# python region carries all six annotation fields, so each key's
# count must equal the total number of profiled slices.
total_slices = sum(self.PYTHON_BUILTIN_GENERAL["counts"])
self.assert_perfetto(
result,
key_names=self.PYTHON_ANNOTATE_DEBUG_KEYS,
key_counts=[total_slices] * len(self.PYTHON_ANNOTATE_DEBUG_KEYS),
)
self.assert_rocpd(
result,
rules_files=python_builtin_annotated_rocpd_rules,
)
@pytest.mark.timeout(120)
@pytest.mark.parametrize(
"annotated",
[
False,
pytest.param(True, id="annotated"),
],
)
def test_builtin_noprofile(self, python_version, annotated):
result = self.run_test(
"python",
target="noprofile.py",
profile_args=["-b", "--label", "file"],
annotated=annotated,
python_version=python_version,
run_args=["-v", "15", "-n", "5"],
)
self.assert_regex(result)
if not annotated:
self.assert_file_regex(
result.output_dir / "trip_count.txt",
pass_regex=[r"run..noprofile.py."],
fail_regex=[r"(fib|inefficient)..noprofile.py."],
)
@pytest.mark.rocpd("python_rocpd_env")
def test_source(self, python_version, python_rocpd_env, python_source_rocpd_rules):
result = self.run_test(
"python",
target="source.py",
env=python_rocpd_env,
python_version=python_version,
run_args=["-v", "5", "-n", "5", "-s", "3"],
standalone=True,
)
self.assert_regex(result)
self.assert_timemory(
result,
file_name=self.PYTHON_SOURCE_GENERAL["file"],
metric=self.PYTHON_SOURCE_GENERAL["metric"],
labels=self.PYTHON_SOURCE_GENERAL["labels"],
counts=self.PYTHON_SOURCE_GENERAL["counts"],
depths=self.PYTHON_SOURCE_GENERAL["depths"],
)
self.assert_perfetto(
result,
categories=self.PYTHON_SOURCE_GENERAL["categories"],
labels=self.PYTHON_SOURCE_GENERAL["labels"],
counts=self.PYTHON_SOURCE_GENERAL["counts"],
depths=self.PYTHON_SOURCE_GENERAL["depths"],
)
self.assert_rocpd(
result,
rules_files=python_source_rocpd_rules,
)