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
461 lines
13 KiB
Python
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])
|