Files
rocm-systems/projects/rocprofiler-systems/tests/pytest/test_binaries.py
T
Kian Cossettini 0eac446cb0 [rocprofiler-systems] - Implement subset of CTests into PyTests (#2666)
Convert a subset of the ctest to pytest to be used in TheRock CI.
Create a new cmake flag `ROCPROFSYS_INSTALL_TESTING` to control test suite installation.
- pytest package will be installed to share/rocprofiler-systems/tests
- all compiled examples are put in share/rocprofiler-systems/examples
- all test relevant scripts are put in share/rocprofiler-systems/tests
- see README.md in share/rocprofiler-systems/tests
2026-01-26 23:10:01 -05:00

758 rader
20 KiB
Python

# Copyright (c) Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT
"""
Tests rocprof-sys binaries
"""
from __future__ import annotations
import pytest
from pathlib import Path
import os
pytestmark = [pytest.mark.rocprof_binary]
# ============================================================================
# Helper functions
# ============================================================================
def get_ls_command() -> tuple[str, list[str]]:
"""Get ls binary name and args (handles RedHat coreutils wrapper).
Returns:
Tuple of (binary_name, args_list)
"""
if os.path.exists("/usr/bin/coreutils"):
return "coreutils", ["--coreutils-prog=ls"]
return "ls", []
# ============================================================================
# rocprof-sys-instrument tests
# ============================================================================
class TestInstrumentBinary:
"""Tests for rocprof-sys-instrument binary."""
target = "rocprof-sys-instrument"
def test_help(
self,
run_test,
assert_regex,
):
pass_regex = [
r"\[rocprof-sys-instrument\] Usage:[\s\S]*"
r"\[DEBUG OPTIONS\][\s\S]*"
r"\[MODE OPTIONS\][\s\S]*"
r"\[LIBRARY OPTIONS\][\s\S]*"
r"\[SYMBOL SELECTION OPTIONS\][\s\S]*"
r"\[RUNTIME OPTIONS\][\s\S]*"
r"\[GRANULARITY OPTIONS\][\s\S]*"
r"\[DYNINST OPTIONS\]"
]
result = run_test(
"baseline",
target=self.target,
run_args=["--help"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
def test_simulate_ls(
self,
run_test,
assert_regex,
assert_file_exists,
):
ls_name, ls_args = get_ls_command()
test_args = [
"--simulate",
"--print-format",
"json",
"txt",
"xml",
"-v",
"2",
"--all-functions",
"--",
ls_name,
*ls_args,
]
expected_files = [
"available.json",
"available.txt",
"available.xml",
"excluded.json",
"excluded.txt",
"excluded.xml",
"instrumented.json",
"instrumented.txt",
"instrumented.xml",
"overlapping.json",
"overlapping.txt",
"overlapping.xml",
]
result = run_test(
"baseline",
target=self.target,
run_args=test_args,
timeout=240,
fail_on_not_found=True,
)
assert_regex(result)
expected_files_paths = [
result.output_dir / "instrumentation" / f for f in expected_files
]
assert_file_exists(expected_files_paths)
def test_simulate_lib(
self,
rocprof_config,
run_test,
assert_regex,
):
user_lib = rocprof_config.rocprofsys_lib_dir / "librocprof-sys-user.so"
if not user_lib.exists():
pytest.fail("librocprof-sys-user.so not found")
pass_regex = [
r"\[rocprof-sys\]\[exe\] Runtime instrumentation is not possible![\s\S]*"
r"\[rocprof-sys\]\[exe\] Switching to binary rewrite mode and assuming '--simulate --all-functions'"
]
result = run_test(
"baseline",
target=self.target,
run_args=["--print-available", "functions", "-v", "2", "--", str(user_lib)],
timeout=120,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
def test_simulate_lib_basename(
self,
rocprof_config,
test_output_dir,
run_test,
assert_regex,
):
"""Test instrument with library basename.
This MUST be run from a tmp directory, NOT from the actual lib directory.
Running from the lib directory causes Dyninst to modify the library in-place,
contaminating it with instrumentation markers. This breaks all subsequent
binary rewrite tests with "unable to reinstrument previously instrumented
binary" errors.
"""
lib_basename = "librocprof-sys-user.so"
user_lib = rocprof_config.rocprofsys_lib_dir / lib_basename
if not user_lib.exists():
pytest.skip(f"{lib_basename} not built")
tmp_dir = test_output_dir / "tmp"
tmp_dir.mkdir(parents=True, exist_ok=True)
output_lib = test_output_dir / lib_basename
result = run_test(
"baseline",
target=self.target,
run_args=[
"--print-available",
"functions",
"-v",
"2",
"-o",
str(output_lib),
"--",
lib_basename,
],
timeout=120,
working_directory=tmp_dir,
fail_on_not_found=True,
)
assert_regex(result)
def test_write_log(
self,
run_test,
assert_regex,
assert_file_exists,
):
"""Test instrument writing to log file."""
ls_name, ls_args = get_ls_command()
pass_regex = [r"Opening .*/instrumentation/user\.log"]
result = run_test(
"baseline",
target=self.target,
run_args=[
"--print-instrumented",
"functions",
"-v",
"1",
"--log-file",
"user.log",
"--",
ls_name,
*ls_args,
],
timeout=120,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
assert_file_exists(result.output_dir / "instrumentation" / "user.log")
# ============================================================================
# rocprof-sys-avail tests
# ============================================================================
class TestAvailBinary:
"""Tests for rocprof-sys-avail binary."""
target = "rocprof-sys-avail"
def test_help(
self,
run_test,
assert_regex,
):
pass_regex = [
r"\[rocprof-sys-avail\] Usage:[\s\S]*"
r"\[DEBUG OPTIONS\][\s\S]*"
r"\[INFO OPTIONS\][\s\S]*"
r"\[FILTER OPTIONS\][\s\S]*"
r"\[COLUMN OPTIONS\][\s\S]*"
r"\[DISPLAY OPTIONS\][\s\S]*"
r"\[OUTPUT OPTIONS\][\s\S]*"
]
result = run_test(
"baseline",
target=self.target,
run_args=["--help"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
def test_all(
self,
run_test,
assert_regex,
):
result = run_test(
"baseline",
target=self.target,
run_args=["--all"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result)
def test_all_expand_keys(
self,
run_test,
assert_regex,
):
fail_regex = [r"%[a-zA-Z_]%"]
result = run_test(
"baseline",
target=self.target,
run_args=["--all", "--expand-keys"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, fail_regex=fail_regex)
def test_all_only_available_alphabetical(
self,
run_test,
test_output_dir,
assert_regex,
assert_file_exists,
):
log_file = (
test_output_dir / "rocprof-sys-avail-all-only-available-alphabetical.log"
)
result = run_test(
"baseline",
target=self.target,
run_args=[
"--all",
"--available",
"--alphabetical",
"--debug",
"--output",
str(log_file),
],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result)
assert_file_exists(log_file)
def test_all_csv(
self,
run_test,
assert_regex,
):
pass_regex = [
r"COMPONENT#AVAILABLE#VALUE_TYPE#STRING_IDS#FILENAME#DESCRIPTION#CATEGORY#[\s\S]*"
r"ENVIRONMENT VARIABLE#VALUE#DATA TYPE#DESCRIPTION#CATEGORIES#[\s\S]*"
r"HARDWARE COUNTER#DEVICE#AVAILABLE#DESCRIPTION#"
]
result = run_test(
"baseline",
target=self.target,
run_args=["--all", "--csv", "--csv-separator", "#"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
def test_filter_wall_clock_available(
self,
run_test,
assert_regex,
):
pass_regex = [
r"\|[-]+\|[\s\S]*"
r"\|[ ]+COMPONENT[ ]+\|[\s\S]*"
r"\|[-]+\|[\s\S]*"
r"\| (wall_clock)[ ]+\|[\s\S]*"
r"\|[-]+\|"
]
result = run_test(
"baseline",
target=self.target,
run_args=["-r", "wall_clock", "-C", "--available"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
def test_category_filter_rocprofiler_systems(
self,
run_test,
assert_regex,
):
pass_regex = [r"ROCPROFSYS_(SETTINGS_DESC|OUTPUT_FILE|OUTPUT_PREFIX)"]
fail_regex = [
r"ROCPROFSYS_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)",
]
result = run_test(
"baseline",
target=self.target,
run_args=["--categories", "settings::rocprofsys", "--brief"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex, fail_regex=fail_regex)
def test_category_filter_timemory(
self,
run_test,
assert_regex,
):
pass_regex = [
r"ROCPROFSYS_(ADD_SECONDARY|SCIENTIFIC|PRECISION|MEMORY_PRECISION|TIMING_PRECISION)"
]
fail_regex = [r"ROCPROFSYS_(SETTINGS_DESC|OUTPUT_FILE)"]
result = run_test(
"baseline",
target=self.target,
run_args=["--categories", "settings::timemory", "--brief", "--advanced"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex, fail_regex=fail_regex)
def test_regex_negation(
self,
run_test,
assert_regex,
):
pass_regex = [
r"ENVIRONMENT VARIABLE,[\s\S]*"
r"ROCPROFSYS_CI_SKIP_PUSH_POP_CHECK,[\s\S]*"
r"ROCPROFSYS_THREAD_POOL_SIZE,[\s\S]*"
r"ROCPROFSYS_USE_PID,"
]
fail_regex = [r"ROCPROFSYS_TRACE"]
result = run_test(
"baseline",
target=self.target,
run_args=[
"-R",
"rocprofsys",
"~timemory",
"-r",
"_P",
"~PERFETTO",
"~PROCESS_SAMPLING",
"~KOKKOSP",
"~PAGE",
"--csv",
"--brief",
"--advanced",
],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex, fail_regex=fail_regex)
def test_write_config(
self,
run_test,
test_output_dir,
assert_regex,
assert_file_exists,
):
config_base = test_output_dir / "rocprof-sys-test"
avail_cfg_path = test_output_dir / "rocprof-sys-"
avail_cfg_path = str(avail_cfg_path).replace("+", r"\+")
pass_regex = [
rf"Outputting JSON configuration file '{avail_cfg_path}test\.json'"
r"[\s\S]*"
rf"Outputting XML configuration file '{avail_cfg_path}test\.xml'"
r"[\s\S]*"
rf"Outputting text configuration file '{avail_cfg_path}test\.cfg'"
]
result = run_test(
"baseline",
target=self.target,
run_args=[
"-G",
str(config_base) + ".cfg",
"-F",
"txt",
"json",
"xml",
"--force",
"--all",
"-c",
"rocprofsys",
],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
config_files = [
test_output_dir / f"rocprof-sys-test.{ext}" for ext in ["cfg", "json", "xml"]
]
assert_file_exists(config_files, subtest_name="Config file existence validation")
def test_write_config_tweak(
self,
run_test,
test_output_dir,
assert_regex,
assert_file_exists,
):
config_base = test_output_dir / "rocprof-sys-tweak"
env_overrides = {
"ROCPROFSYS_TRACE": "OFF",
"ROCPROFSYS_PROFILE": "ON",
"ROCPROFSYS_USE_SAMPLING": "OFF",
"ROCPROFSYS_TIME_OUTPUT": "OFF",
}
avail_cfg_path = test_output_dir / "rocprof-sys-"
avail_cfg_path = str(avail_cfg_path).replace("+", r"\+")
pass_regex = [
rf"Outputting JSON configuration file '{avail_cfg_path}tweak\.json'"
r"[\s\S]*"
rf"Outputting XML configuration file '{avail_cfg_path}tweak\.xml'"
r"[\s\S]*"
rf"Outputting text configuration file '{avail_cfg_path}tweak\.cfg'"
]
result = run_test(
"baseline",
target=self.target,
run_args=[
"-G",
str(config_base) + ".cfg",
"-F",
"txt",
"json",
"xml",
"--force",
],
timeout=45,
fail_on_not_found=True,
env=env_overrides,
)
assert_regex(result, pass_regex=pass_regex)
config_files = [
test_output_dir / f"rocprof-sys-tweak.{ext}" for ext in ["cfg", "json", "xml"]
]
assert_file_exists(config_files, subtest_name="Config file existence validation")
def test_list_keys(
self,
run_test,
assert_regex,
):
pass_regex = [r"Output Keys:[\s\S]*%argv%[\s\S]*%argv_hash%"]
result = run_test(
"baseline",
target=self.target,
run_args=["--list-keys", "--expand-keys"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
def test_list_keys_markdown(
self,
run_test,
assert_regex,
):
pass_regex = [r"`%argv%`[\s\S]*`%argv_hash%`"]
result = run_test(
"baseline",
target=self.target,
run_args=["--list-keys", "--expand-keys", "--markdown"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
def test_list_categories(
self,
run_test,
assert_regex,
):
pass_regex = [r" component::[\s\S]* hw_counters::[\s\S]* settings::"]
result = run_test(
"baseline",
target=self.target,
run_args=["--list-categories"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
def test_core_categories(
self,
run_test,
assert_regex,
):
pass_regex = [
r"ROCPROFSYS_CONFIG_FILE[\s\S]*ROCPROFSYS_ENABLED[\s\S]*"
r"ROCPROFSYS_SUPPRESS_CONFIG[\s\S]*ROCPROFSYS_SUPPRESS_PARSING[\s\S]*ROCPROFSYS_VERBOSE"
]
result = run_test(
"baseline",
target=self.target,
run_args=["-c", "core"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result, pass_regex=pass_regex)
# ============================================================================
# rocprof-sys-run tests
# ============================================================================
class TestRunBinary:
"""Tests for rocprof-sys-run binary."""
target = "rocprof-sys-run"
def test_help(
self,
run_test,
assert_regex,
):
"""Test rocprof-sys-run --help output."""
result = run_test(
"baseline",
target=self.target,
run_args=["--help"],
timeout=45,
fail_on_not_found=True,
)
assert_regex(result)
def test_args(
self,
test_output_dir,
run_test,
assert_regex,
):
"""Test rocprof-sys-run with comprehensive arguments."""
import shutil
# Check if sleep command exists
sleep_cmd = shutil.which("sleep")
if not sleep_cmd:
pytest.skip("sleep command not found")
# Create empty config file
config_dir = test_output_dir / "config"
config_dir.mkdir(parents=True, exist_ok=True)
empty_cfg = config_dir / "empty.cfg"
empty_cfg.write_text("#\n# empty config file\n#\n")
tmpdir = test_output_dir / "tmpdir"
tmpdir = tmpdir.resolve()
tmpdir.mkdir(parents=True, exist_ok=True)
args = [
"--monochrome",
"--debug=false",
"-v",
"1",
"-c",
str(empty_cfg),
"-o",
str(test_output_dir),
"run-args-output/",
"-TPHD",
"-S",
"cputime",
"realtime",
"--trace-wait=1.0e-12",
"--trace-duration=5.0",
"--wait=1.0",
"--duration=3.0",
"--trace-file=perfetto-run-args-trace.proto",
"--trace-buffer-size=100",
"--trace-fill-policy=ring_buffer",
"--profile-format",
"console",
"json",
"text",
"--process-freq",
"1000",
"--process-wait",
"0.0",
"--process-duration",
"10",
"--cpus",
"0-4",
"--gpus",
"0",
"-f",
"1000",
"--sampling-wait",
"1.0",
"--sampling-duration",
"10",
"-t",
"0-3",
"--sample-cputime",
"1000",
"1.0",
"0-3",
"--sample-realtime",
"10",
"0.5",
"0-3",
"-I",
"all",
"-E",
"mutex-locks",
"rw-locks",
"spin-locks",
"-C",
"perf::INSTRUCTIONS",
"--inlines",
"--hsa-interrupt",
"0",
"--use-causal=false",
"--use-kokkosp",
"--num-threads-hint=4",
"--sampling-allocator-size=32",
"--ci",
"--dl-verbose=3",
"--perfetto-annotations=off",
"--kokkosp-kernel-logger",
"--kokkosp-name-length-max=1024",
'--kokkosp-prefix="[kokkos]"',
"--tmpdir",
str(tmpdir),
"--perfetto-backend",
"inprocess",
"--use-pid",
"false",
"--time-output",
"off",
"--thread-pool-size",
"0",
"--timemory-components",
"wall_clock",
"cpu_clock",
"peak_rss",
"page_rss",
"--fork",
"--",
sleep_cmd,
"5",
]
result = run_test(
"baseline",
target=self.target,
run_args=args,
timeout=45,
fail_on_not_found=True,
)
assert_regex(result)