diff --git a/projects/rocprofiler-sdk/CHANGELOG.md b/projects/rocprofiler-sdk/CHANGELOG.md index 8e7a87d8ee..fa31ce0d60 100644 --- a/projects/rocprofiler-sdk/CHANGELOG.md +++ b/projects/rocprofiler-sdk/CHANGELOG.md @@ -191,6 +191,8 @@ Full documentation for ROCprofiler-SDK is available at [rocm.docs.amd.com/projec - Updated disassembly.hpp's vaddr-to-file-offset mapping to use the dedicated comgr API. - rocprofv3 shorthand argument for `--collection-period` is now `-P` (upper-case) as `-p` (lower-case) is reserved for later use - default output format for rocprofv3 is now `rocpd` (SQLite3 database) +- rocprofv3 avail tool renamed from rocprofv3_avail to rocprofv3-avail tool +- rocprofv3 avail tool has support for command line arguments. ### Resolved issues diff --git a/projects/rocprofiler-sdk/cmake/Templates/rocprofiler-sdk/config.cmake.in b/projects/rocprofiler-sdk/cmake/Templates/rocprofiler-sdk/config.cmake.in index 51ceaba334..c0964cd07d 100644 --- a/projects/rocprofiler-sdk/cmake/Templates/rocprofiler-sdk/config.cmake.in +++ b/projects/rocprofiler-sdk/cmake/Templates/rocprofiler-sdk/config.cmake.in @@ -150,6 +150,11 @@ set_property( PROPERTY IMPORTED_LOCATION ${@PACKAGE_NAME@_ROOT_DIR}/@CMAKE_INSTALL_BINDIR@/rocprofv3) +add_executable(@PACKAGE_NAME@::rocprofv3-avail IMPORTED) +set_property( + TARGET @PACKAGE_NAME@::rocprofv3-avail + PROPERTY IMPORTED_LOCATION ${@PACKAGE_NAME@_ROOT_DIR}/@CMAKE_INSTALL_BINDIR@/rocprofv3-avail) + include(FindPackageHandleStandardArgs) find_package_handle_standard_args( @PACKAGE_NAME@ diff --git a/projects/rocprofiler-sdk/source/bin/CMakeLists.txt b/projects/rocprofiler-sdk/source/bin/CMakeLists.txt index d6bda044bc..cba31cf1cc 100644 --- a/projects/rocprofiler-sdk/source/bin/CMakeLists.txt +++ b/projects/rocprofiler-sdk/source/bin/CMakeLists.txt @@ -4,6 +4,8 @@ rocprofiler_activate_clang_tidy() +add_subdirectory(rocprofv3_avail_module) + # Adding main rocprofv3 configure_file(rocprofv3.py ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/rocprofv3 @ONLY) @@ -14,11 +16,11 @@ install( WORLD_EXECUTE COMPONENT tools) -configure_file(rocprofv3_avail.py - ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/rocprofv3_avail COPYONLY) +configure_file(rocprofv3-avail.py + ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/rocprofv3-avail COPYONLY) install( - FILES ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/rocprofv3_avail + FILES ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/rocprofv3-avail DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE diff --git a/projects/rocprofiler-sdk/source/bin/rocprofv3-avail.py b/projects/rocprofiler-sdk/source/bin/rocprofv3-avail.py new file mode 100755 index 0000000000..50e7d7b465 --- /dev/null +++ b/projects/rocprofiler-sdk/source/bin/rocprofv3-avail.py @@ -0,0 +1,425 @@ +#!/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 os +import argparse +import sys +from rocprofv3_avail_module import avail + + +def format_help(formatter, w=120, h=40): + """Return a wider HelpFormatter, if possible.""" + try: + kwargs = {"width": w, "max_help_position": h} + formatter(None, **kwargs) + return lambda prog: formatter(prog, **kwargs) + except TypeError: + return formatter + + +def strtobool(val): + """Convert a string representation of truth to true or false. + True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values + are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if + 'val' is anything else. + """ + if isinstance(val, (list, tuple)): + if len(val) > 1: + val_type = type(val).__name__ + raise ValueError(f"invalid truth value {val} (type={val_type})") + else: + val = val[0] + + if isinstance(val, bool): + return val + elif isinstance(val, str) and val.lower() in ("y", "yes", "t", "true", "on", "1"): + return True + elif isinstance(val, str) and val.lower() in ("n", "no", "f", "false", "off", "0"): + return False + else: + val_type = type(val).__name__ + raise ValueError(f"invalid truth value {val} (type={val_type})") + + +class booleanArgAction(argparse.Action): + def __call__(self, parser, args, value, option_string=None): + setattr(args, self.dest, strtobool(value)) + + +def parse_arguments(args=None): + + usage_examples = """ + +%(prog)s, e.g. + + $ rocprofv3-avail [ ...] + $ rocprofv3-avail -- avail-hw-counters + +""" + + def add_list_options(subparsers): + list_command = subparsers.add_parser( + "list", help="List options for hw counters, agents and pc-sampling support" + ) + add_parser_bool_argument(list_command, "--pmc", help="List counters") + add_parser_bool_argument( + list_command, "--agent", help="List basic info of agents" + ) + add_parser_bool_argument( + list_command, "--pc-sampling", help="List agents supporting pc-sampling" + ) + list_command.set_defaults(func=process_list) + + def add_info_options(subparsers): + info_command = subparsers.add_parser( + "info", + help="Info options for detailed information of counters, agents, and pc-sampling configurations", + ) + + info_command.add_argument("--pmc", nargs="*", help="PMC info") + info_command.add_argument( + "--pc-sampling", nargs="*", help="Detailed PC Sampling info" + ) + info_command.set_defaults(func=process_info) + + def add_pmc_check_options(subparsers): + pmc_check_command = subparsers.add_parser( + "pmc-check", help="Checking counters collection support on agents" + ) + pmc_check_command.add_argument( + "pmc", nargs="*", default=None, help="List of PMC names" + ) + pmc_check_command.set_defaults(func=process_pmc_check) + + def add_parser_bool_argument(gparser, *args, **kwargs): + gparser.add_argument( + *args, + **kwargs, + action=booleanArgAction, + nargs="?", + const=True, + type=str, + required=False, + metavar="BOOL", + ) + + # Create the parser + parser = argparse.ArgumentParser( + description="ROCProfilerV3-avail Run Script", + usage="%(prog)s [options] ", + epilog=usage_examples, + formatter_class=format_help(argparse.RawTextHelpFormatter), + ) + + parser.add_argument( + "-d", + "--device", + help="device index, device on a node to apply the sub-commands on", + type=int, + default=None, + ) + + subparsers = parser.add_subparsers( + dest="command", help="rocprofv3-avail sub commands" + ) + + add_list_options(subparsers) + add_info_options(subparsers) + add_pmc_check_options(subparsers) + + return parser.parse_args(args[:]) + + +def get_basic_agent_info(info): + basic_info = {} + req_keys = [ + "cpu_cores_count", + "simd_count", + "max_waves_per_simd", + "runtime_visibility", + "wave_front_size", + "num_xcc", + "cu_count", + "array_count", + "num_shader_banks", + "simd_arrays_per_engine", + "cu_per_simd_array", + "simd_per_cu", + "gfx_target_version", + "max_waves_per_cu", + "gpu_id", + "workgroup_max_dim", + "grid_max_dim", + "name", + "vendor_name", + "product_name", + "model_name", + "runtime_visibility", + "node_id", + "logical_node_id", + "logical_node_type_id", + ] + for key in req_keys: + basic_info.update({key: info[key]}) + return basic_info + + +def list_basic_agent(args, list_counters): + def print_agent_counter(counters, info): + names = ["{:20}".format(counter.name) for counter in counters] + print(" PMC:\n") + for idx in range(0, len(names), int(len(counters) / 20)): + print(" {}".format(" ".join(names[idx : (idx + 5)]))) + + def print_basic_info(info): + print("GPU:{}\n".format(info["logical_node_type_id"])) + print("\n".join([" {:20}: {}".format(key, itr) for key, itr in info.items()])) + if not list_counters: + print("\n") + + agent_info_map = avail.get_agent_info_map() + agent_counters = avail.get_counters() + + for agent, info in agent_info_map.items(): + if ( + info["type"] == 2 + and args.device is not None + and info["logical_node_type_id"] == args.device + ): + print_basic_info(get_basic_agent_info(info)) + if list_counters: + print_agent_counter(agent_counters[agent], info) + break + + elif info["type"] == 2 and args.device is None: + print_basic_info(get_basic_agent_info(info)) + if list_counters: + print_agent_counter(agent_counters[agent], info) + + +def list_pc_sampling(args): + sampling_agents = avail.get_pc_sample_configs() + agent_info_map = avail.get_agent_info_map() + print("Agents supporting PC Sampling\n") + for agent in sampling_agents.keys(): + info = agent_info_map[agent] + print("GPU:{}\nNAME:{}\n".format(info["logical_node_type_id"], info["name"])) + + +def info_pc_sampling(args): + sampling_agents = avail.get_pc_sample_configs() + agent_info_map = avail.get_agent_info_map() + for agent, configs in sampling_agents.items(): + info = agent_info_map[agent] + print("GPU:{}\nNAME:{}".format(info["logical_node_type_id"], info["name"])) + print("configs:") + for config in configs: + print(config) + print("\n") + print("\n") + + +def listing(args): + def print_agent_counter(counters, info): + names = ["{:20}".format(counter.name) for counter in counters] + print("PMC:\n") + for idx in range(0, len(names), int(len(counters) / 20)): + print(" {}".format(" ".join(names[idx : (idx + 5)]))) + + agent_counters = avail.get_counters() + agent_info_map = avail.get_agent_info_map() + + for agent, info in agent_info_map.items(): + if ( + info["type"] == 2 + and args.device is not None + and info["logical_node_type_id"] == args.device + ): + print("GPU:{}\nNAME:{}".format(info["logical_node_type_id"], info["name"])) + print_agent_counter(agent_counters[agent], info) + break + elif info["type"] == 2 and args.device is None: + print("GPU:{}\nNAME:{}".format(info["logical_node_type_id"], info["name"])) + print_agent_counter(agent_counters[agent], info) + + +def info_pmc(args): + agent_counters = avail.get_counters() + agent_info_map = avail.get_agent_info_map() + + def print_pmc_info(args, pmc_counters): + + if not args.pmc: + for counter in pmc_counters: + print(counter) + print("\n") + else: + for pmc in args.pmc: + for counter in pmc_counters: + if pmc == counter.get_as_dict()["counter_name"]: + print(counter) + print("\n") + + for agent, info in agent_info_map.items(): + if ( + info["type"] == 2 + and args.device is not None + and info["logical_node_type_id"] == args.device + ): + print("GPU:{}\nNAME:{}".format(info["logical_node_type_id"], info["name"])) + print_pmc_info(args, agent_counters[agent]) + break + elif info["type"] == 2 and args.device is None: + print("GPU:{}\nNAME:{}".format(info["logical_node_type_id"], info["name"])) + print_pmc_info(args, agent_counters[agent]) + + +def process_info(args): + if args.pmc is None and args.pc_sampling is None: + list_basic_agent(args, True) + if args.pmc is not None: + info_pmc(args) + if args.pc_sampling is not None: + os.environ["ROCPROFILER_PC_SAMPLING_BETA_ENABLED"] = "on" + info_pc_sampling(args) + + +def process_list(args): + if not args.agent and args.pc_sampling is None: + listing(args) + if args.agent: + list_basic_agent(args, False) + if args.pc_sampling: + os.environ["ROCPROFILER_PC_SAMPLING_BETA_ENABLED"] = "on" + list_pc_sampling(args) + + +def process_pmc_check(args): + def get_device_agent(device_id): + for agent, info in agent_info_map.items(): + if info["type"] == 2 and info["logical_node_type_id"] == device_id: + return agent + avail.fatal_error("Invalid device id : {}".format(device_id)) + + def get_gpu_agents(): + agent_ids = [] + for agent, info in agent_info_map.items(): + if info["type"] == 2: + agent_ids.append(agent) + return agent_ids + + def get_counter_handle(counter_name): + agent_counters = avail.get_counters() + for agent, counters in agent_counters.items(): + for counter in counters: + if counter.get_as_dict()["counter_name"] == counter_name: + return counter.counter_handle + avail.fatal_error("Invalid counter name") + + def get_counter_names(pmc_list, agent): + agent_counters = avail.get_counters() + counter_names = [] + for pmc in pmc_list: + for counter in agent_counters[agent]: + if counter.counter_handle == pmc: + counter_names.append(counter.name) + return counter_names + + def get_logical_node_type_id(agent_id): + for agent, info in agent_info_map.items(): + if info["type"] == 2 and info["id"]["handle"] == agent_id: + return info["logical_node_type_id"] + + def process_qualifiers(pmc): + res = pmc.split(":") + if len(res) > 2: + avail.fatal_error("Invalid format for pmc-check") + + if len(res) == 2: + qualifiers_list = [] + qualifiers = res[1] + if "device" not in qualifiers: + avail.fatal_error("Incorrect input format for device index") + qualifiers = qualifiers.split(",") + for qualifier in qualifiers: + qualifiers_list.append(dict([qualifier.split("=")])) + return (res[0], qualifiers_list) + + return (res[0], None) + + if not args.pmc: + avail.fatal_error("Provide counter to check") + + device_pmc = {} + agent_info_map = avail.get_agent_info_map() + + for pmc in args.pmc: + counter, qualifiers = process_qualifiers(pmc) + if qualifiers is None: + if args.device is not None: + agent_handle = get_device_agent(args.device) + if agent_handle not in device_pmc.keys(): + device_pmc.setdefault(agent_handle, []) + device_pmc[agent_handle].append(get_counter_handle(counter)) + else: + agent_ids = get_gpu_agents() + for agent_handle in agent_ids: + if agent_handle not in device_pmc.keys(): + device_pmc.setdefault(agent_handle, []) + device_pmc[agent_handle].append(get_counter_handle(counter)) + else: + for itr in qualifiers: + agent_handle = get_device_agent(int(itr["device"])) + if agent_handle not in device_pmc.keys(): + device_pmc.setdefault(agent_handle, []) + device_pmc[agent_handle].append(get_counter_handle(counter)) + + if avail.check_pmc(device_pmc) is True: + for agent, pmc in device_pmc.items(): + device_id = get_logical_node_type_id(agent) + pmc_names = get_counter_names(pmc, agent) + print( + "Following input counters can be collected together on GPU:{}\t{}".format( + device_id, "\t".join(pmc_names) + ) + ) + + +def main(argv=None): + ROCPROFV3_AVAIL_DIR = os.path.dirname(os.path.realpath(__file__)) + ROCM_DIR = os.path.dirname(ROCPROFV3_AVAIL_DIR) + ROCPROF_LIST_AVAIL_TOOL_LIBRARY = ( + f"{ROCM_DIR}/libexec/rocprofiler-sdk/librocprofv3-list-avail.so" + ) + avail.loadLibrary.libname = os.environ.get( + "ROCPROF_LIST_AVAIL_TOOL_LIBRARY", ROCPROF_LIST_AVAIL_TOOL_LIBRARY + ) + args = parse_arguments(argv) + if args.command: + args.func(args) + + +if __name__ == "__main__": + ec = main(sys.argv[1:]) + sys.exit(ec) diff --git a/projects/rocprofiler-sdk/source/bin/rocprofv3.py b/projects/rocprofiler-sdk/source/bin/rocprofv3.py index d5d64aaf3a..cb1bb8f5a4 100755 --- a/projects/rocprofiler-sdk/source/bin/rocprofv3.py +++ b/projects/rocprofiler-sdk/source/bin/rocprofv3.py @@ -1321,11 +1321,25 @@ def run(app_args, args, **kwargs): if args.list_avail: update_env("ROCPROFILER_PC_SAMPLING_BETA_ENABLED", "on") - path = os.path.join(f"{ROCM_DIR}", "bin/rocprofv3_avail") + path = os.path.join(f"{ROCM_DIR}", "bin/rocprofv3-avail") if app_args: - exit_code = subprocess.check_call([sys.executable, path], env=app_env) + exit_code = subprocess.check_call( + [sys.executable, path, "info"], + env=app_env, + ) + if exit_code != 0: + fatal_error("rocprofv3-avail exit with error") + + exit_code = subprocess.check_call( + [sys.executable, path, "info", "--pc-sampling"], + env=app_env, + ) else: - app_args = [sys.executable, path] + app_args = [sys.executable, path, "info"] + exit_code = subprocess.check_call( + [sys.executable, path, "info", "--pc-sampling"], + env=app_env, + ) elif not app_args and not args.echo: log_config(app_env) diff --git a/projects/rocprofiler-sdk/source/bin/rocprofv3_avail.py b/projects/rocprofiler-sdk/source/bin/rocprofv3_avail.py deleted file mode 100755 index 64a5a166bd..0000000000 --- a/projects/rocprofiler-sdk/source/bin/rocprofv3_avail.py +++ /dev/null @@ -1,455 +0,0 @@ -#!/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 ctypes -import os -import io -import csv -import socket -import sys - - -def fatal_error(msg, exit_code=1): - sys.stderr.write(f"Fatal error: {msg}\n") - sys.stderr.flush() - sys.exit(exit_code) - - -class derived_counter: - - def __init__( - self, counter_name, counter_description, counter_expression, counter_dimensions - ): - - self.name = counter_name - self.description = counter_description - self.expression = counter_expression - self.dimensions = counter_dimensions - - -class basic_counter: - - def __init__( - self, counter_name, counter_description, counter_block, counter_dimensions - ): - - self.name = counter_name - self.description = counter_description - self.block = counter_block - self.dimensions = counter_dimensions - - -class pc_config: - - def __init__(self, config_method, config_unit, min_interval, max_interval): - self.method = config_method - self.unit = config_unit - self.min_interval = min_interval - self.max_interval = max_interval - - -ROCPROFV3_AVAIL_DIR = os.path.dirname(os.path.realpath(__file__)) -ROCM_DIR = os.path.dirname(ROCPROFV3_AVAIL_DIR) -ROCPROF_LIST_AVAIL_TOOL_LIBRARY = ( - f"{ROCM_DIR}/libexec/rocprofiler-sdk/librocprofv3-list-avail.so" -) - -MAX_STR = 256 -libname = os.environ.get( - "ROCPROF_LIST_AVAIL_TOOL_LIBRARY", ROCPROF_LIST_AVAIL_TOOL_LIBRARY -) -c_lib = ctypes.CDLL(libname) - -if c_lib is None: - fatal_error(f"Error opening {libname}") - -c_lib.get_number_of_counters.restype = ctypes.c_ulong -c_lib.get_number_of_pc_sample_configs.restype = ctypes.c_ulong -c_lib.get_number_of_dimensions.restype = ctypes.c_ulong - -c_lib.get_number_of_counters.argtypes = [ctypes.c_int] -c_lib.get_number_of_pc_sample_configs.argtypes = [ctypes.c_int] -c_lib.get_number_of_dimensions.argtypes = [ctypes.c_int] - -c_lib.get_pc_sample_config.argtypes = [ - ctypes.c_ulong, - ctypes.c_ulong, - ctypes.POINTER(ctypes.POINTER(ctypes.c_char * MAX_STR)), - ctypes.POINTER(ctypes.POINTER(ctypes.c_char * MAX_STR)), - ctypes.POINTER(ctypes.c_ulong), - ctypes.POINTER(ctypes.c_ulong), -] - -c_lib.get_counters_info.argtypes = [ - ctypes.c_ulong, - ctypes.c_int, - ctypes.POINTER(ctypes.c_ulong), - ctypes.POINTER(ctypes.POINTER(ctypes.c_char * MAX_STR)), - ctypes.POINTER(ctypes.POINTER(ctypes.c_char * MAX_STR)), - ctypes.POINTER(ctypes.c_int), -] - -c_lib.get_counter_expression.argtypes = [ - ctypes.c_ulong, - ctypes.c_int, - ctypes.POINTER(ctypes.POINTER(ctypes.c_char * MAX_STR)), -] - -c_lib.get_counter_dimension.argtypes = [ - ctypes.c_ulong, - ctypes.c_ulong, - ctypes.POINTER(ctypes.c_ulong), - ctypes.POINTER(ctypes.POINTER(ctypes.c_char * MAX_STR)), - ctypes.POINTER(ctypes.c_ulong), -] - -c_lib.get_counter_block.argtypes = [ - ctypes.c_ulong, - ctypes.c_ulong, - ctypes.POINTER(ctypes.POINTER(ctypes.c_char * MAX_STR)), -] - -c_lib.get_number_of_agents.restype = ctypes.c_size_t - -c_lib.get_agent_node_id.restype = ctypes.c_ulong -c_lib.get_agent_node_id.argtypes = [ctypes.c_int] - - -agent_derived_counter_map = dict() -agent_basic_counter_map = dict() -agent_pc_sample_config_map = dict() - - -def get_counters(node_id): - - no_of_counters = c_lib.get_number_of_counters(node_id) - - basic_counters = [] - derived_counters = [] - for counter_idx in range(0, no_of_counters): - - name_args = ctypes.POINTER(ctypes.c_char * MAX_STR)() - description_args = ctypes.POINTER(ctypes.c_char * MAX_STR)() - block_args = ctypes.POINTER(ctypes.c_char * MAX_STR)() - is_derived_args = ctypes.c_int() - counter_id_args = ctypes.c_ulong() - - c_lib.get_counters_info( - node_id, - counter_idx, - ctypes.byref(counter_id_args), - name_args, - description_args, - ctypes.byref(is_derived_args), - ) - - is_derived = is_derived_args.value - counter_id = counter_id_args.value - no_of_dimensions = c_lib.get_number_of_dimensions(counter_id) - - name = ctypes.cast(name_args, ctypes.c_char_p).value.decode("utf-8") - description = ctypes.cast(description_args, ctypes.c_char_p).value.decode("utf-8") - dimensions_stream = io.StringIO() - - for dim in range(0, no_of_dimensions): - - dim_name_args = ctypes.POINTER(ctypes.c_char * MAX_STR)() - dim_instance_args = ctypes.c_ulong() - dimension_id_args = ctypes.c_ulong() - - c_lib.get_counter_dimension( - counter_id, - dim, - ctypes.byref(dimension_id_args), - dim_name_args, - ctypes.byref(dim_instance_args), - ) - - dim_name = ctypes.cast(dim_name_args, ctypes.c_char_p).value.decode("utf-8") - dim_instance = dim_instance_args.value - - dimensions_stream.write(dim_name) - dimensions_stream.write("[0:") - dimensions_stream.write(str(dim_instance)) - dimensions_stream.write("]") - if dim != no_of_dimensions - 1: - dimensions_stream.write("\t") - - if is_derived: - - expression_args = ctypes.POINTER(ctypes.c_char * MAX_STR)() - c_lib.get_counter_expression(node_id, counter_idx, expression_args) - counter_expression = ctypes.cast( - expression_args, ctypes.c_char_p - ).value.decode("utf-8") - derived_counters.append( - derived_counter( - name, description, counter_expression, dimensions_stream.getvalue() - ) - ) - - else: - - block_args = ctypes.POINTER(ctypes.c_char * MAX_STR)() - c_lib.get_counter_block(node_id, counter_idx, block_args) - block = ctypes.cast(block_args, ctypes.c_char_p).value.decode("utf-8") - basic_counters.append( - basic_counter(name, description, block, dimensions_stream.getvalue()) - ) - dimensions_stream.close() - - agent_derived_counter_map[node_id] = derived_counters - agent_basic_counter_map[node_id] = basic_counters - - -def get_pc_sample_configs(node_id): - - no_of_pc_sample_configs = c_lib.get_number_of_pc_sample_configs(node_id) - pc_sample_configs = [] - if no_of_pc_sample_configs: - for config_idx in range(0, no_of_pc_sample_configs): - - method_args = ctypes.POINTER(ctypes.c_char * MAX_STR)() - unit_args = ctypes.POINTER(ctypes.c_char * MAX_STR)() - min_interval = ctypes.c_ulong() - max_interval = ctypes.c_ulong() - - c_lib.get_pc_sample_config( - node_id, - config_idx, - method_args, - unit_args, - ctypes.byref(min_interval), - ctypes.byref(max_interval), - ) - - method = ctypes.cast(method_args, ctypes.c_char_p).value.decode("utf-8") - unit = ctypes.cast(unit_args, ctypes.c_char_p).value.decode("utf-8") - pc_sample_configs.append( - pc_config(method, unit, min_interval.value, max_interval.value) - ) - - agent_pc_sample_config_map[node_id] = pc_sample_configs - - -def process_filename(file_path, file_type): - - filename = os.environ.get( - "ROCPROF_OUTPUT_FILE_NAME", socket.gethostname() + "/" + str(os.getpid()) - ) - - if os.path.exists(file_path) and os.path.isfile(file_path): - fatal_error("ROCPROFILER_OUTPUT_PATH already exists and is not a directory") - - elif not os.path.exists(file_path): - os.makedirs(file_path) - - output_filename = "" - if file_type == "derived": - output_filename = filename + "_" + "derived_metrics" + ".csv" - elif file_type == "basic": - output_filename = filename + "_" + "basic_metrics" + ".csv" - elif file_type == "pc_sample_config": - output_filename = filename + "_" + "pc_sample_config" + ".csv" - output_path = os.path.join(file_path, output_filename) - output_path_parent = os.path.dirname(output_path) - - if not os.path.exists(output_path_parent): - os.makedirs(output_path_parent) - - elif os.path.exists(output_path_parent) and os.path.isfile(output_path_parent): - fatal_error("ROCPROFILER_OUTPUT_PATH already exists and is not a directory") - - return output_path - - -def generate_output(agent_ids): - - list_avail_file = os.environ.get("ROCPROF_OUTPUT_LIST_AVAIL_FILE") - - if list_avail_file: - - file_path = os.environ.get("ROCPROF_OUTPUT_PATH") - derived_output_file = process_filename(file_path, "derived") - basic_output_file = process_filename(file_path, "basic") - pc_sample_config_file = process_filename(file_path, "pc_sample_config") - - with open(derived_output_file, "w") as csvfile: - print(f"Opened result file: {derived_output_file}") - fieldnames = ["Agent_Id", "Name", "Description", "Expression", "Dimensions"] - writer = csv.DictWriter(csvfile, fieldnames=fieldnames) - writer.writeheader() - for node_id, counters in agent_derived_counter_map.items(): - for counter in counters: - writer.writerow( - { - "Agent_Id": node_id, - "Name": counter.name, - "Description": counter.description, - "Expression": counter.expression, - "Dimensions": counter.dimensions, - } - ) - - with open(basic_output_file, "w") as csvfile: - print(f"Opened result file: {basic_output_file}") - fieldnames = ["Agent_Id", "Name", "Description", "Block", "Dimensions"] - writer = csv.DictWriter(csvfile, fieldnames=fieldnames) - writer.writeheader() - for node_id, counters in agent_basic_counter_map.items(): - for counter in counters: - if counter.block: - writer.writerow( - { - "Agent_Id": node_id, - "Name": counter.name, - "Description": counter.description, - "Block": counter.block, - "Dimensions": counter.dimensions, - } - ) - - with open(pc_sample_config_file, "w") as csvfile: - print(f"Opened result file: {pc_sample_config_file}") - fieldnames = [ - "Agent_Id", - "Method", - "Unit", - "Minimum_Interval", - "Maximum_Interval", - ] - writer = csv.DictWriter(csvfile, fieldnames=fieldnames) - writer.writeheader() - for node_id, configs in agent_pc_sample_config_map.items(): - for config in configs: - writer.writerow( - { - "Agent_Id": node_id, - "Method": config.method, - "Unit": config.unit, - "Minimum_Interval": config.min_interval, - "Maximum_Interval": config.max_interval, - } - ) - else: - - for node_id in agent_ids: - if node_id in agent_basic_counter_map.keys(): - basic_counters_stream = io.StringIO() - counters = agent_basic_counter_map[node_id] - for counter in counters: - if counter.block: - basic_counters_stream.write(f"gpu-agent:{node_id}\n") - basic_counters_stream.write("Name:") - basic_counters_stream.write("\t") - basic_counters_stream.write(str(counter.name)) - basic_counters_stream.write("\n") - basic_counters_stream.write("Description:") - basic_counters_stream.write("\t") - basic_counters_stream.write(str(counter.description)) - basic_counters_stream.write("\n") - basic_counters_stream.write("Block:") - basic_counters_stream.write("\t") - basic_counters_stream.write(str(counter.block)) - basic_counters_stream.write("\n") - basic_counters_stream.write("Dimensions:") - basic_counters_stream.write("\t") - basic_counters_stream.write(str(counter.dimensions)) - basic_counters_stream.write("\n\n") - basic_counters = basic_counters_stream.getvalue() - print(basic_counters) - basic_counters_stream.close() - - if node_id in agent_derived_counter_map.keys(): - derived_counters_stream = io.StringIO() - counters = agent_derived_counter_map[node_id] - for counter in counters: - derived_counters_stream.write(f"gpu-agent:{node_id}\n") - derived_counters_stream.write("Name:") - derived_counters_stream.write("\t") - derived_counters_stream.write(str(counter.name)) - derived_counters_stream.write("\n") - derived_counters_stream.write("Description:") - derived_counters_stream.write("\t") - derived_counters_stream.write(str(counter.description)) - derived_counters_stream.write("\n") - derived_counters_stream.write("Expression:") - derived_counters_stream.write("\t") - derived_counters_stream.write(str(counter.expression)) - derived_counters_stream.write("\n") - derived_counters_stream.write("Dimensions:") - derived_counters_stream.write("\t") - derived_counters_stream.write(str(counter.dimensions)) - derived_counters_stream.write("\n\n") - derived_counters = derived_counters_stream.getvalue() - print(derived_counters) - derived_counters_stream.close() - - if node_id in agent_pc_sample_config_map.keys(): - pc_sample_config_stream = io.StringIO() - configs = agent_pc_sample_config_map[node_id] - for config in configs: - pc_sample_config_stream.write("Method:") - pc_sample_config_stream.write("\t") - pc_sample_config_stream.write(str(config.method)) - pc_sample_config_stream.write("\n") - pc_sample_config_stream.write("Unit:") - pc_sample_config_stream.write("\t") - pc_sample_config_stream.write(str(config.unit)) - pc_sample_config_stream.write("\n") - pc_sample_config_stream.write("Minimum_Interval:") - pc_sample_config_stream.write("\t") - pc_sample_config_stream.write(str(config.min_interval)) - pc_sample_config_stream.write("\n") - pc_sample_config_stream.write("Maximum_Interval:") - pc_sample_config_stream.write("\t") - pc_sample_config_stream.write(str(config.max_interval)) - pc_sample_config_stream.write("\n") - pc_sample = pc_sample_config_stream.getvalue() - print( - "List available PC Sample Configurations for node_id\t" - + str(node_id) - + "\n" - ) - print(pc_sample) - print("\n") - pc_sample_config_stream.close() - else: - print("PC Sampling not supported on node_id\t" + str(node_id) + "\n") - - -if __name__ == "__main__": - # Load the shared library into ctypes - - c_lib.avail_tool_init() - no_of_agents = c_lib.get_number_of_agents() - agent_ids = [] - for idx in range(0, no_of_agents): - - node_id = c_lib.get_agent_node_id(idx) - agent_ids.append(node_id) - get_counters(node_id) - get_pc_sample_configs(node_id) - - generate_output(agent_ids) diff --git a/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/CMakeLists.txt b/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/CMakeLists.txt new file mode 100644 index 0000000000..7306e31f44 --- /dev/null +++ b/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/CMakeLists.txt @@ -0,0 +1,37 @@ +# MIT License +# +# Copyright (c) 2023-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. + +set(PACKAGE_OUTPUT_DIR + ${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}/rocprofv3_avail_module) + +set(ROCPROFV3_AVAIL_SOURCES __init__.py avail.py) + +foreach(_FILE ${ROCPROFV3_AVAIL_SOURCES}) + configure_file(${CMAKE_CURRENT_LIST_DIR}/${_FILE} ${PACKAGE_OUTPUT_DIR}/${_FILE} + COPYONLY) + install( + FILES ${PACKAGE_OUTPUT_DIR}/${_FILE} + DESTINATION ${CMAKE_INSTALL_BINDIR}/rocprofv3_avail_module + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE + COMPONENT tools) +endforeach() diff --git a/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/__init__.py b/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/__init__.py new file mode 100644 index 0000000000..d94763ee7d --- /dev/null +++ b/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/__init__.py @@ -0,0 +1,23 @@ +# MIT License +# +# Copyright (c) 2023-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. + +from __future__ import absolute_import diff --git a/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/avail.py b/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/avail.py new file mode 100644 index 0000000000..9e8dbecdbf --- /dev/null +++ b/projects/rocprofiler-sdk/source/bin/rocprofv3_avail_module/avail.py @@ -0,0 +1,499 @@ +#!/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 ctypes +import json + + +def fatal_error(msg, exit_code=1): + sys.stderr.write(f"Fatal error: {msg}\n") + sys.stderr.flush() + sys.exit(exit_code) + + +def build_counter_string(obj): + counter_str = "\n".join( + ["{:20}: {}".format(key, itr) for key, itr in obj.get_as_dict().items()] + ) + counter_str = counter_str + "\n" + for dim in obj.dimensions: + counter_str = counter_str + dim.__str__() + counter_str = counter_str + "\n" + return counter_str + + +class dimension: + + columns = ["dimension_id", "dimension_name", "dimension_instances"] + + def __init__(self, dimension_id, dimension_name, dimension_instances): + self.id = dimension_id + self.name = dimension_name + self.instances = dimension_instances + + def get_as_dict(self): + return dict(zip((self.columns), [self.id, self.name, self.instances])) + + def __str__(self): + dimension = "{:20}: {}\n".format( + "dimension_name", self.get_as_dict()["dimension_name"] + ) + dimension += "{:20}: [0:{}]".format( + "dimension_instances", self.get_as_dict()["dimension_instances"] + ) + return dimension + + +class counter: + + columns = ["counter_name", "description"] + + def __init__( + self, + counter_handle, + counter_name, + counter_description, + counter_dimensions, + is_hw_constant, + ): + self.name = counter_name + self.counter_handle = counter_handle + self.description = counter_description + self.dimensions = counter_dimensions + self.is_hw_constant = is_hw_constant + + def get_as_dict(self): + return dict(zip((self.columns), [self.name, self.description])) + + def __str__(self): + return "\n".join( + ["{:20}: {}".format(key, itr) for key, itr in self.get_as_dict().items()] + ) + + +class derived_counter(counter): + + columns = ["counter_name", "description", "expression"] + + def __init__( + self, + counter_handle, + counter_name, + counter_description, + counter_expression, + counter_dimensions, + is_hw_constant, + ): + super().__init__( + counter_handle, + counter_name, + counter_description, + counter_dimensions, + is_hw_constant, + ) + self.expression = counter_expression + + def get_as_dict(self): + return dict(zip((self.columns), [self.name, self.description, self.expression])) + + def __str__(self): + return build_counter_string(self) + + +class basic_counter(counter): + + columns = ["counter_name", "description", "block"] + + def __init__( + self, + counter_handle, + counter_name, + counter_description, + counter_block, + counter_dimensions, + is_hw_constant, + ): + super().__init__( + counter_handle, + counter_name, + counter_description, + counter_dimensions, + is_hw_constant, + ) + self.block = counter_block + + def get_as_dict(self): + return dict(zip((self.columns), [self.name, self.description, self.block])) + + def __str__(self): + return build_counter_string(self) + + +class pc_config: + + columns = ["method", "unit", "min_interval", "max_interval", "flags"] + + def __init__(self, config_method, config_unit, min_interval, max_interval, flags): + + self.method = self.get_method_string(config_method.value) + self.unit = self.get_unit_string(config_unit.value) + self.min_interval = min_interval + self.max_interval = max_interval + self.flags = flags + + def __str__(self): + + return "\n".join( + [ + " {:20}:{}".format( + key, + itr if key == "method" or key == "unit" else self.get_value(key, itr), + ) + for key, itr in self.get_as_dict().items() + ] + ) + + @staticmethod + def get_value(key, itr): + if key == "min_interval" or key == "max_interval": + return itr.value + elif key == "flags": + if itr.value == 1: + return "interval pow2" + else: + return "none" + else: + fatal_error("Incorrect key") + + @staticmethod + def get_method_string(key): + method_map = {1: "stochastic", 2: "host_trap"} + return method_map[key] + + @staticmethod + def get_unit_string(key): + unit_map = {1: "instructions", 2: "cycle", 3: "time"} + return unit_map[key] + + def get_as_dict(self): + + return dict( + zip( + (self.columns), + [ + self.method, + self.unit, + self.min_interval, + self.max_interval, + self.flags, + ], + ) + ) + + +class loadLibrary: + libname = None + c_lib = None + + +def get_library(): + + get_library.libname = None + if loadLibrary.c_lib is None: + loadLibrary.c_lib = ctypes.CDLL(loadLibrary.libname) + return loadLibrary.c_lib + + +def get_string_value(str_ptr): + return ctypes.cast(str_ptr, ctypes.c_char_p).value.decode("utf-8") + + +def get_agent_info(agent_handle): + lib = get_library() + lib.agent_info.argtypes = [ctypes.c_ulong, ctypes.POINTER(ctypes.c_char_p)] + agent_info_str = ctypes.c_char_p() + lib.agent_info(agent_handle, ctypes.byref(agent_info_str)) + return json.loads(agent_info_str.value.decode("utf-8")) + + +def get_number_of_counters(agent_handle): + lib = get_library() + lib.get_number_of_counters.restype = ctypes.c_ulong + lib.get_number_of_counters.argtypes = [ctypes.c_ulong] + return lib.get_number_of_agent_counters(agent_handle) + + +def get_number_agents(): + lib = get_library() + lib.get_number_of_agents.restype = ctypes.c_ulong + return lib.get_number_of_agents() + + +def get_agent_handles(): + lib = get_library() + num_agents = get_number_agents() + lib.agent_handles.argtypes = [ctypes.c_ulong * num_agents, ctypes.c_ulong] + agent_handles_arr = (ctypes.c_ulong * num_agents)() + lib.agent_handles(agent_handles_arr, num_agents) + return list(agent_handles_arr) + + +def get_agent_info_map(): + agent_info_map = {} + agents = get_agent_handles() + for agent in agents: + agent_info_map[agent] = get_agent_info(agent) + + return agent_info_map + + +def get_number_of_agent_counters(agent_handle): + lib = get_library() + lib.get_number_of_agent_counters.argtypes = [ctypes.c_ulong] + return lib.get_number_of_agent_counters(agent_handle) + + +def get_agent_counter_handles(agent_handle): + lib = get_library() + num_counters = get_number_of_agent_counters(agent_handle) + lib.agent_counter_handles.argtypes = [ + ctypes.c_ulong * num_counters, + ctypes.c_ulong, + ctypes.c_ulong, + ] + counter_handles = (ctypes.c_ulong * num_counters)() + lib.agent_counter_handles(counter_handles, agent_handle, num_counters) + return list(counter_handles) + + +def get_dimensions(counter_handle): + lib = get_library() + lib.get_number_of_dimensions.argtypes = [ctypes.c_ulong] + lib.get_number_of_dimensions.restype = ctypes.c_ulong + num_dims = lib.get_number_of_dimensions(counter_handle) + + lib.counter_dimension_ids.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong * num_dims, + ctypes.c_uint, + ] + dims_ids = (ctypes.c_ulong * num_dims)() + lib.counter_dimension.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.POINTER(ctypes.c_char_p), + ctypes.POINTER(ctypes.c_uint), + ] + lib.counter_dimension_ids(counter_handle, dims_ids, num_dims) + dimensions = [] + for dim_id in list(dims_ids): + dimension_name = ctypes.c_char_p() + dimension_instance = ctypes.c_uint() + lib.counter_dimension( + counter_handle, + dim_id, + ctypes.byref(dimension_name), + ctypes.byref(dimension_instance), + ) + dim = dimension( + dim_id, get_string_value(dimension_name), dimension_instance.value + ) + dimensions.append(dim) + return dimensions + + +def get_counters(): + agent_counters = {} + agents = get_agent_handles() + agent_counters = {} + agent_info_map = get_agent_info_map() + for agent in agents: + if agent_info_map[agent]["type"] != 2: + continue + agent_counters.setdefault(agent, []) + counters = get_agent_counter_handles(agent) + if counters: + for counter_id in list(counters): + counter_info = get_counter_info(counter_id) + agent_counters[agent].append(counter_info) + return agent_counters + + +def get_pc_sample_configs(): + agent_pc_sample_config = {} + agents = get_agent_handles() + + for agent in agents: + configs = get_pc_sample_config(agent) + if len(configs) > 0: + agent_pc_sample_config[agent] = configs + + return agent_pc_sample_config + + +def get_counter_info(counter_handle): + lib = get_library() + lib.counter_info.argtypes = [ + ctypes.c_ulong, + ctypes.POINTER(ctypes.c_char_p), + ctypes.POINTER(ctypes.c_char_p), + ctypes.POINTER(ctypes.c_uint), + ctypes.POINTER(ctypes.c_uint), + ] + counter_name = ctypes.c_char_p() + counter_description = ctypes.c_char_p() + is_derived = ctypes.c_uint() + is_hw_constant = ctypes.c_uint() + lib.counter_info( + counter_handle, + ctypes.byref(counter_name), + ctypes.byref(counter_description), + ctypes.byref(is_derived), + ctypes.byref(is_hw_constant), + ) + + if is_derived.value == 1: + lib.counter_expression.argtypes = [ + ctypes.c_ulong, + ctypes.POINTER(ctypes.c_char_p), + ] + expression = ctypes.c_char_p() + lib.counter_expression(counter_handle, ctypes.byref(expression)) + dimensions = get_dimensions(counter_handle) + return derived_counter( + counter_handle, + get_string_value(counter_name), + get_string_value(counter_description), + get_string_value(expression), + dimensions, + is_hw_constant, + ) + + elif not is_hw_constant.value: + lib.counter_block.argtypes = [ctypes.c_ulong, ctypes.POINTER(ctypes.c_char_p)] + block = ctypes.c_char_p() + lib.counter_block(counter_handle, ctypes.byref(block)) + dimensions = get_dimensions(counter_handle) + return basic_counter( + counter_handle, + get_string_value(counter_name), + get_string_value(counter_description), + get_string_value(block), + dimensions, + is_hw_constant, + ) + else: + return counter( + counter_handle, + get_string_value(counter_name), + get_string_value(counter_description), + [], + is_hw_constant.value, + ) + + +def get_number_of_pc_sample_configs(agent_handle): + lib = get_library() + lib.get_number_of_pc_sample_configs.argtypes = [ctypes.c_ulong] + lib.get_number_of_pc_sample_configs.restype = ctypes.c_ulong + return lib.get_number_of_pc_sample_configs(agent_handle) + + +def get_pc_sample_config(agent_handle): + lib = get_library() + num_configs = get_number_of_pc_sample_configs(agent_handle) + lib.pc_sample_config.argtypes = [ + ctypes.c_ulong, + ctypes.c_ulong, + ctypes.POINTER(ctypes.c_ulong), + ctypes.POINTER(ctypes.c_ulong), + ctypes.POINTER(ctypes.c_ulong), + ctypes.POINTER(ctypes.c_ulong), + ctypes.POINTER(ctypes.c_ulong), + ] + pc_configs = [] + + for config in range(0, num_configs): + method = (ctypes.c_ulong)() + unit = (ctypes.c_ulong)() + max_interval = (ctypes.c_ulong)() + min_interval = (ctypes.c_ulong)() + flags = (ctypes.c_ulong)() + lib.pc_sample_config( + agent_handle, + config, + ctypes.byref(method), + ctypes.byref(unit), + ctypes.byref(min_interval), + ctypes.byref(max_interval), + flags, + ) + pc_configs.append( + pc_config( + method, + unit, + min_interval, + max_interval, + flags, + ) + ) + return pc_configs + + +def check_pmc(agent_counter): + lib = get_library() + + def get_counter_names(counter_ids): + + counter_names = [] + for counter_id in counter_ids: + counter = get_counter_info(counter_id) + if counter.counter_handle == counter_id: + counter_names.append(counter.name) + return counter_names + + def get_agent_name(agent_id): + agent_info_map = get_agent_info_map() + for agent, info in agent_info_map.items(): + if agent == agent_id: + return info["name"] + + for agent, counter_ids in agent_counter.items(): + num_counters = len(counter_ids) + counters = (ctypes.c_ulong * num_counters)(*counter_ids) + lib.is_counter_set.argtypes = [ + ctypes.c_ulong * num_counters, + ctypes.c_ulong, + ctypes.c_ulong, + ] + lib.is_counter_set.restype = ctypes.c_bool + if lib.is_counter_set(counters, agent, num_counters) is False: + fatal_error( + "{} not collected on agent {}".format( + " ".join(get_counter_names(counter_ids)), get_agent_name(agent) + ) + ) + return True diff --git a/projects/rocprofiler-sdk/source/docs/_toc.yml.in b/projects/rocprofiler-sdk/source/docs/_toc.yml.in index 0375b3574e..a202b30126 100644 --- a/projects/rocprofiler-sdk/source/docs/_toc.yml.in +++ b/projects/rocprofiler-sdk/source/docs/_toc.yml.in @@ -14,6 +14,7 @@ subtrees: - file: how-to/samples title: Samples - file: how-to/using-rocprofv3 + - file: how-to/using-rocprofv3-avail - file: how-to/using-rocprofiler-sdk-roctx - file: how-to/using-rocprofv3-with-mpi - file: how-to/using-pc-sampling diff --git a/projects/rocprofiler-sdk/source/docs/how-to/using-rocprofv3-avail.rst b/projects/rocprofiler-sdk/source/docs/how-to/using-rocprofv3-avail.rst new file mode 100644 index 0000000000..d7371d24f0 --- /dev/null +++ b/projects/rocprofiler-sdk/source/docs/how-to/using-rocprofv3-avail.rst @@ -0,0 +1,117 @@ +.. meta:: + :description: Documentation of the usage of rocprofv3-avail + :keywords: ROCprofiler-SDK tool usage, rocprofv3-avail usage, rocprofv3 user manual, rocprofv3 usage, rocprofv3 user guide, using rocprofv3, ROCprofiler-SDK tool user guide, ROCprofiler-SDK tool user manual, using ROCprofiler-SDK tool, ROCprofiler-SDK command-line tool, ROCprofiler-SDK CLI, ROCprofiler-SDK command line tool + +.. _using-rocprofv3-avail: + +====================== +Using rocprofv3-avail +====================== + +``rocprofv3-avail`` is a CLI tool that helps you to query the features supported by the hardware and Rocprofiler SDK. + +The following sections demonstrate the use of ``rocprofv3-avail`` for querying features using various command-line options. + +``rocprofv3-avail`` is installed with ROCm under ``/opt/rocm/bin``. To use the tool from anywhere in the system, export ``PATH`` variable: + +.. code-block:: bash + + export PATH=$PATH:/opt/rocm/bin + +.. _rocprofv3-avail_cli-options: + +Command-line options +-------------------- + +The following table lists ``rocprofv3-avail`` command-line options categorized according to their purpose. + +.. # COMMENT: The following lines define a line break for use in the table below. +.. |br| raw:: html + +
+ +.. list-table:: rocprofv3-avail options + :header-rows: 1 + + * - Purpose + - Option + - Description + + * - avail-aptions commands + - | ``info`` + | ``list`` + | ``pmc-check`` + - | Info options for detailed information of counters, agents, and pc-sampling configurations. + | List options for hw counters, agents and pc-sampling support". + | Checking counters collection support on agents. + + +Available Hardware Counters +++++++++++++++++++++++++++++ +.. code-block:: bash + + rocprofv3-avail -d 0 + +The preceding command selects a device with logical node type id as 0 in the node. +The option is applied to further sub commands and options + +.. code-block:: bash + + rocprofv3-avail list + +The preceding command generates an output listing agents and hardware counters + +.. code-block:: bash + + rocprofv3-avail list --agent + +The preceding command generates an output listing basic info for all agents, if used with -d only basic info for device -d is listed. + +.. code-block:: bash + + rocprofv3-avail list --pmc + +The preceding command generates an output listing counters for all agents, if used with -d only counters on the the -d device is listed + +.. code-block:: bash + + rocprofv3-avail list --pc-sampling + +The preceding command generates an output listing agents that supports any kind of PC Sampling. -d option is not applicable here. +.. code-block:: bash + + rocprofv3-avail info + +The preceding command generates an output with agent information and listing all counters +supported on each + +.. code-block:: bash + + rocprofv3-avail info --pmc + +The preceding command generates an output with the pmc info, if used with -d information of pmc for device -d is generated. + +.. code-block:: bash + + rocprofv3-avail info --pc-sampling + +The preceding command generates list of supported PC sampling configurations for each agent that supports PC sampling. -d option is not applicable here. + +.. code-block:: bash + + rocprofv3-avail pmc-check [pmc [pmc...]] + +The preceding command checks if the pmc can be collected together + +.. code-block:: bash + + rocprofv3-avail pmc-check -d 0 :device=1 + + The preceding command checks if the pmc1 and pmc2 can be collected together on agent 0 + and pmc3 on agent 1 + +.. note:: + + The above command writes the ouptut to the standard output. + + diff --git a/projects/rocprofiler-sdk/source/lib/output/metadata.cpp b/projects/rocprofiler-sdk/source/lib/output/metadata.cpp index 98ef41d6a8..507beefc9e 100644 --- a/projects/rocprofiler-sdk/source/lib/output/metadata.cpp +++ b/projects/rocprofiler-sdk/source/lib/output/metadata.cpp @@ -185,8 +185,10 @@ void metadata::init(inprocess) &static_cast(_info))); for(uint64_t j = 0; j < _info.dimensions_count; ++j) + { _dim_ids.emplace_back(_info.dimensions[j].id); - + _dim_info.emplace_back(_info.dimensions[j]); + } data_v->at(id).emplace_back( id, _info, std::move(_dim_ids), std::move(_dim_info)); } @@ -379,11 +381,13 @@ metadata::get_gpu_agents() const pc_sample_config_vec_t metadata::get_pc_sample_config_info(rocprofiler_agent_id_t _val) const { - auto _ret = pc_sample_config_vec_t{}; + auto _ret = pc_sample_config_vec_t{}; + if(agent_pc_sample_config_info.find(_val) == agent_pc_sample_config_info.end()) return _ret; auto pc_sample_config = agent_pc_sample_config_info.at(_val); for(const auto& itr : pc_sample_config) + { _ret.emplace_back(itr); - + } return _ret; } diff --git a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/agent.cpp b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/agent.cpp index 793cea5717..3e2ac0f92c 100644 --- a/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/agent.cpp +++ b/projects/rocprofiler-sdk/source/lib/rocprofiler-sdk/agent.cpp @@ -278,23 +278,23 @@ read_map(const std::string& fname) return data; } - auto ifs = std::ifstream{fname}; + auto ifs = std::ifstream(fname); + if(!ifs || !ifs.good()) { ROCP_CI_LOG(WARNING) << fmt::format("file '{}' cannot be read", fname); return data; } - auto last_label = std::string{}; - while(true) + while(ifs && ifs.good()) { auto label = std::string{}; ifs >> label; - if(ifs.eof() || label.empty()) break; + if(ifs.fail() || ifs.eof() || label.empty()) break; auto entry = std::string{}; ifs >> entry; - if(ifs.eof()) + if(ifs.fail() || ifs.eof()) { ROCP_CI_LOG(WARNING) << fmt::format( "unexpected file format in '{}' at {}", fname, label); diff --git a/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/CMakeLists.txt b/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/CMakeLists.txt index 70aad537ae..6d8a5ee603 100644 --- a/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/CMakeLists.txt +++ b/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/CMakeLists.txt @@ -23,6 +23,7 @@ rocprofiler_activate_clang_tidy() add_library(rocprofv3-list-avail SHARED) +add_library(rocprofiler-sdk::rocprofv3-list-avail ALIAS rocprofv3-list-avail) target_sources(rocprofv3-list-avail PRIVATE rocprofv3_avail.cpp) target_link_libraries( @@ -30,8 +31,15 @@ target_link_libraries( PRIVATE rocprofiler-sdk::rocprofiler-sdk-shared-library rocprofiler-sdk::rocprofiler-sdk-headers rocprofiler-sdk::rocprofiler-sdk-build-flags + rocprofiler-sdk::rocprofiler-sdk-memcheck rocprofiler-sdk::rocprofiler-sdk-common-library - rocprofiler-sdk::rocprofiler-sdk-cereal) + rocprofiler-sdk::rocprofiler-sdk-cereal + rocprofiler-sdk::rocprofiler-sdk-perfetto + rocprofiler-sdk::rocprofiler-sdk-output-library + rocprofiler-sdk::rocprofiler-sdk-drm + rocprofiler-sdk::rocprofiler-sdk-dl + rocprofiler-sdk::rocprofiler-sdk-dw + rocprofiler-sdk::rocprofiler-sdk-amd-comgr) set_target_properties( rocprofv3-list-avail diff --git a/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.cpp b/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.cpp index b67b76a2b5..065f481469 100644 --- a/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.cpp +++ b/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.cpp @@ -20,6 +20,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. +#include "rocprofv3_avail.hpp" #include "lib/common/environment.hpp" #include "lib/common/filesystem.hpp" #include "lib/common/logging.hpp" @@ -28,13 +29,16 @@ #include "lib/common/synchronized.hpp" #include "lib/common/units.hpp" #include "lib/common/utility.hpp" +#include "lib/output/agent_info.hpp" +#include "lib/output/counter_info.hpp" +#include "lib/output/metadata.hpp" #include #include #include -#include #include #include +#include #include #include @@ -46,43 +50,10 @@ #include #include -auto destructors = new std::vector>{}; - namespace common = ::rocprofiler::common; +namespace tool = ::rocprofiler::tool; -namespace -{ -auto pc_sampling_method = std::deque{ - "ROCPROFILER_PC_SAMPLING_METHOD_NONE", - "ROCPROFILER_PC_SAMPLING_METHOD_STOCHASTIC", - "ROCPROFILER_PC_SAMPLING_METHOD_HOST_TRAP", - "ROCPROFILER_PC_SAMPLING_METHOD_LAST", -}; - -auto pc_sampling_unit = std::deque{ - "ROCPROFILER_PC_SAMPLING_UNIT_NONE", - "ROCPROFILER_PC_SAMPLING_UNIT_INSTRUCTIONS", - "ROCPROFILER_PC_SAMPLING_UNIT_CYCLES", - "ROCPROFILER_PC_SAMPLING_UNIT_TIME", - "ROCPROFILER_PC_SAMPLING_UNIT_LAST", -}; -} // namespace - -using counter_info_t = std::vector>; -using pc_sample_info_t = std::vector>; -auto agent_counter_info = std::unordered_map{}; -auto agent_pc_sample_info = std::unordered_map{}; -// auto agent_configs_info = std::unordered_map{}; -using counter_dimension_info_t = - std::unordered_map>>; -auto counter_dim_info = counter_dimension_info_t{}; -std::vector agent_node_ids; - -constexpr size_t pc_config_fields = 4, method_idx = 0, unit_idx = 1, min_interval_idx = 2, - max_interval_idx = 3; -constexpr size_t dimensions_fields = 3, dim_id_idx = 0, dim_name_idx = 1, size_idx = 2; -constexpr size_t counter_fields = 5, counter_id_idx = 0, name_idx = 1, description_idx = 2, - is_derived_idx = 3, block_idx = 4, expression_idx = 4; +using JSONOutputArchive = cereal::MinimalJSONOutputArchive; #define ROCPROFILER_CALL(result, msg) \ { \ @@ -98,56 +69,10 @@ constexpr size_t counter_fields = 5, counter_id_idx = 0, name_idx = 1, descripti std::stringstream errmsg{}; \ errmsg << "[" #result "][" << __FILE__ << ":" << __LINE__ << "] " << msg " failure (" \ << status_msg << ")"; \ - throw std::runtime_error(errmsg.str()); \ + ROCP_FATAL << errmsg.str() \ } \ } -using counter_vec_t = std::vector; - -ROCPROFILER_EXTERN_C_INIT -void -avail_tool_init() ROCPROFILER_EXPORT; -size_t -get_number_of_agents() ROCPROFILER_EXPORT; -uint64_t -get_agent_node_id(int idx) ROCPROFILER_EXPORT; -int -get_number_of_counters(uint64_t node_id) ROCPROFILER_EXPORT; -void -get_counters_info(uint64_t node_id, - int idx, - uint64_t* counter_id, - const char** counter_name, - const char** counter_description, - uint8_t* is_derived) ROCPROFILER_EXPORT; -void -get_counter_expression(uint64_t node_id, int idx, const char** counter_expr) ROCPROFILER_EXPORT; - -void -get_counter_block(uint64_t node_id, int idx, const char** counter_block) ROCPROFILER_EXPORT; - -int -get_number_of_dimensions(int counter_id) ROCPROFILER_EXPORT; - -void -get_counter_dimension(uint64_t counter_id, - uint64_t dimension_idx, - uint64_t* dimension_id, - const char** dimension_name, - uint64_t* dimension_instance) ROCPROFILER_EXPORT; - -int -get_number_of_pc_sample_configs(uint64_t node_id) ROCPROFILER_EXPORT; - -void -get_pc_sample_config(uint64_t node_id, - int idx, - const char** method, - const char** unit, - uint64_t* min_interval, - uint64_t* max_interval) ROCPROFILER_EXPORT; -ROCPROFILER_EXTERN_C_FINI - void initialize_logging() { @@ -156,243 +81,223 @@ initialize_logging() FLAGS_colorlogtostderr = true; } -rocprofiler_status_t -pc_configuration_callback(const rocprofiler_pc_sampling_configuration_t* configs, - long unsigned int num_config, - void* user_data) +tool::metadata& +get_metadata() { - auto* avail_configs = static_cast>*>(user_data); - - for(size_t i = 0; i < num_config; i++) - { - auto config = std::vector{}; - config.reserve(pc_config_fields); - auto it = config.begin(); - config.insert(it + method_idx, pc_sampling_method.at(configs[i].method)); - config.insert(it + unit_idx, pc_sampling_unit.at(configs[i].unit)); - config.insert(it + min_interval_idx, std::to_string(configs[i].min_interval)); - config.insert(it + max_interval_idx, std::to_string(configs[i].max_interval)); - avail_configs->push_back(config); - } - - return ROCPROFILER_STATUS_SUCCESS; + static auto tool_metadata = std::make_unique(tool::metadata::inprocess{}); + auto _once = std::once_flag{}; + std::call_once(_once, [&]() { + initialize_logging(); + tool_metadata->init(tool::metadata::inprocess{}); + }); + return *tool_metadata; } -rocprofiler_status_t -dimensions_info_callback(rocprofiler_counter_id_t /*id*/, - const rocprofiler_counter_record_dimension_info_t* dim_info, - long unsigned int num_dims, - void* user_data) +tool::agent_info_map_t +get_agents() { - auto* dimensions_info = - static_cast*>(user_data); - dimensions_info->reserve(num_dims); - for(size_t j = 0; j < num_dims; j++) - dimensions_info->emplace_back(dim_info[j]); - return ROCPROFILER_STATUS_SUCCESS; + return get_metadata().agents_map; } -rocprofiler_status_t -iterate_agent_counters_callback(rocprofiler_agent_id_t, - rocprofiler_counter_id_t* counters, - size_t num_counters, - void* user_data) +tool::agent_counter_info_map_t +get_agent_counters() { - auto* _counters_info = static_cast>*>(user_data); - for(size_t i = 0; i < num_counters; i++) - { - auto _info = rocprofiler_counter_info_v1_t{}; - auto dimensions_data = std::vector{}; - ROCPROFILER_CALL( - rocprofiler_query_counter_info( - counters[i], ROCPROFILER_COUNTER_INFO_VERSION_1, static_cast(&_info)), - "Could not query counter_id"); - - dimensions_data = std::vector{ - _info.dimensions, _info.dimensions + _info.dimensions_count}; - auto dimensions_info = std::vector>{}; - dimensions_info.reserve(dimensions_data.size()); - for(auto& dim : dimensions_data) - { - auto dimensions = std::vector{}; - dimensions.reserve(dimensions_fields); - auto it = dimensions.begin(); - dimensions.insert(it + dim_id_idx, std::to_string(dim.id)); - dimensions.insert(it + dim_name_idx, std::string(dim.name)); - dimensions.insert(it + size_idx, std::to_string(dim.instance_size - 1)); - dimensions_info.emplace_back(dimensions); - } - counter_dim_info.emplace(counters[i].handle, dimensions_info); - - auto counter = std::vector{}; - - if(_info.is_derived) - { - counter.reserve(counter_fields); - auto it = counter.begin(); - counter.insert(it + counter_id_idx, std::to_string(_info.id.handle)); - counter.insert(it + name_idx, std::string(_info.name)); - counter.insert(it + description_idx, std::string(_info.description)); - counter.insert(it + is_derived_idx, std::to_string(_info.is_derived)); - counter.insert(it + expression_idx, std::string(_info.expression)); - } - else - { - counter.reserve(counter_fields); - auto it = counter.begin(); - counter.insert(it + counter_id_idx, std::to_string(_info.id.handle)); - counter.insert(it + name_idx, std::string(_info.name)); - counter.insert(it + description_idx, std::string(_info.description)); - counter.insert(it + is_derived_idx, std::to_string(_info.is_derived)); - counter.insert(it + block_idx, std::string(_info.block)); - } - - _counters_info->emplace_back(counter); - } - return ROCPROFILER_STATUS_SUCCESS; + return get_metadata().agent_counter_info; } -rocprofiler_status_t -list_avail_configs(rocprofiler_agent_version_t, const void** agents, size_t num_agents, void*) +const tool::tool_counter_info* +get_counter_info(rocprofiler_counter_id_t counter_id) { - for(size_t idx = 0; idx < num_agents; idx++) - { - const auto* agent = static_cast(agents[idx]); - if(agent->type == ROCPROFILER_AGENT_TYPE_GPU) - { - auto counters_v = counter_vec_t{}; - - // TODO(aelwazir): To be changed back to use node id once ROCR fixes - // the hsa_agents to use the real node id - uint32_t node_id = agent->node_id; - std::vector> configs = {}; - std::vector> _counter_dim_info = {}; - agent_node_ids.emplace_back(node_id); - rocprofiler_query_pc_sampling_agent_configurations( - agent->id, pc_configuration_callback, &configs); - ROCPROFILER_CALL( - rocprofiler_iterate_agent_supported_counters( - agent->id, iterate_agent_counters_callback, (void*) (&_counter_dim_info)), - "Iterate rocprofiler counters"); - if(!_counter_dim_info.empty()) agent_counter_info.emplace(node_id, _counter_dim_info); - if(!configs.empty()) - - { - agent_pc_sample_info.emplace(node_id, configs); - } - } - } - - return ROCPROFILER_STATUS_SUCCESS; + const auto* counter_info = get_metadata().get_counter_info(counter_id); + if(!counter_info) ROCP_FATAL << "Invalid counter handle"; + return counter_info; } +auto agent_json = std::map{}; + ROCPROFILER_EXTERN_C_INIT -void -avail_tool_init() -{ - initialize_logging(); - ROCPROFILER_CALL(rocprofiler_query_available_agents(ROCPROFILER_AGENT_INFO_VERSION_0, - list_avail_configs, - sizeof(rocprofiler_agent_t), - nullptr), - "Iterate rocporfiler agents"); -} - size_t get_number_of_agents() { - return agent_node_ids.size(); -} - -uint64_t -get_agent_node_id(int idx) -{ - return agent_node_ids.at(idx); -} - -int -get_number_of_counters(uint64_t node_id) -{ - if(agent_counter_info.find(node_id) != agent_counter_info.end()) - return agent_counter_info.at(node_id).size(); - else - return 0; + return get_agents().size(); } void -get_counters_info(uint64_t node_id, - int counter_idx, - uint64_t* counter_id, - const char** counter_name, - const char** counter_description, - uint8_t* is_derived) +agent_handles(uint64_t* agent_handles, size_t num_agents) { - if(agent_counter_info.find(node_id) == agent_counter_info.end()) return; - *counter_id = - std::stoull(agent_counter_info.at(node_id).at(counter_idx).at(0).c_str(), nullptr, 10); - *counter_name = agent_counter_info.at(node_id).at(counter_idx).at(1).c_str(); - *counter_description = agent_counter_info.at(node_id).at(counter_idx).at(2).c_str(); - *is_derived = std::stoi(agent_counter_info.at(node_id).at(counter_idx).at(3).c_str()); + auto agent_info = get_agents(); + if(num_agents != agent_info.size()) ROCP_FATAL << "Incorrect number of agents"; + auto agent_ids = std::vector{}; + std::for_each(agent_info.begin(), agent_info.end(), [&agent_ids](auto& agent) { + agent_ids.emplace_back(agent.first.handle); + }); + std::copy(agent_ids.begin(), agent_ids.end(), agent_handles); +} + +size_t +get_number_of_agent_counters(uint64_t agent_handle) +{ + auto agent_id = rocprofiler_agent_id_t{agent_handle}; + auto agent_counter_info = get_agent_counters(); + if(agent_counter_info.find(agent_id) != agent_counter_info.end()) + return agent_counter_info.at(agent_id).size(); + return 0; } void -get_counter_block(uint64_t node_id, int counter_idx, const char** counter_block) +agent_counter_handles(uint64_t* counter_handles, + uint64_t agent_handle, + size_t number_of_agent_counters) { - if(agent_counter_info.find(node_id) == agent_counter_info.end()) return; - *counter_block = agent_counter_info.at(node_id).at(counter_idx).at(4).c_str(); + auto agent_counter_info = get_agent_counters(); + auto itr = agent_counter_info.find(rocprofiler_agent_id_t{agent_handle}); + + if(itr == agent_counter_info.end()) return; + + if(number_of_agent_counters != itr->second.size()) ROCP_FATAL << "Incorrect number of agents"; + auto counter_ids = std::vector{}; + + std::for_each(itr->second.begin(), itr->second.end(), [&counter_ids](auto& counter) { + counter_ids.emplace_back(counter.id.handle); + }); + std::copy(counter_ids.begin(), counter_ids.end(), counter_handles); } void -get_counter_expression(uint64_t node_id, int idx, const char** counter_expr) +counter_info(uint64_t counter_handle, + const char** counter_name, + const char** counter_description, + uint8_t* is_derived, + uint8_t* is_hw_constant) { - if(agent_counter_info.find(node_id) == agent_counter_info.end()) return; - *counter_expr = agent_counter_info.at(node_id).at(idx).at(4).c_str(); -} + const auto* counter_info = get_counter_info(rocprofiler_counter_id_t{counter_handle}); -int -get_number_of_dimensions(int counter_id) -{ - if(counter_dim_info.find(counter_id) == counter_dim_info.end()) return 0; - return counter_dim_info.at(counter_id).size(); -} -void -get_counter_dimension(uint64_t counter_id, - uint64_t dimension_idx, - uint64_t* dimension_id, - const char** dimension_name, - uint64_t* dimension_instance) -{ - if(counter_dim_info.find(counter_id) == counter_dim_info.end()) return; - *dimension_id = - std::stoull(counter_dim_info.at(counter_id).at(dimension_idx).at(0).c_str(), nullptr, 10); - *dimension_name = counter_dim_info.at(counter_id).at(dimension_idx).at(1).c_str(); - *dimension_instance = - std::stoull(counter_dim_info.at(counter_id).at(dimension_idx).at(2).c_str(), nullptr, 10); -} - -int -get_number_of_pc_sample_configs(uint64_t node_id) -{ - if(agent_pc_sample_info.find(node_id) == agent_pc_sample_info.end()) return 0; - return agent_pc_sample_info.at(node_id).size(); + *counter_name = counter_info->name; + *counter_description = counter_info->description; + *is_derived = counter_info->is_derived; + *is_hw_constant = counter_info->is_constant; } void -get_pc_sample_config(uint64_t node_id, - int config_idx, - const char** method, - const char** unit, - uint64_t* min_interval, - uint64_t* max_interval) +counter_block(uint64_t counter_handle, const char** counter_block) { - if(agent_pc_sample_info.find(node_id) == agent_pc_sample_info.end()) return; - *method = agent_pc_sample_info.at(node_id).at(config_idx).at(0).c_str(); - *unit = agent_pc_sample_info.at(node_id).at(config_idx).at(1).c_str(); - *min_interval = - std::stoull(agent_pc_sample_info.at(node_id).at(config_idx).at(2).c_str(), nullptr, 10); - *max_interval = - std::stoull(agent_pc_sample_info.at(node_id).at(config_idx).at(3).c_str(), nullptr, 10); + const auto* counter_info = get_counter_info(rocprofiler_counter_id_t{counter_handle}); + *counter_block = counter_info->block; +} + +void +counter_expression(uint64_t counter_handle, const char** counter_expr) +{ + const auto* counter_info = get_counter_info(rocprofiler_counter_id_t{counter_handle}); + *counter_expr = counter_info->expression; +} + +size_t +get_number_of_dimensions(uint64_t counter_handle) +{ + const auto* counter_info = get_counter_info(rocprofiler_counter_id_t{counter_handle}); + return counter_info->dimensions_count; +} + +void +counter_dimension_ids(uint64_t counter_handle, uint64_t* dimension_ids, size_t num_dimensions) +{ + const auto* counter_info = get_counter_info(rocprofiler_counter_id_t{counter_handle}); + if(num_dimensions != counter_info->dimension_ids.size()) ROCP_FATAL << "Invalid counter handle"; + auto dimensions = std::vector{}; + std::for_each(counter_info->dimension_ids.begin(), + counter_info->dimension_ids.end(), + [&dimensions](auto& dimension) { dimensions.emplace_back(dimension); }); + std::copy(dimensions.begin(), dimensions.end(), dimension_ids); +} + +void +counter_dimension(uint64_t counter_handle, + uint64_t dimension_handle, + const char** dimension_name, + uint64_t* dimension_instance) +{ + const auto* counter_info = get_counter_info(rocprofiler_counter_id_t{counter_handle}); + + rocprofiler::tool::counter_dimension_info_vec_t dimensions = counter_info->dimensions; + + for(auto dim : dimensions) + { + if(dim.id == dimension_handle) + { + *dimension_name = dim.name; + *dimension_instance = dim.instance_size; + return; + } + } +} + +size_t +get_number_of_pc_sample_configs(uint64_t agent_handle) +{ + auto pc_sampling_config = + get_metadata().get_pc_sample_config_info(rocprofiler_agent_id_t{agent_handle}); + + return pc_sampling_config.size(); +} + +void +pc_sample_config(uint64_t agent_handle, + uint64_t config_idx, + uint64_t* method, + uint64_t* unit, + uint64_t* min_interval, + uint64_t* max_interval, + uint64_t* flags) +{ + auto pc_sampling_config = + get_metadata().get_pc_sample_config_info(rocprofiler_agent_id_t{agent_handle}); + if(config_idx >= pc_sampling_config.size()) ROCP_FATAL << "Invalid config idx"; + *method = pc_sampling_config.at(config_idx).method; + *unit = pc_sampling_config.at(config_idx).unit; + *min_interval = pc_sampling_config.at(config_idx).min_interval; + *max_interval = pc_sampling_config.at(config_idx).max_interval; + *flags = pc_sampling_config.at(config_idx).flags; +} + +bool +is_counter_set(uint64_t* counter_handles, uint64_t agent_handle, size_t num_counters) +{ + rocprofiler_profile_config_id_t cfg_id = {.handle = 0}; + for(size_t itr = 0; itr < num_counters; itr++) + { + auto counter_id = rocprofiler_counter_id_t{counter_handles[itr]}; + if(rocprofiler_create_profile_config( + rocprofiler_agent_id_t{agent_handle}, &counter_id, 1, &cfg_id) != + ROCPROFILER_STATUS_SUCCESS) + return false; + } + rocprofiler_destroy_profile_config(cfg_id); + return true; +} + +void +agent_info(uint64_t agent_handle, const char** agent_info_str) +{ + auto agents = get_agents(); + + if(agent_json.empty()) + { + for(auto& [agent_id, value] : agents) + { + auto ss = std::stringstream{}; + { + constexpr auto json_prec = 16; + constexpr auto json_indent = JSONOutputArchive::Options::IndentChar::space; + auto json_opts = JSONOutputArchive::Options{json_prec, json_indent, 0}; + auto json_ar = JSONOutputArchive{ss, json_opts}; + cereal::save(json_ar, value); + } + agent_json.emplace(agent_id, ss.str()); + } + } + *agent_info_str = agent_json.at(rocprofiler_agent_id_t{agent_handle}).c_str(); } ROCPROFILER_EXTERN_C_FINI diff --git a/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.hpp b/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.hpp new file mode 100644 index 0000000000..664142b02d --- /dev/null +++ b/projects/rocprofiler-sdk/source/libexec/rocprofiler-sdk/rocprofiler-avail/rocprofv3_avail.hpp @@ -0,0 +1,84 @@ +// MIT License +// +// Copyright (c) 2023-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. + +#include + +ROCPROFILER_EXTERN_C_INIT + +size_t +get_number_of_agents() ROCPROFILER_EXPORT; +void +agent_handles(uint64_t* agent_handles, size_t num_agents) ROCPROFILER_EXPORT; +uint64_t +get_agent_id(uint64_t agent_handle, int id_type) ROCPROFILER_EXPORT; +size_t +get_number_of_agent_counters(uint64_t agent_handle) ROCPROFILER_EXPORT; +void +agent_counter_handles(uint64_t* counter_handles, + uint64_t agent_handle, + size_t number_of_agent_counters) ROCPROFILER_EXPORT; +void +counter_info(uint64_t counter_handle, + const char** counter_name, + const char** counter_description, + uint8_t* is_derived, + uint8_t* is_hw_constant) ROCPROFILER_EXPORT; +void +counter_block(uint64_t counter_handle, const char** counter_block) ROCPROFILER_EXPORT; + +void +counter_expression(uint64_t counter_handle, const char** counter_expr) ROCPROFILER_EXPORT; + +size_t +get_number_of_dimensions(uint64_t counter_handle) ROCPROFILER_EXPORT; + +void +counter_dimension_ids(uint64_t counter_handle, + uint64_t* dimension_ids, + size_t num_dimensions) ROCPROFILER_EXPORT; + +void +counter_dimension(uint64_t counter_handle, + uint64_t dimension_handle, + const char** dimension_name, + uint64_t* dimension_instance) ROCPROFILER_EXPORT; + +size_t +get_number_of_pc_sample_configs(uint64_t agent_handle) ROCPROFILER_EXPORT; + +void +pc_sample_config(uint64_t agent_handle, + uint64_t config_idx, + uint64_t* method, + uint64_t* unit, + uint64_t* min_interval, + uint64_t* max_interval, + uint64_t* flags) ROCPROFILER_EXPORT; +bool +is_counter_set(uint64_t* counter_handles, + uint64_t agent_handle, + size_t num_counters) ROCPROFILER_EXPORT; + +void +agent_info(uint64_t agent_handle, const char** agent_info_str) ROCPROFILER_EXPORT; + +ROCPROFILER_EXTERN_C_FINI diff --git a/projects/rocprofiler-sdk/tests/CMakeLists.txt b/projects/rocprofiler-sdk/tests/CMakeLists.txt index 5dcfd44d83..9a5fe52c8e 100644 --- a/projects/rocprofiler-sdk/tests/CMakeLists.txt +++ b/projects/rocprofiler-sdk/tests/CMakeLists.txt @@ -86,3 +86,4 @@ add_subdirectory(rocprofv3) # python bindings add_subdirectory(python-bindings) +add_subdirectory(rocprofv3-avail) diff --git a/projects/rocprofiler-sdk/tests/rocprofv3-avail/CMakeLists.txt b/projects/rocprofiler-sdk/tests/rocprofv3-avail/CMakeLists.txt new file mode 100644 index 0000000000..96cb7866a2 --- /dev/null +++ b/projects/rocprofiler-sdk/tests/rocprofv3-avail/CMakeLists.txt @@ -0,0 +1,106 @@ +# +# rocprofv3 tool test +# +cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR) + +project( + rocprofiler-sdk-tests-rocprofv3-avail + LANGUAGES CXX + VERSION 0.0.0) + +find_package(rocprofiler-sdk REQUIRED) +find_package(Python3 REQUIRED) +rocprofiler_configure_pytest_files(CONFIG pytest.ini COPY conftest.py validate.py) + +set(PRELOAD_ENV "${ROCPROFILER_MEMCHECK_PRELOAD_ENV}") + +add_test(NAME rocprofv3-avail-test-hw-counters + COMMAND ${Python3_EXECUTABLE} $ + info --pmc) + +add_test(NAME rocprofv3-avail-test-pc-sample-config + COMMAND ${Python3_EXECUTABLE} $ + info --pc-sampling) + +add_test(NAME rocprofv3-avail-test-check-hw-counters + COMMAND ${Python3_EXECUTABLE} $ -d + 0 pmc-check SQ_WAVES GRBM_COUNT TCC_HIT) + +add_test(NAME rocprofv3-avail-test-check-hw-counters_2 + COMMAND ${Python3_EXECUTABLE} $ + pmc-check SQ_WAVES GRBM_COUNT TCC_HIT:device=0) + +# disable when GPU-0 is navi2, navi3, and navi4 +list(GET rocprofiler-sdk-tests-gfx-info 0 pc-sampling-gpu-0-gfx-info) +if("${pc-sampling-gpu-0-gfx-info}" MATCHES "^gfx(10|11|12)[0-9][0-9]$" + OR "${pc-sampling-gpu-0-gfx-info}" MATCHES "^gfx906$" + OR ROCPROFILER_MEMCHECK STREQUAL "AddressSanitizer") + set(IS_DISABLED ON) +endif() + +set(test-rocprofv3-avail-env "${PRELOAD_ENV}") +set(enable_pc_sampling "ROCPROFILER_PC_SAMPLING_BETA_ENABLED=on") +set_tests_properties( + rocprofv3-avail-test-hw-counters + PROPERTIES + TIMEOUT + 45 + LABELS + "integration-tests" + ENVIRONMENT + "${test-rocprofv3-avail-env}" + PASS_REGULAR_EXPRESSION + "GPU:[0-9]*\\n*;Name:\\t[a-zA-Z_]*\\n;counter_name:\\t[a-zA-Z_]*\\n;description:\\t(.*)\\n*;expression:\\t(.)*\\n*;block:\\t[a-zA-Z]*\\n*;dimension_name:\\t([A-Z_]*)\\n*; dimension_instances:\\t([[0-9]*:[0-9]*\\])*\\n*" + DISABLED + "${IS_DISABLED}") + +set_tests_properties( + rocprofv3-avail-test-pc-sample-config + PROPERTIES + TIMEOUT + 45 + LABELS + "integration-tests;pc-sampling" + ENVIRONMENT + "${test-rocprofv3-avail-env};${enable_pc_sampling}" + PASS_REGULAR_EXPRESSION + "GPU:[0-9]*\\n*;Name:\\t[a-zA-Z_]*\\n;method:(.*)\\n*;unit:(.*)\\n*;min_interval:[0-9]*\\n*;max_interval:[0-9]*\\n*;flags:(.*)\\n*" + DISABLED + "${IS_DISABLED}") + +set_tests_properties( + rocprofv3-avail-test-check-hw-counters + PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT + "${test-rocprofv3-avail-env}" DISABLED "${IS_DISABLED}") + +set_tests_properties( + rocprofv3-avail-test-check-hw-counters_2 + PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT + "${test-rocprofv3-avail-env}" DISABLED "${IS_DISABLED}") + +if(TARGET rocprofv3-list-avail) + + add_test(NAME rocprofv3-avail-test-validate + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py + --rocm-path ${CMAKE_BINARY_DIR}) + + # for validate, explicitly set ROCPROF_LIST_AVAIL_TOOL_LIBRARY since we copy + # rocprofv3-avail to directory + set(test-rocprofv3-avail-validate-env + "${PRELOAD_ENV}" "PYTHONPATH=$ENV{PYTHONPATH}:${CMAKE_BINARY_DIR}/bin/") + + set_tests_properties( + rocprofv3-avail-test-validate + PROPERTIES TIMEOUT + 45 + LABELS + "integration-tests" + ENVIRONMENT + "${test-rocprofv3-avail-validate-env}" + FAIL_REGULAR_EXPRESSION + "${ROCPROFILER_DEFAULT_FAIL_REGEX}" + DEPENDS + rocprofv3-avail-test-copy-module + DISABLED + "${IS_DISABLED}") +endif() diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/conftest.py b/projects/rocprofiler-sdk/tests/rocprofv3-avail/conftest.py similarity index 50% rename from projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/conftest.py rename to projects/rocprofiler-sdk/tests/rocprofv3-avail/conftest.py index c5586632bc..37b05bca3b 100644 --- a/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/conftest.py +++ b/projects/rocprofiler-sdk/tests/rocprofv3-avail/conftest.py @@ -22,50 +22,19 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -import csv +import json import pytest 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.") - parser.addoption("--pc-sample-config-input", action="store", help="Path to csv file.") + parser.addoption( + "--rocm-path", + action="store", + default="scratch-memory-tracing-test.json", + help="Input JSON", + ) @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 - - -@pytest.fixture -def pc_sample_config_input_data(request): - filename = request.config.getoption("--pc-sample-config-input") - data = [] - if filename: - with open(filename, "r") as inp: - reader = csv.DictReader(inp) - for row in reader: - data.append(row) - - return data +def rocm_path(request): + return request.config.getoption("--rocm-path") diff --git a/projects/rocprofiler-sdk/tests/rocprofv3-avail/pytest.ini b/projects/rocprofiler-sdk/tests/rocprofv3-avail/pytest.ini new file mode 100644 index 0000000000..5e1e1c14a0 --- /dev/null +++ b/projects/rocprofiler-sdk/tests/rocprofv3-avail/pytest.ini @@ -0,0 +1,5 @@ + +[pytest] +addopts = --durations=20 -rA -s -vv +testpaths = validate.py +pythonpath = @ROCPROFILER_SDK_TESTS_BINARY_DIR@/pytest-packages diff --git a/projects/rocprofiler-sdk/tests/rocprofv3-avail/validate.py b/projects/rocprofiler-sdk/tests/rocprofv3-avail/validate.py new file mode 100644 index 0000000000..95e0eada67 --- /dev/null +++ b/projects/rocprofiler-sdk/tests/rocprofv3-avail/validate.py @@ -0,0 +1,122 @@ +#!/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 pandas as pd +import sys +import os +import pytest +from rocprofv3_avail_module import avail +import ctypes + + +def test_validate_metrics(rocm_path): + set_library(rocm_path) + lib = avail.get_library() + + agent_counters_dict = avail.get_counters() + for agent, counters in agent_counters_dict.items(): + for counter in counters: + assert counter.name != "" + assert counter.description != "" + if isinstance(counter, avail.derived_counter): + assert counter.expression != "" + elif isinstance(counter, avail.basic_counter): + assert counter.block != "" + assert counter.dimensions != "" + if counter.name == "TA_BUSY_min": + counter.description == "TA block is busy. Min over TA instances." + counter.expression == "reduce(TA_TA_BUSY,min)" + if counter.name == "TA_BUSY_min": + counter.description == "TA block is busy. Min over TA instances." + counter.expression == "reduce(TA_TA_BUSY,min)" + + +def test_validate_list_pc_sample_config(rocm_path): + set_library(rocm_path) + lib = avail.get_library() + + pc_sample_configs_dict = avail.get_pc_sample_configs() + for agent, configs in pc_sample_configs_dict.items(): + for config in configs: + assert config.method != "" + assert config.unit != "" + assert isinstance(config.max_interval, ctypes.c_ulong) == True + assert isinstance(config.min_interval, ctypes.c_ulong) == True + if config.method == "stochastic": + assert config.flags.value == 1 + elif config.method == "host_trap": + assert config.flags.value == 0 + assert config.max_interval.value >= config.min_interval.value + + +def test_counter_set(capsys, rocm_path): + set_library(rocm_path) + + def get_counter_names(counter_ids): + + counter_names = [] + for counter_id in counter_ids: + counter = get_counter_info(counter_id) + if counter.counter_handle == counter_id: + counter_names.append(counter.name) + return counter_names + + def get_agent_name(agent_id): + agent_info_map = get_agent_info_map() + for agent, info in agent_info_map.items(): + if agent == agent_id: + return info["name"] + + lib = avail.get_library() + agent_counters = avail.get_counters() + pmc_input_1 = [] + + for agent, counters in agent_counters.items(): + counter_ids = [] + for counter in counters: + counter_ids.append(counter.counter_handle) + input = {agent: [counter_ids[0]]} + assert avail.check_pmc(input) == True + with pytest.raises(SystemExit) as excinfo: + input = {agent: counter_ids} + avail.check_pmc(input) + output = capsys.readouterr() + test = "{} not collected on agent {}".format( + " ".join(get_counter_names(counter_ids)), get_agent_name(agent) + ) + assert test == output + + +def set_library(rocm_path): + ROCPROF_LIST_AVAIL_TOOL_LIBRARY = ( + f"{rocm_path}/libexec/rocprofiler-sdk/librocprofv3-list-avail.so" + ) + avail.loadLibrary.libname = os.environ.get( + "ROCPROF_LIST_AVAIL_TOOL_LIBRARY", ROCPROF_LIST_AVAIL_TOOL_LIBRARY + ) + + +if __name__ == "__main__": + exit_code = pytest.main(["-x", __file__] + sys.argv[1:]) + sys.exit(exit_code) diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/CMakeLists.txt b/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/CMakeLists.txt index 6e98aabbb8..c9dd2a33a1 100644 --- a/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/CMakeLists.txt +++ b/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/CMakeLists.txt @@ -10,9 +10,7 @@ project( find_package(rocprofiler-sdk REQUIRED) -rocprofiler_configure_pytest_files(CONFIG pytest.ini COPY validate.py conftest.py - input.json) - +rocprofiler_configure_pytest_files(CONFIG pytest.ini COPY input.json) # basic-metrics add_test( NAME rocprofv3-test-list-avail-execute @@ -52,39 +50,10 @@ set_tests_properties( ENVIRONMENT "${cc-env-list-metrics}" PASS_REGULAR_EXPRESSION - "gpu-agent:[0-9]*\\n*; Name:\\t[a-zA-Z_]*\\n;Description:\\t(.*)\\n*;Expression:\\t(.)*\\n*;Block:\\t[a-zA-Z]*\\n*;Dimensions:\\t([A-Z_]*\\[[0-9]*:[0-9]*\\])*\\n*;Method:\\t(.*);Unit:\\t(.*);Minimum_Interval:\\t[0-9]*;Maximum_Interval:\\t[0-9]*\\n*;" + "GPU:[0-9]*\\n*;Name:\\t[a-zA-Z_]*\\n;counter_name:\\t[a-zA-Z_]*\\n;description:\\t(.*)\\n*;expression:\\t(.)*\\n*;block:\\t[a-zA-Z]*\\n*;dimension_name:\\t([A-Z_]*)\\n*; dimension_instances:\\t([[0-9]*:[0-9]*\\])*\\n*;method:(.*)\\n*;unit:(.*)\\n*;min_interval:[0-9]*\\n*;max_interval:[0-9]*\\n*;flags:(.*)\\n*" ) set_tests_properties( rocprofv3-test-list-avail-trace-execute PROPERTIES TIMEOUT 45 LABELS "integration-tests" ENVIRONMENT "${cc-env-list-metrics}" FAIL_REGULAR_EXPRESSION "${ROCPROFILER_DEFAULT_FAIL_REGEX}") - -set(VALIDATION_FILES - ${CMAKE_CURRENT_BINARY_DIR}/out_counter_collection_2/metrics_basic_metrics.csv - ${CMAKE_CURRENT_BINARY_DIR}/out_counter_collection_2/metrics_derived_metrics.csv - ${CMAKE_CURRENT_BINARY_DIR}/out_counter_collection_2/metrics_pc_sample_config.csv) - -add_test( - NAME rocprofv3-test-list-avail-validate - COMMAND - ${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py - --derived-metrics-input - ${CMAKE_CURRENT_BINARY_DIR}/out_counter_collection_2/metrics_derived_metrics.csv - --basic-metrics-input - ${CMAKE_CURRENT_BINARY_DIR}/out_counter_collection_2/metrics_basic_metrics.csv - --pc-sample-config-input - ${CMAKE_CURRENT_BINARY_DIR}/out_counter_collection_2/metrics_pc_sample_config.csv) - -set_tests_properties( - rocprofv3-test-list-avail-validate - PROPERTIES TIMEOUT - 45 - LABELS - "integration-tests" - DEPENDS - rocprofv3-test-list-metrics-execute - FAIL_REGULAR_EXPRESSION - "${ROCPROFILER_DEFAULT_FAIL_REGEX}" - ATTACHED_FILES_ON_FAIL - "${VALIDATION_FILES}") diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/pytest.ini b/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/pytest.ini index 5e1e1c14a0..c3c15206ee 100644 --- a/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/pytest.ini +++ b/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/pytest.ini @@ -1,5 +1,4 @@ [pytest] addopts = --durations=20 -rA -s -vv -testpaths = validate.py pythonpath = @ROCPROFILER_SDK_TESTS_BINARY_DIR@/pytest-packages diff --git a/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/validate.py b/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/validate.py deleted file mode 100644 index 87e165b830..0000000000 --- a/projects/rocprofiler-sdk/tests/rocprofv3/counter-collection/list_metrics/validate.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/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 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)" - - -def test_validate_list_pc_sample_config(pc_sample_config_input_data): - for row in pc_sample_config_input_data: - assert row["Agent_Id"].isdigit() == True - assert row["Method"] != "" - assert row["Unit"] != "" - assert row["Minimum_Interval"].isdigit() == True - assert row["Maximum_Interval"].isdigit() == True - - -if __name__ == "__main__": - exit_code = pytest.main(["-x", __file__] + sys.argv[1:]) - sys.exit(exit_code)