diff --git a/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.cpp b/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.cpp index 9f5008006fc..a19f0ae831f 100644 --- a/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.cpp +++ b/projects/rocprofiler-sdk/source/lib/python/rocpd/source/perfetto.cpp @@ -271,6 +271,15 @@ write_perfetto( return &pmc_info.at(pmc_id); }; + auto read_region_args = [&conn, &process, &ocfg](uint64_t region_id) { + if(!ocfg.annotate_args) return std::vector{}; + + return rocpd::read_sql_query( + conn, + fmt::format( + "SELECT * FROM region_args WHERE guid='{}' AND id={}", process.guid, region_id)); + }; + { for(auto ditr : memory_copy_gen) for(const auto& itr : memory_copy_gen.get(ditr)) @@ -437,6 +446,7 @@ write_perfetto( auto _pmc_events = read_pmc_events(itr.event_id); auto _event = (ocfg.annotate_kfd) ? read_event(itr.event_id) : types::event{}; + auto _args = read_region_args(itr.id); auto _category = ::perfetto::DynamicCategory{get_category_string(itr.category)}; TRACE_EVENT_BEGIN( @@ -497,6 +507,11 @@ write_perfetto( _extdata.kfd.value().record); } } + + for(const auto& a : _args) + { + rocprofiler::sdk::add_perfetto_annotation(ctx, a.name, a.value); + } }); TRACE_EVENT_END(_category, track, itr.end); diff --git a/projects/rocprofiler-sdk/tests/pytest-packages/tests/rocprofv3.py b/projects/rocprofiler-sdk/tests/pytest-packages/tests/rocprofv3.py index e7113abbf49..f8311e733c6 100644 --- a/projects/rocprofiler-sdk/tests/pytest-packages/tests/rocprofv3.py +++ b/projects/rocprofiler-sdk/tests/pytest-packages/tests/rocprofv3.py @@ -516,3 +516,59 @@ def get_json_corr_id(x): for a, b in zip(_csv_data_sorted, _js_data_sorted): _perform_csv_json_match(a, b, keys_mapping[category], json_data) + + +def test_perfetto_arg_annotations(pftrace_reader): + """ + Test that function argument annotations are available in perfetto with --annotate-args. + This validates that all API call arguments are annotated as debug annotations + across all categories (hip_api, marker_api, etc.). + """ + import pytest + + # Query for API function argument annotations from --annotate-args + # Filter for hip_api/hsa_api/marker_api (KFD/kernel have args from other sources) + # Exclude metadata fields (always present, even without --annotate-args) + query = """ + SELECT + slice.name as slice_name, + slice.category as slice_category, + slice.id as slice_id, + args.key as arg_name, + args.string_value as arg_value + FROM slice + JOIN args ON slice.arg_set_id = args.arg_set_id + WHERE args.key LIKE 'debug.%' + AND slice.category IN ('hip_api', 'hsa_api', 'marker_api') + AND args.key NOT IN ( + 'debug.begin_ns', 'debug.end_ns', 'debug.delta_ns', + 'debug.tid', 'debug.kind', 'debug.operation', + 'debug.corr_id', 'debug.ancestor_id' + ) + """ + + result = pftrace_reader.query_tp(query) + + # Function argument annotations must exist - perfetto was generated with --annotate-args + assert not result.empty, ( + "No function argument annotations found - --annotate-args may be broken. " + "Only metadata fields were found, which are always present." + ) + + # Validate the structure + assert "slice_name" in result.columns + assert "slice_category" in result.columns + assert "arg_name" in result.columns + assert "arg_value" in result.columns + + # Get statistics for debugging + unique_slices = result["slice_name"].nunique() + unique_args = result["arg_name"].nunique() + unique_categories = result["slice_category"].nunique() + categories = result["slice_category"].unique() + + print(f"\nValidation passed: Found {len(result)} argument annotations") + print(f" - {unique_slices} unique API calls annotated") + print(f" - {unique_categories} categories: {list(categories)}") + print(f" - {unique_args} unique argument types") + print(f" - Sample argument names: {list(result['arg_name'].unique()[:10])}") diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/CMakeLists.txt b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/CMakeLists.txt index ef10bb7720f..a58c3bca4ac 100644 --- a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/CMakeLists.txt +++ b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/CMakeLists.txt @@ -137,6 +137,20 @@ rocprofiler_add_integration_execute_test( FIXTURES_SETUP rocprofv3-test-rocpd-generation FIXTURES_REQUIRED rocprofv3-test-rocpd) +rocprofiler_add_integration_execute_test( + rocprofv3-test-rocpd-perfetto-generation-annotations + COMMAND + ${Python3_EXECUTABLE} -m rocpd convert -f pftrace --kernel-rename --annotate-args + -d ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data-annotations -i + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data/out_results.db + DEPENDS rocprofiler-sdk::rocprofv3 + TIMEOUT 120 + LABELS "integration-tests;rocpd" + PRELOAD "${ROCPROFILER_MEMCHECK_PRELOAD_ENV_VALUE}" + ENVIRONMENT "${rocprofv3-rocpd-env}" + FIXTURES_SETUP rocprofv3-test-rocpd-generation-annotations + FIXTURES_REQUIRED rocprofv3-test-rocpd) + rocprofiler_add_integration_execute_test( rocprofv3-test-rocpd-perfetto-generation-multiproc COMMAND @@ -239,6 +253,17 @@ rocprofiler_add_integration_validate_test( LABELS "integration-tests;rocpd" FIXTURES_REQUIRED rocprofv3-test-rocpd-generation) +rocprofiler_add_integration_validate_test( + rocprofv3-test-rocpd-annotations + TEST_PATHS validate_annotations.py + COPY conftest.py + CONFIG pytest.ini + ARGS --pftrace-input + ${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data-annotations/out_results.pftrace + TIMEOUT 120 + LABELS "integration-tests;rocpd" + FIXTURES_REQUIRED rocprofv3-test-rocpd-generation-annotations) + ######################################################################################### # # Package generation diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/conftest.py b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/conftest.py index af17f431721..4a25ae3e92d 100644 --- a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/conftest.py +++ b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/conftest.py @@ -82,9 +82,14 @@ def json_data(request): @pytest.fixture -def pftrace_data(request): +def pftrace_reader(request): filename = request.config.getoption("--pftrace-input") - return PerfettoReader(filename).read()[0] + return PerfettoReader(filename) + + +@pytest.fixture +def pftrace_data(pftrace_reader): + return pftrace_reader.read()[0] @pytest.fixture diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/validate_annotations.py b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/validate_annotations.py new file mode 100644 index 00000000000..491b5319fdd --- /dev/null +++ b/projects/rocprofiler-sdk/tests/rocprofv3/rocpd/validate_annotations.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +# MIT License +# +# Copyright (c) 2024-2025 Advanced Micro Devices, Inc. All rights reserved. +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +import sys +import pytest + + +def test_arg_annotations(pftrace_reader): + import rocprofiler_sdk.tests.rocprofv3 as rocprofv3 + + rocprofv3.test_perfetto_arg_annotations(pftrace_reader) + + +if __name__ == "__main__": + exit_code = pytest.main(["-x", __file__] + sys.argv[1:]) + sys.exit(exit_code)