Files
TheRock/rocm-systems/projects/rocprofiler-compute/tests/test_pc_sampling.py
T
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

461 lines
13 KiB
Python

# Copyright (c) Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
from pathlib import Path
from types import SimpleNamespace
import common
import pandas as pd
import pytest
from pc_sampling.pc_sampling_analysis import load_pc_sample_records
from utils.parser import load_pc_sampling_data
config = {}
config["app_1"] = ["./tests/vcopy", "-n", "1048576", "-b", "256", "-i", "3"]
config["app_mat_mul_max"] = ["./tests/mat_mul_max"]
config["cleanup"] = True
config["COUNTER_LOGGING"] = False
config["METRIC_COMPARE"] = False
num_devices = 1
PC_SAMPLING_HOST_TRAP_FILES = sorted([
"ps_file_results.json",
"sysinfo.csv",
])
PC_SAMPLING_STOCHASTIC_FILES = sorted([
"ps_file_results.json",
"sysinfo.csv",
])
def _assert_pc_sampling_files(file_dict, expected):
"""Assert the PC sampling output file-set, matching the native collector's
``<pid>_code_obj_info.json``.
"""
keys = list(file_dict.keys())
code_obj = [k for k in keys if k.endswith("_code_obj_info.json")]
assert len(code_obj) == 1, (
f"expected exactly one *_code_obj_info.json, got {code_obj}"
)
remaining = sorted(k for k in keys if k not in code_obj)
assert remaining == sorted(expected)
def is_pc_sampling_not_supported(output):
"""
To be called with the stdout + stderr after profiling.
Check whether profiling output said PC sampling is not supported on the machine
"""
return "Given PC sampling configuration is not supported" in output
def _skip_if_pc_sampling_unsupported(stdout, stderr, workload_dir):
if is_pc_sampling_not_supported(f"{stdout}\n{stderr}"):
common.clean_output_dir(config["cleanup"], workload_dir)
pytest.skip("PC sampling is not supported")
def test_pc_sampling_host_trap(binary_handler_profile_rocprof_compute, monkeypatch):
"""
Test that PC sampling works with --block 21 and --pc-sampling-method host_trap.
"""
common.require_pc_sampling_gpu()
monkeypatch.setenv("ROCPROF", "rocprofiler-sdk")
options = [
"--experimental",
"--pc-sampling",
"--block",
"21",
"--pc-sampling-method",
"host_trap",
]
workload_dir = common.get_output_dir()
code, stdout, stderr = binary_handler_profile_rocprof_compute(
config,
workload_dir,
options,
check_success=False,
capture_output=True,
stream=True,
roof=False,
app_name="app_mat_mul_max",
)
_skip_if_pc_sampling_unsupported(stdout, stderr, workload_dir)
assert code == 0
file_dict = common.check_non_pmc_files(workload_dir, num_devices, 1)
_assert_pc_sampling_files(file_dict, PC_SAMPLING_HOST_TRAP_FILES)
common.clean_output_dir(config["cleanup"], workload_dir)
def test_pc_sampling_stochastic(binary_handler_profile_rocprof_compute, monkeypatch):
"""
Test that PC sampling works with --block 21 and --pc-sampling-method stochastic.
"""
common.require_pc_sampling_gpu(is_stochastic=True)
monkeypatch.setenv("ROCPROF", "rocprofiler-sdk")
options = [
"--experimental",
"--pc-sampling",
"--block",
"21",
"--pc-sampling-method",
"stochastic",
]
workload_dir = common.get_output_dir()
code, stdout, stderr = binary_handler_profile_rocprof_compute(
config,
workload_dir,
options,
check_success=False,
capture_output=True,
stream=True,
roof=False,
app_name="app_mat_mul_max",
)
_skip_if_pc_sampling_unsupported(stdout, stderr, workload_dir)
assert code == 0
file_dict = common.check_non_pmc_files(workload_dir, num_devices, 1)
_assert_pc_sampling_files(file_dict, PC_SAMPLING_STOCHASTIC_FILES)
common.clean_output_dir(config["cleanup"], workload_dir)
def test_multi_rank_pc_sampling_only(
binary_handler_profile_rocprof_compute, monkeypatch
):
"""
Test that no multi-rank warning is printed when running with only
--block 21 (PC sampling only mode requires a single pass) with multi-rank.
"""
common.require_pc_sampling_gpu()
monkeypatch.setenv("ROCPROF", "rocprofiler-sdk")
monkeypatch.setenv("OMPI_COMM_WORLD_RANK", "0")
monkeypatch.setenv("OMPI_COMM_WORLD_SIZE", "2")
workload_dir = common.get_output_dir()
options = [
"--experimental",
"--pc-sampling",
"--block",
"21",
"--pc-sampling-method",
"host_trap",
]
_, stdout, stderr = binary_handler_profile_rocprof_compute(
config,
workload_dir,
options,
app_name="app_1",
capture_output=True,
stream=True,
check_success=False,
)
_skip_if_pc_sampling_unsupported(stdout, stderr, workload_dir)
output = stdout + stderr
assert "Multi-rank application detected" not in output
common.clean_output_dir(config["cleanup"], workload_dir)
def test_multi_rank_warning_pc_sampling_with_counters(
binary_handler_profile_rocprof_compute, monkeypatch
):
"""
Test that a multi-rank warning is printed when running with --block 21
and another block (PC sampling with counters mode requires multiple passes)
with multi-rank.
"""
common.require_pc_sampling_gpu()
monkeypatch.setenv("ROCPROF", "rocprofiler-sdk")
monkeypatch.setenv("OMPI_COMM_WORLD_RANK", "0")
monkeypatch.setenv("OMPI_COMM_WORLD_SIZE", "2")
workload_dir = common.get_output_dir()
options = [
"--experimental",
"--pc-sampling",
"--block",
"21",
"2",
"--pc-sampling-method",
"host_trap",
]
_, stdout, stderr = binary_handler_profile_rocprof_compute(
config,
workload_dir,
options,
app_name="app_1",
capture_output=True,
stream=True,
check_success=False,
)
_skip_if_pc_sampling_unsupported(stdout, stderr, workload_dir)
output = stdout + stderr
assert "Multi-rank application detected" in output
assert "Application replay mode" in output
assert "--iteration-multiplexing" in output
assert "--block" not in output
assert "--set" in output
common.clean_output_dir(config["cleanup"], workload_dir)
def test_pc_sampling_profile_then_analyze(
binary_handler_profile_rocprof_compute,
binary_handler_analyze_rocprof_compute,
capsys,
monkeypatch,
):
"""
End-to-end: profile with PC sampling (host_trap), then
run analysis on the profiling output.
"""
common.require_pc_sampling_gpu()
monkeypatch.setenv("ROCPROF", "rocprofiler-sdk")
options = [
"--experimental",
"--pc-sampling",
"--block",
"21",
"--pc-sampling-method",
"host_trap",
]
workload_dir = common.get_output_dir()
code, stdout, stderr = binary_handler_profile_rocprof_compute(
config,
workload_dir,
options,
check_success=False,
capture_output=True,
stream=True,
roof=False,
app_name="app_mat_mul_max",
)
_skip_if_pc_sampling_unsupported(stdout, stderr, workload_dir)
assert code == 0
file_dict = common.check_non_pmc_files(workload_dir, num_devices, 1)
_assert_pc_sampling_files(file_dict, PC_SAMPLING_HOST_TRAP_FILES)
code = binary_handler_analyze_rocprof_compute(
[
"analyze",
"--path",
workload_dir,
"--block",
"21",
],
)
assert code == 0
captured = capsys.readouterr()
assert "0.1 Top Kernels" in captured.out
assert "0.2 Dispatch List" in captured.out
workload_path = Path(workload_dir)
kernel_top_csv = workload_path / "pmc_kernel_top.csv"
assert kernel_top_csv.exists()
kernel_top_header = kernel_top_csv.read_text().splitlines()[0]
assert "Kernel_Name" in kernel_top_header
assert "Count" in kernel_top_header
assert "Percent" in kernel_top_header
dispatch_info_csv = workload_path / "pmc_dispatch_info.csv"
assert dispatch_info_csv.exists()
dispatch_info_header = dispatch_info_csv.read_text().splitlines()[0]
assert "Dispatch_ID" in dispatch_info_header
assert "Kernel_Name" in dispatch_info_header
assert "GPU_ID" in dispatch_info_header
code = binary_handler_analyze_rocprof_compute(
[
"analyze",
"--path",
workload_dir,
"--block",
"21",
"--kernel",
"0",
],
)
assert code == 0
captured = capsys.readouterr()
assert "0.1 Top Kernels" in captured.out
assert "0.2 Dispatch List" in captured.out
assert "21. PC Sampling" in captured.out
common.clean_output_dir(config["cleanup"], workload_dir)
def test_pc_sampling_with_sol_block(
binary_handler_profile_rocprof_compute,
binary_handler_analyze_rocprof_compute,
capsys,
monkeypatch,
):
"""
PC sampling with counter collection (--block 21 2): profiling produces the
expected artifacts and analyze renders both counter and PC sampling panels.
"""
common.require_pc_sampling_gpu()
monkeypatch.setenv("ROCPROF", "rocprofiler-sdk")
options = [
"--experimental",
"--pc-sampling",
"--block",
"21",
"2",
"--pc-sampling-method",
"host_trap",
]
workload_dir = common.get_output_dir()
code, stdout, stderr = binary_handler_profile_rocprof_compute(
config,
workload_dir,
options,
check_success=False,
capture_output=True,
stream=True,
roof=False,
app_name="app_mat_mul_max",
)
_skip_if_pc_sampling_unsupported(stdout, stderr, workload_dir)
assert code == 0
file_dict = common.check_csv_files(workload_dir, num_devices, 1)
_assert_pc_sampling_files(file_dict, PC_SAMPLING_HOST_TRAP_FILES)
assert common.check_file_pattern("- '21'", f"{workload_dir}/profiling_config.yaml")
assert common.check_file_pattern("- '2'", f"{workload_dir}/profiling_config.yaml")
# Analyze with a single kernel so the detailed PC sampling table renders.
code = binary_handler_analyze_rocprof_compute(
[
"analyze",
"--path",
workload_dir,
"--kernel",
"0",
],
)
assert code == 0
captured = capsys.readouterr()
assert "2.1 System Speed-of-Light" in captured.out
assert "21. PC Sampling" in captured.out
# The "instruction" column header only renders when the table has rows.
assert "instruction" in captured.out
common.clean_output_dir(config["cleanup"], workload_dir)
def _kernel_top_workload() -> SimpleNamespace:
"""Workload stub with dfs[1] populated for load_pc_sampling_data tests."""
return SimpleNamespace(
filter_kernel_ids=[],
dfs={
1: pd.DataFrame({
"Kernel_Name": ["kernel_a", "kernel_b", "kernel_c"],
"Count": [2, 1, 1],
"Sum(ns)": [900, 800, 200],
}),
},
)
def test_load_pc_sampling_data_missing_or_empty_sources_return_empty() -> None:
"""Absent tool data and empty buffer records both yield empty frames."""
workload = SimpleNamespace(filter_kernel_ids=[])
assert load_pc_sampling_data(workload, "none", "count", None).empty
assert load_pc_sampling_data(workload, "missing", "count", None).empty
workload.filter_kernel_ids = [0, 1, 2]
assert load_pc_sampling_data(workload, "test", "count", None).empty
empty_records = load_pc_sample_records({
"buffer_records": {
"pc_sample_stochastic": [],
"pc_sample_host_trap": [],
"kernel_dispatch": [],
},
})
assert empty_records.empty
def test_load_pc_sampling_data_out_of_bounds_kernel_warns(monkeypatch) -> None:
"""An out-of-bounds kernel index warns and returns empty."""
mock_warning = common.patch_console(monkeypatch, "utils.parser", "warning")[
"warning"
]
workload = _kernel_top_workload()
tool_data = {
"buffer_records": {"pc_sample_stochastic": [{}], "pc_sample_host_trap": []}
}
workload.filter_kernel_ids = [99]
result = load_pc_sampling_data(workload, "test", "count", tool_data)
mock_warning.assert_called()
call_args_str = str(mock_warning.call_args)
assert "out of bounds" in call_args_str or "99" in call_args_str
assert result.empty
def test_load_pc_sampling_data_single_kernel_uses_workload_dfs(monkeypatch) -> None:
"""A single-kernel filter reads the kernel name from workload.dfs[1]."""
per_kernel_calls = []
def record_per_kernel(*args, **kwargs):
per_kernel_calls.append((args, kwargs))
return pd.DataFrame()
monkeypatch.setattr(
"utils.parser.load_pc_sampling_data_per_kernel", record_per_kernel
)
workload = _kernel_top_workload()
tool_data = {
"buffer_records": {"pc_sample_stochastic": [{}], "pc_sample_host_trap": []}
}
workload.filter_kernel_ids = [1]
load_pc_sampling_data(workload, "test", "count", tool_data)
if per_kernel_calls:
assert "kernel_b" in str(per_kernel_calls[0])