diff --git a/source/bin/rocprofv3 b/source/bin/rocprofv3 index b79dda0491..6e36dfc0ee 100755 --- a/source/bin/rocprofv3 +++ b/source/bin/rocprofv3 @@ -39,6 +39,7 @@ usage() { echo -e "\t#${GREY} usage e.g:(with custom dir): rocprofv3 --hsa-trace -d ${RESET}" echo -e "${GREEN}-M | --mangled-kernels ${RESET} Do not demangle the kernel names" echo -e "${GREEN}-T | --truncate-kernels ${RESET} Truncate the demangled kernel names" + echo -e "${GREEN}-L | --list-metrics ${RESET} List metrics" echo -e "" exit ${EC} } @@ -75,6 +76,7 @@ while true; do elif [[ "$1" == "-o" || "$1" == "--output-file-name" ]]; then if [ "$2" ]; then export ROCPROF_OUTPUT_FILE_NAME=$2 + export ROCPROF_OUTPUT_LIST_METRICS_FILE=1 else usage 1 fi @@ -84,6 +86,7 @@ while true; do if [ "$2" ]; then ROCPROF_OUTPUT_PATH_INTERNAL=$2 export ROCPROF_OUTPUT_PATH=$ROCPROF_OUTPUT_PATH_INTERNAL + export ROCPROF_OUTPUT_LIST_METRICS_FILE=1 else usage 1 fi @@ -92,6 +95,12 @@ while true; do elif [ "$1" == "--hsa-trace" ]; then export ROCPROF_HSA_API_TRACE=1 shift + elif [[ "$1" == "-L" || "$1" == "--list-metrics" ]]; then + export ROCPROF_LIST_METRICS=1 + ROCP_TOOL_LIBRARIES="${ROCM_DIR}/lib/rocprofiler-sdk/librocprofiler-sdk-tool.so" \ + LD_LIBRARY_PATH=${ROCM_DIR}/lib:${LD_LIBRARY_PATH} \ + LD_PRELOAD="${ROCPROF_PRELOAD}:${ROCM_DIR}/lib/librocprofiler-sdk.so" \ + exec ${ROCM_DIR}/lib/rocprofiler-sdk/rocprofv3-trigger-list-metrics elif [ "$1" == "--kernel-trace" ]; then export ROCPROF_KERNEL_TRACE=1 shift diff --git a/source/lib/rocprofiler-sdk-tool/CMakeLists.txt b/source/lib/rocprofiler-sdk-tool/CMakeLists.txt index cb3266835b..7d1eeec55d 100644 --- a/source/lib/rocprofiler-sdk-tool/CMakeLists.txt +++ b/source/lib/rocprofiler-sdk-tool/CMakeLists.txt @@ -34,3 +34,18 @@ install( DESTINATION ${CMAKE_INSTALL_LIBDIR}/rocprofiler-sdk COMPONENT tools EXPORT rocprofiler-sdk-tool-targets) + +add_executable(rocprofv3-trigger-list-metrics) +target_sources(rocprofv3-trigger-list-metrics PRIVATE rocprofv3_trigger_list_metrics.cpp) +target_link_libraries(rocprofv3-trigger-list-metrics + PRIVATE rocprofiler::rocprofiler-hsa-runtime) +set_target_properties( + rocprofv3-trigger-list-metrics + PROPERTIES RUNTIME_OUTPUT_DIRECTORY + ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/rocprofiler-sdk) + +install( + TARGETS rocprofv3-trigger-list-metrics + DESTINATION ${CMAKE_INSTALL_LIBDIR}/rocprofiler-sdk + COMPONENT tools + EXPORT rocprofiler-sdk-tool-targets) diff --git a/source/lib/rocprofiler-sdk-tool/config.hpp b/source/lib/rocprofiler-sdk-tool/config.hpp index 3e379bcae9..63d9e1a61d 100644 --- a/source/lib/rocprofiler-sdk-tool/config.hpp +++ b/source/lib/rocprofiler-sdk-tool/config.hpp @@ -57,17 +57,19 @@ struct config { config(); - bool demangle = get_env("ROCPROF_DEMANGLE_KERNELS", true); - bool truncate = get_env("ROCPROF_TRUNCATE_KERNELS", false); - bool kernel_trace = get_env("ROCPROF_KERNEL_TRACE", false); - bool hsa_api_trace = get_env("ROCPROF_HSA_API_TRACE", false); - bool marker_api_trace = get_env("ROCPROF_MARKER_API_TRACE", false); - bool memory_copy_trace = get_env("ROCPROF_MEMORY_COPY_TRACE", false); - bool counter_collection = get_env("ROCPROF_COUNTER_COLLECTION", false); - bool hip_api_trace = get_env("ROCPROF_HIP_API_TRACE", false); - bool hip_compiler_api_trace = get_env("ROCPROF_HIP_COMPILER_API_TRACE", false); - int mpi_size = get_mpi_size(); - int mpi_rank = get_mpi_rank(); + bool demangle = get_env("ROCPROF_DEMANGLE_KERNELS", true); + bool truncate = get_env("ROCPROF_TRUNCATE_KERNELS", false); + bool kernel_trace = get_env("ROCPROF_KERNEL_TRACE", false); + bool hsa_api_trace = get_env("ROCPROF_HSA_API_TRACE", false); + bool marker_api_trace = get_env("ROCPROF_MARKER_API_TRACE", false); + bool memory_copy_trace = get_env("ROCPROF_MEMORY_COPY_TRACE", false); + bool counter_collection = get_env("ROCPROF_COUNTER_COLLECTION", false); + bool hip_api_trace = get_env("ROCPROF_HIP_API_TRACE", false); + bool hip_compiler_api_trace = get_env("ROCPROF_HIP_COMPILER_API_TRACE", false); + bool list_metrics = get_env("ROCPROF_LIST_METRICS", false); + bool list_metrics_output_file = get_env("ROCPROF_OUTPUT_LIST_METRICS_FILE", false); + int mpi_size = get_mpi_size(); + int mpi_rank = get_mpi_rank(); std::string output_path = get_env("ROCPROF_OUTPUT_PATH", fs::current_path().string()); std::string output_file = get_env("ROCPROF_OUTPUT_FILE_NAME", std::to_string(getpid())); std::vector kernel_names = {}; diff --git a/source/lib/rocprofiler-sdk-tool/csv.hpp b/source/lib/rocprofiler-sdk-tool/csv.hpp index d16ab91247..f383b97532 100644 --- a/source/lib/rocprofiler-sdk-tool/csv.hpp +++ b/source/lib/rocprofiler-sdk-tool/csv.hpp @@ -74,11 +74,13 @@ struct csv_encoder } }; -using api_csv_encoder = csv_encoder<7>; -using kernel_trace_csv_encoder = csv_encoder<16>; -using counter_collection_csv_encoder = csv_encoder<15>; -using memory_copy_csv_encoder = csv_encoder<7>; -using marker_csv_encoder = csv_encoder<7>; +using api_csv_encoder = csv_encoder<7>; +using kernel_trace_csv_encoder = csv_encoder<16>; +using counter_collection_csv_encoder = csv_encoder<15>; +using memory_copy_csv_encoder = csv_encoder<7>; +using marker_csv_encoder = csv_encoder<7>; +using list_basic_metrics_csv_encoder = csv_encoder<5>; +using list_derived_metrics_csv_encoder = csv_encoder<5>; } // namespace csv } // namespace tool } // namespace rocprofiler diff --git a/source/lib/rocprofiler-sdk-tool/rocprofv3_trigger_list_metrics.cpp b/source/lib/rocprofiler-sdk-tool/rocprofv3_trigger_list_metrics.cpp new file mode 100644 index 0000000000..ee1df1eb5f --- /dev/null +++ b/source/lib/rocprofiler-sdk-tool/rocprofv3_trigger_list_metrics.cpp @@ -0,0 +1,30 @@ +// MIT License +// +// Copyright (c) 2023 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. + +#include + +int +main() +{ + hsa_init(); + return 0; +} diff --git a/source/lib/rocprofiler-sdk-tool/tool.cpp b/source/lib/rocprofiler-sdk-tool/tool.cpp index a7a07f0dc8..a3d4a75b79 100644 --- a/source/lib/rocprofiler-sdk-tool/tool.cpp +++ b/source/lib/rocprofiler-sdk-tool/tool.cpp @@ -200,6 +200,28 @@ get_marker_api_file() return _v; } +tool::output_file*& +get_list_basic_metrics_file() +{ + static auto* _v = + new tool::output_file{"basic_metrics", + tool::csv::list_basic_metrics_csv_encoder{}, + {"Agent-id", "Name", "Description", "Block", "Dimensions"}}; + ADD_DESTRUCTOR(_v); + return _v; +} + +tool::output_file*& +get_list_derived_metrics_file() +{ + static auto* _v = + new tool::output_file{"derived_metrics", + tool::csv::list_derived_metrics_csv_encoder{}, + {"Agent-id", "Name", "Description", "Expression", "Dimensions"}}; + ADD_DESTRUCTOR(_v); + return _v; +} + #undef ADD_DESTRUCTOR struct marker_entry @@ -694,15 +716,25 @@ rocprofiler_status_t dimensions_info_callback(rocprofiler_counter_id_t id, const rocprofiler_record_dimension_info_t* dim_info, long unsigned int num_dims, - void*) + void* user_data) { - counter_dimension_data.wlock( - [&id, &dim_info, &num_dims](counter_dimension_info_map_t& counter_dimension_data_v) { - std::vector dimensions; - for(size_t dim = 0; dim < num_dims; dim++) - dimensions.emplace_back(dim_info[dim]); - counter_dimension_data_v.emplace(std::make_pair(id.handle, dimensions)); - }); + if(user_data != nullptr) + { + auto* dimensions_info = + static_cast*>(user_data); + for(size_t j = 0; j < num_dims; j++) + dimensions_info->push_back(dim_info[j]); + } + else + { + counter_dimension_data.wlock( + [&id, &dim_info, &num_dims](counter_dimension_info_map_t& counter_dimension_data_v) { + std::vector dimensions; + for(size_t dim = 0; dim < num_dims; dim++) + dimensions.emplace_back(dim_info[dim]); + counter_dimension_data_v.emplace(std::make_pair(id.handle, dimensions)); + }); + } return ROCPROFILER_STATUS_SUCCESS; } @@ -771,6 +803,111 @@ get_agent_profile(const rocprofiler_agent_t* agent) return profile; } +rocprofiler_status_t +list_metrics_iterate_agents(const rocprofiler_agent_t** agents, size_t num_agents, void*) +{ + for(size_t idx = 0; idx < num_agents; idx++) + { + auto counters_v = counter_vec_t{}; + uint32_t node_id = agents[idx]->node_id; + ROCPROFILER_CALL( + rocprofiler_iterate_agent_supported_counters( + agents[idx]->id, + [](rocprofiler_agent_id_t, + rocprofiler_counter_id_t* counters, + size_t num_counters, + void* user_data) { + auto* agent_node_id = static_cast(user_data); + for(size_t i = 0; i < num_counters; i++) + { + rocprofiler_counter_info_v0_t counter_info; + auto dimensions = std::vector{}; + ROCPROFILER_CALL( + rocprofiler_iterate_counter_dimensions(counters[i], + dimensions_info_callback, + static_cast(&dimensions)), + "iterate_dimension_info"); + + ROCPROFILER_CALL( + rocprofiler_query_counter_info(counters[i], + ROCPROFILER_COUNTER_INFO_VERSION_0, + static_cast(&counter_info)), + "Could not query counter_id"); + + auto dimensions_info = std::stringstream{}; + for(size_t j = 0; j != dimensions.size(); j++) + { + dimensions_info << dimensions[j].name + << "[0:" << dimensions[j].instance_size - 1 << "]"; + if(j != dimensions.size() - 1) dimensions_info << "\t"; + } + if(!counter_info.is_derived && tool::get_config().list_metrics && + !std::string(counter_info.block).empty()) + { + auto counter_info_ss = std::stringstream{}; + if(tool::get_config().list_metrics_output_file) + { + tool::csv::list_basic_metrics_csv_encoder::write_row( + counter_info_ss, + *agent_node_id, + counter_info.name, + counter_info.description, + counter_info.block, + dimensions_info.str()); + get_dereference(get_list_basic_metrics_file()) + << counter_info_ss.str(); + } + else + { + counter_info_ss << "gpu-agent" << *agent_node_id << ":" + << "\t" << counter_info.name << "\n"; + counter_info_ss << "Description:" + << "\t" << counter_info.description << "\n"; + counter_info_ss << "Block:" + << "\t" << counter_info.block << "\n"; + counter_info_ss << "Dimensions:" + << "\t" << dimensions_info.str() << "\n"; + counter_info_ss << "\n"; + std::cout << counter_info_ss.str(); + } + } + else if(counter_info.is_derived && tool::get_config().list_metrics) + { + auto counter_info_ss = std::stringstream{}; + if(tool::get_config().list_metrics_output_file) + { + tool::csv::list_derived_metrics_csv_encoder::write_row( + counter_info_ss, + *agent_node_id, + counter_info.name, + counter_info.description, + counter_info.expression, + dimensions_info.str()); + get_dereference(get_list_derived_metrics_file()) + << counter_info_ss.str(); + } + else + { + counter_info_ss << "gpu-agent" << *agent_node_id << ":" + << "\t" << counter_info.name << "\n" + << "Description: " << counter_info.description + << "\n"; + counter_info_ss << "Expression: " << counter_info.expression + << "\n"; + counter_info_ss << "Dimensions: " << dimensions_info.str() << "\n"; + counter_info_ss << "\n"; + std::cout << counter_info_ss.str(); + } + } + } + return ROCPROFILER_STATUS_SUCCESS; + }, + reinterpret_cast(&node_id)), + "Iterate rocprofiler counters"); + } + return ROCPROFILER_STATUS_SUCCESS; +} + void dispatch_callback(rocprofiler_queue_id_t queue_id, const rocprofiler_agent_t* agent, @@ -1004,6 +1141,19 @@ tool_init(rocprofiler_client_finalize_t fini_func, void* tool_data) return 0; } +void +api_registration_callback(rocprofiler_intercept_table_t, + uint64_t, + uint64_t, + void**, + uint64_t, + void*) +{ + ROCPROFILER_CALL(rocprofiler_query_available_agents( + list_metrics_iterate_agents, sizeof(rocprofiler_agent_t), nullptr), + "Iterate rocporfiler agents") +} + void tool_fini(void* tool_data) { @@ -1049,6 +1199,14 @@ rocprofiler_configure(uint32_t version, uint32_t minor = (version % 10000) / 100; uint32_t patch = version % 100; + if(tool::get_config().list_metrics) + { + ROCPROFILER_CALL(rocprofiler_at_intercept_table_registration( + api_registration_callback, ROCPROFILER_HSA_TABLE, nullptr), + "api registration"); + return nullptr; + } + LOG(INFO) << id->name << " is using rocprofiler-sdk v" << major << "." << minor << "." << patch << " (" << runtime_version << ")"; diff --git a/tests/rocprofv3/counter-collection/CMakeLists.txt b/tests/rocprofv3/counter-collection/CMakeLists.txt index 36ee2980d2..f26986d80e 100644 --- a/tests/rocprofv3/counter-collection/CMakeLists.txt +++ b/tests/rocprofv3/counter-collection/CMakeLists.txt @@ -5,3 +5,4 @@ endforeach() add_subdirectory(input1) add_subdirectory(input2) +add_subdirectory(list_metrics) diff --git a/tests/rocprofv3/counter-collection/list_metrics/CMakeLists.txt b/tests/rocprofv3/counter-collection/list_metrics/CMakeLists.txt new file mode 100644 index 0000000000..edbb364d55 --- /dev/null +++ b/tests/rocprofv3/counter-collection/list_metrics/CMakeLists.txt @@ -0,0 +1,81 @@ +# +# rocprofv3 tool test +# +cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR) + +project( + rocprofiler-tests-counter-collection + LANGUAGES CXX + VERSION 0.0.0) + +find_package(rocprofiler-sdk REQUIRED) + +foreach(FILENAME validate.py conftest.py) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME} + ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME} COPYONLY) +endforeach() + +# basic-metrics +add_test(NAME rocprofv3-test-list-metrics-execute + COMMAND $ -d + ${CMAKE_CURRENT_BINARY_DIR}/out_cc_2 -o metrics --list-metrics) + +# list-metrics-stdout +add_test(NAME rocprofv3-test-list-metrics-std-out-execute + COMMAND $ --list-metrics) + +string(REPLACE "LD_PRELOAD=" "ROCPROF_PRELOAD=" PRELOAD_ENV + "${ROCPROFILER_MEMCHECK_PRELOAD_ENV}") + +if(ROCPROFILER_MEMCHECK STREQUAL "LeakSanitizer") + set(LOG_LEVEL "warning") # info produces memory leak +else() + set(LOG_LEVEL "info") +endif() + +set(cc-env-list-metrics + "${PRELOAD_ENV}" "ROCPROF_LOG_LEVEL=${LOG_LEVEL}" + "ROCPROFILER_LOG_LEVEL=${LOG_LEVEL}" + "HSA_TOOLS_LIB=$") + +set_tests_properties( + rocprofv3-test-list-metrics-execute + PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT "${cc-env-list-metrics}" + FAIL_REGULAR_EXPRESSION "threw an exception") + +set_tests_properties( + rocprofv3-test-list-metrics-std-out-execute + PROPERTIES + TIMEOUT + 45 + LABELS + "integration-tests" + ENVIRONMENT + "${cc-env-list-metrics}" + PASS_REGULAR_EXPRESSION + "gpu-agent[0-9]*:[a-zA-Z_]*\\n;Description:(.*)\\n*;Expression:(.)*\\n*;Block:[a-zA-Z]*\\n*;Dimensions:([A-Z_]*\\[[0-9]*:[0-9]*\\])*\\n*;" + ) +set(VALIDATION_FILES ${CMAKE_CURRENT_BINARY_DIR}/out_cc_2/metrics_basic_metrics.csv + ${CMAKE_CURRENT_BINARY_DIR}/out_cc_2/metrics_derived_metrics.csv) + +add_test( + NAME rocprofv3-test-list-metrics-validate + COMMAND + ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py + --derived-metrics-input + ${CMAKE_CURRENT_BINARY_DIR}/out_cc_2/metrics_derived_metrics.csv + --basic-metrics-input + ${CMAKE_CURRENT_BINARY_DIR}/out_cc_2/metrics_basic_metrics.csv) + +set_tests_properties( + rocprofv3-test-list-metrics-validate + PROPERTIES TIMEOUT + 45 + LABELS + "integration-tests" + DEPENDS + rocprofv3-test-list-metrics-execute + FAIL_REGULAR_EXPRESSION + "threw an exception" + ATTACHED_FILES_ON_FAIL + "${VALIDATION_FILES}") diff --git a/tests/rocprofv3/counter-collection/list_metrics/conftest.py b/tests/rocprofv3/counter-collection/list_metrics/conftest.py new file mode 100644 index 0000000000..340f9df28f --- /dev/null +++ b/tests/rocprofv3/counter-collection/list_metrics/conftest.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +import csv +import pytest +import pandas as pd + + +def pytest_addoption(parser): + parser.addoption("--basic-metrics-input", action="store", help="Path to csv file.") + parser.addoption("--derived-metrics-input", action="store", help="Path to csv file.") + + +@pytest.fixture +def derived_metrics_input_data(request): + filename = request.config.getoption("--derived-metrics-input") + data = [] + if filename: + with open(filename, "r") as inp: + reader = csv.DictReader(inp) + for row in reader: + data.append(row) + + return data + + +@pytest.fixture +def basic_metrics_input_data(request): + filename = request.config.getoption("--basic-metrics-input") + data = [] + if filename: + with open(filename, "r") as inp: + reader = csv.DictReader(inp) + for row in reader: + data.append(row) + + return data diff --git a/tests/rocprofv3/counter-collection/list_metrics/validate.py b/tests/rocprofv3/counter-collection/list_metrics/validate.py new file mode 100644 index 0000000000..df3eeb24c4 --- /dev/null +++ b/tests/rocprofv3/counter-collection/list_metrics/validate.py @@ -0,0 +1,34 @@ +import pandas as pd +import sys +import pytest + + +def test_validate_list_basic_metrics(basic_metrics_input_data): + for row in basic_metrics_input_data: + assert row["Agent-id"].isdigit() == True + assert row["Name"] != "" + assert row["Description"] != "" + assert row["Block"] != "" + assert row["Dimensions"] != "" + if row["Name"] == "SQ_WAVES": + row[ + "Description" + ] == "Count number of waves sent to SQs. (per-simd, emulated, global)" + row["Block"] == "SQ" + + +def test_validate_list_derived_metrics(derived_metrics_input_data): + for row in derived_metrics_input_data: + assert row["Agent-id"].isdigit() == True + assert row["Name"] != "" + assert row["Description"] != "" + assert row["Expression"] != "" + assert row["Dimensions"] != "" + if row["Name"] == "TA_BUSY_min": + row["Description"] == "TA block is busy. Min over TA instances." + row["Expression"] == "reduce(TA_TA_BUSY,min)" + + +if __name__ == "__main__": + exit_code = pytest.main(["-x", __file__] + sys.argv[1:]) + sys.exit(exit_code)