[roctx] Python bindings for rocprofiler-sdk-roctx (#402)

* [roctx] Python bindings for rocprofiler-sdk-roctx

* Update CHANGELOG

---------

Co-authored-by: Jonathan R. Madsen <jonathanrmadsen@gmail.com>
Этот коммит содержится в:
Madsen, Jonathan
2025-05-19 20:02:51 -05:00
коммит произвёл GitHub
родитель afb27f3f1a
Коммит 14c2dc55ff
16 изменённых файлов: 888 добавлений и 1 удалений
+56
Просмотреть файл
@@ -0,0 +1,56 @@
#
# rocprofv3 python bindings for roctx test(s)
#
cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR)
project(
rocprofiler-tests-python-bindings
LANGUAGES CXX
VERSION 0.0.0)
find_package(rocprofiler-sdk REQUIRED)
if(NOT Python3_EXECUTABLE)
find_package(Python3 3.6 REQUIRED COMPONENTS Interpreter)
endif()
string(REPLACE "LD_PRELOAD=" "ROCPROF_PRELOAD=" PRELOAD_ENV
"${ROCPROFILER_MEMCHECK_PRELOAD_ENV}")
set(tracing-env
"${PRELOAD_ENV}"
"PYTHONPATH=${rocprofiler-sdk_LIB_DIR}/python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}/site-packages"
)
rocprofiler_configure_pytest_files(CONFIG pytest.ini marker.py COPY validate.py
conftest.py)
add_test(
NAME test-roctx-python-bindings-execute
COMMAND
$<TARGET_FILE:rocprofiler-sdk::rocprofv3> --marker-trace --summary -u sec -d
${CMAKE_CURRENT_BINARY_DIR}/marker-python-bindings -o out --output-format csv
json pftrace --log-level config -- ${Python3_EXECUTABLE}
${CMAKE_CURRENT_BINARY_DIR}/marker.py)
set_tests_properties(
test-roctx-python-bindings-execute
PROPERTIES TIMEOUT 45 LABELS "integration-tests;python-bindings" ENVIRONMENT
"${tracing-env}")
add_test(
NAME test-roctx-python-bindings-validate
COMMAND
${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --agent-input
${CMAKE_CURRENT_BINARY_DIR}/marker-python-bindings/out_agent_info.csv
--marker-input
${CMAKE_CURRENT_BINARY_DIR}/marker-python-bindings/out_marker_api_trace.csv
--json-input ${CMAKE_CURRENT_BINARY_DIR}/marker-python-bindings/out_results.json
--pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/marker-python-bindings/out_results.pftrace)
set_tests_properties(
test-roctx-python-bindings-validate
PROPERTIES TIMEOUT 45 LABELS "integration-tests;python-bindings" DEPENDS
"test-roctx-python-bindings-execute" FAIL_REGULAR_EXPRESSION
"AssertionError")
+70
Просмотреть файл
@@ -0,0 +1,70 @@
#!/usr/bin/env python3
import csv
import pytest
import json
from rocprofiler_sdk.pytest_utils.dotdict import dotdict
from rocprofiler_sdk.pytest_utils import collapse_dict_list
from rocprofiler_sdk.pytest_utils.perfetto_reader import PerfettoReader
def pytest_addoption(parser):
parser.addoption(
"--agent-input",
action="store",
help="Path to agent info CSV file.",
)
parser.addoption(
"--marker-input",
action="store",
help="Path to marker API tracing CSV file.",
)
parser.addoption(
"--json-input",
action="store",
help="Path to JSON file.",
)
parser.addoption(
"--pftrace-input",
action="store",
help="Path to Perfetto trace file.",
)
@pytest.fixture
def agent_info_input_data(request):
filename = request.config.getoption("--agent-input")
data = []
with open(filename, "r") as inp:
reader = csv.DictReader(inp)
for row in reader:
data.append(row)
return data
@pytest.fixture
def marker_input_data(request):
filename = request.config.getoption("--marker-input")
data = []
with open(filename, "r") as inp:
reader = csv.DictReader(inp)
for row in reader:
data.append(row)
return data
@pytest.fixture
def json_data(request):
filename = request.config.getoption("--json-input")
with open(filename, "r") as inp:
return dotdict(collapse_dict_list(json.load(inp)))
@pytest.fixture
def pftrace_data(request):
filename = request.config.getoption("--pftrace-input")
return PerfettoReader(filename).read()[0]
+59
Просмотреть файл
@@ -0,0 +1,59 @@
#!@Python3_EXECUTABLE@
import os
import roctx
import random
from roctx.context_decorators import RoctxRange
_prefix = ""
@RoctxRange("fib")
def fib(n, nmin):
with RoctxRange(f"fib(n={n})" if n >= nmin else None):
return n if n < 2 else (fib(n - 1, nmin) + fib(n - 2, nmin))
@RoctxRange("sum")
def _sum(arr):
with RoctxRange(f"sum(nelem={len(arr)})"):
return sum(arr)
def inefficient(n):
roctx.rangePush(f"inefficient({n})")
a = 0
for i in range(n):
a += i
for j in range(n):
a += j
_len = a * n * n
_arr = [random.random() for _ in range(_len)]
_ret = _sum(_arr)
roctx.rangePop()
return _ret
def run(n):
idx = roctx.rangeStart(f"run({n})")
_ret_a = fib(n, max([n / 2, n - 10]))
_ret_b = inefficient(n)
roctx.rangeStop(idx)
return (_ret_a, _ret_b)
if __name__ == "__main__":
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-n", "--num-iterations", help="Number", type=int, default=3)
parser.add_argument("-v", "--value", help="Starting value", type=int, default=20)
args = parser.parse_args()
_prefix = os.path.basename(__file__)
roctx.mark(f"iterations: {args.num_iterations}")
for i in range(args.num_iterations):
with RoctxRange("main loop"):
ans_a, ans_b = run(args.value)
print(f"[{_prefix}] [{i}] result of run({args.value}) = {ans_a}, {ans_b}\n")
+5
Просмотреть файл
@@ -0,0 +1,5 @@
[pytest]
addopts = --durations=20 -rA -s -vv
testpaths = validate.py
pythonpath = @ROCPROFILER_SDK_TESTS_BINARY_DIR@/pytest-packages
+115
Просмотреть файл
@@ -0,0 +1,115 @@
#!/usr/bin/env python3
import sys
import pytest
import re
import os
from collections import Counter
def test_agent_info(agent_info_input_data):
logical_node_id = max([int(itr["Logical_Node_Id"]) for itr in agent_info_input_data])
assert logical_node_id + 1 == len(agent_info_input_data)
for row in agent_info_input_data:
agent_type = row["Agent_Type"]
assert agent_type in ("CPU", "GPU")
if agent_type == "CPU":
assert int(row["Cpu_Cores_Count"]) > 0
assert int(row["Simd_Count"]) == 0
assert int(row["Max_Waves_Per_Simd"]) == 0
else:
assert int(row["Cpu_Cores_Count"]) == 0
assert int(row["Simd_Count"]) > 0
assert int(row["Max_Waves_Per_Simd"]) > 0
def extract_number(pattern, string):
match = re.match(pattern, string)
if match:
return int(match.group(1))
else:
raise ValueError(f"Pattern '{pattern}' not found in '{string}'.")
def find_key_with_substring(data, substring):
return next((k for k in data.keys() if substring in k), None)
def check_tot_data(tot_data):
iteration_msg = find_key_with_substring(tot_data, "iterations:")
assert tot_data[iteration_msg] == 1
num_iterations = extract_number(r"iterations: (\d+)", iteration_msg)
assert tot_data["main loop"] == num_iterations
if num_iterations > 0:
run_msg = find_key_with_substring(tot_data, "run")
if run_msg is not None:
value = extract_number(r"run\((\d+)\)", run_msg)
assert tot_data[f"run({value})"] == num_iterations
assert "fib" in tot_data.keys()
assert tot_data[f"fib(n={value})"] == num_iterations
for n in range(value, int(max([value / 2, value - 10]))):
assert f"fib(n={n})" in tot_data.keys()
assert tot_data[f"inefficient({value})"] == num_iterations
assert tot_data[f"sum"] == num_iterations
sum_msg = find_key_with_substring(tot_data, "sum(nelem=")
assert tot_data[sum_msg] == num_iterations
def test_marker_api_trace(marker_input_data):
functions = []
for row in marker_input_data:
assert row["Domain"] in [
"MARKER_CORE_API",
"MARKER_CONTROL_API",
"MARKER_NAME_API",
]
assert int(row["Process_Id"]) > 0
assert int(row["Thread_Id"]) == 0 or int(row["Thread_Id"]) >= int(
row["Process_Id"]
)
assert int(row["End_Timestamp"]) >= int(row["Start_Timestamp"])
functions.append(row["Function"])
check_tot_data(Counter(functions))
def test_marker_api_trace_json(json_data):
data = json_data["rocprofiler-sdk-tool"]
def get_kind_name(kind_id):
return data.strings.buffer_records[kind_id]["kind"]
valid_domain = ("MARKER_CORE_API", "MARKER_CONTROL_API", "MARKER_NAME_API")
marker_data = data.buffer_records.marker_api
for marker in marker_data:
assert get_kind_name(marker["kind"]) in valid_domain
assert marker["thread_id"] >= data["metadata"]["pid"]
assert marker["end_timestamp"] >= marker["start_timestamp"]
tot_data = Counter([m["value"] for m in data.strings.marker_api])
check_tot_data(tot_data)
def test_perfetto_data(pftrace_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_perfetto_data(
pftrace_data, json_data, ("hip", "hsa", "marker", "kernel", "memory_copy")
)
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)