diff --git a/projects/rocprofiler-compute/CHANGELOG.md b/projects/rocprofiler-compute/CHANGELOG.md index 81e578458b..2c9c211701 100644 --- a/projects/rocprofiler-compute/CHANGELOG.md +++ b/projects/rocprofiler-compute/CHANGELOG.md @@ -127,6 +127,9 @@ Full documentation for ROCm Compute Profiler is available at [https://rocm.docs. * `--list-available-metrics` analyze mode option to display the metrics available for analysis. * `--block` option cannot be used with `--list-metrics` and `--list-available-metrics`options. +* Default rocprof interface changed from rocprofv3 to rocprofiler-sdk + * Use ROCPROF=rocprofv3 to use rocprofv3 interface + ### Removed * Usage of `rocm-smi` in favor of `amd-smi`. diff --git a/projects/rocprofiler-compute/src/rocprof_compute_base.py b/projects/rocprofiler-compute/src/rocprof_compute_base.py index 9609a30c26..5eba6c46ce 100644 --- a/projects/rocprofiler-compute/src/rocprof_compute_base.py +++ b/projects/rocprofiler-compute/src/rocprof_compute_base.py @@ -113,11 +113,7 @@ class RocProfCompute: def detect_profiler(self) -> None: profiler_mode = detect_rocprof(self.__args) - if str(profiler_mode).endswith("rocprof"): - self.__profiler_mode = "rocprofv1" - elif str(profiler_mode).endswith("rocprofv2"): - self.__profiler_mode = "rocprofv2" - elif str(profiler_mode).endswith("rocprofv3"): + if str(profiler_mode).endswith("rocprofv3"): self.__profiler_mode = "rocprofv3" elif str(profiler_mode) == "rocprofiler-sdk": self.__profiler_mode = "rocprofiler-sdk" @@ -303,16 +299,32 @@ class RocProfCompute: sys.exit(0) + profiler_classes = { + "rocprofv3": ( + "rocprof_compute_profile.profiler_rocprof_v3", + "rocprof_v3_profiler", + ), + "rocprofiler-sdk": ( + "rocprof_compute_profile.profiler_rocprofiler_sdk", + "rocprofiler_sdk_profiler", + ), + } + + if self.__profiler_mode not in profiler_classes: + console_error("Unsupported profiler") + + module_name, class_name = profiler_classes[self.__profiler_mode] + module = importlib.import_module(module_name) + profiler_class = getattr(module, class_name) + + return profiler_class( + self.__args, + self.__profiler_mode, + self.__soc[self.__mspec.gpu_arch], + ) + def create_profiler(self) -> object: profiler_classes = { - "rocprofv1": ( - "rocprof_compute_profile.profiler_rocprof_v1", - "rocprof_v1_profiler", - ), - "rocprofv2": ( - "rocprof_compute_profile.profiler_rocprof_v2", - "rocprof_v2_profiler", - ), "rocprofv3": ( "rocprof_compute_profile.profiler_rocprof_v3", "rocprof_v3_profiler", diff --git a/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_base.py b/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_base.py index ec82e0cadd..f546d486a6 100644 --- a/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_base.py +++ b/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_base.py @@ -466,16 +466,9 @@ class RocProfCompute_Base: console_debug(output) console_log("profiling", f"Current input file: {fname}") - - if self.__profiler in ( - "rocprofv1", - "rocprofv2", - "rocprofv3", - "rocprofiler-sdk", - ): - options = self.get_profiler_options(str(fname), self._soc) - start_time = time.time() - + options = self.get_profiler_options(fname, self._soc) + start_time = time.time() + if self.__profiler == "rocprofv3" or self.__profiler == "rocprofiler-sdk": # Only 1-run case is permitted for attach/detach if (isinstance(options, list) and "--pid" in options) or ( isinstance(options, dict) @@ -490,7 +483,6 @@ class RocProfCompute_Base: f'passes. Please use "--block" or "--set" ' f"to adjust or reduce the requested performance metrics!" ) - run_prof( fname=str(fname), profiler_options=options, diff --git a/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_rocprof_v1.py b/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_rocprof_v1.py deleted file mode 100644 index 2edc9ab713..0000000000 --- a/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_rocprof_v1.py +++ /dev/null @@ -1,109 +0,0 @@ -############################################################################## -# MIT License -# -# Copyright (c) 2021 - 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 pathlib import Path - -import config -from rocprof_compute_profile.profiler_base import RocProfCompute_Base -from utils.logger import console_log, demarcate -from utils.utils import replace_timestamps, store_app_cmd - - -class rocprof_v1_profiler(RocProfCompute_Base): - def __init__(self, profiling_args, profiler_mode, soc): - super().__init__(profiling_args, profiler_mode, soc) - self.ready_to_profile = ( - self.get_args().roof_only - and not Path(self.get_args().path).joinpath("pmc_perf.csv").is_file() - or not self.get_args().roof_only - ) - - def get_profiler_options(self, fname, soc): - fbase = Path(fname).stem - app_cmd = self.get_args().remaining - - args = [] - # rocprof v1 does not support some counters on gfx 908 architecture - if soc.get_arch() == "gfx908": - metrics_path = str( - Path(str(config.rocprof_compute_home)).joinpath( - "rocprof_compute_soc", "profile_configs", "metrics.xml" - ) - ) - args += ["-m", metrics_path] - - args += [ - # v1 requires request for timestamps - "--timestamp", - "on", - # v1 requires csv extension - "-o", - self.get_args().path + "/" + fbase + ".csv", - # v1 does require quotes on app cmd - '"' + app_cmd + '"', - ] - # store original args for debug message - store_app_cmd([ - "--timestamp", - "on", - "-o", - self.get_args().path + "/" + fbase + ".csv", - app_cmd, - ]) - return args - - # ----------------------- - # Required child methods - # ----------------------- - @demarcate - def pre_processing(self): - """Perform any pre-processing steps prior to profiling.""" - super().pre_processing() - - @demarcate - def run_profiling(self, version: str, prog: str): - """Run profiling.""" - if self.ready_to_profile: - if self.get_args().roof_only: - console_log( - "roofline", "Generating pmc_perf.csv (roofline counters only)." - ) - # Log profiling options and setup filtering - super().run_profiling(version, prog) - else: - console_log("roofline", "Detected existing pmc_perf.csv") - - @demarcate - def post_processing(self): - """Perform any post-processing steps prior to profiling.""" - if self.ready_to_profile: - # Manually join each pmc_perf*.csv output - self.join_prof() - # Run roofline microbenchmark - super().post_processing() - # Replace timestamp data to solve a known rocprof bug - replace_timestamps(self.get_args().path) - else: - console_log("roofline", "Detected existing pmc_perf.csv") diff --git a/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_rocprof_v2.py b/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_rocprof_v2.py deleted file mode 100644 index 0ea2eff365..0000000000 --- a/projects/rocprofiler-compute/src/rocprof_compute_profile/profiler_rocprof_v2.py +++ /dev/null @@ -1,99 +0,0 @@ -############################################################################## -# MIT License -# -# Copyright (c) 2021 - 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 shlex -from pathlib import Path - -import config -from rocprof_compute_profile.profiler_base import RocProfCompute_Base -from utils.logger import console_log, demarcate -from utils.utils import replace_timestamps, store_app_cmd - - -class rocprof_v2_profiler(RocProfCompute_Base): - def __init__(self, profiling_args, profiler_mode, soc): - super().__init__(profiling_args, profiler_mode, soc) - self.ready_to_profile = ( - self.get_args().roof_only - and not Path(self.get_args().path).joinpath("pmc_perf.csv").is_file() - or not self.get_args().roof_only - ) - - def get_profiler_options(self, fname, soc): - app_cmd = shlex.split(self.get_args().remaining) - - args = [] - # rocprof v2 does not support some counters on gfx 908 architecture - if soc.get_arch() == "gfx908": - metrics_path = str( - Path(str(config.rocprof_compute_home)).joinpath( - "rocprof_compute_soc", "profile_configs", "metrics.xml" - ) - ) - args += ["-m", metrics_path] - - args += [ - # v2 requires output directory argument - "-d", - self.get_args().path + "/" + "out", - ] - args.extend(app_cmd) - # store args for debug message - store_app_cmd(args) - return args - - # ----------------------- - # Required child methods - # ----------------------- - @demarcate - def pre_processing(self): - """Perform any pre-processing steps prior to profiling.""" - super().pre_processing() - - @demarcate - def run_profiling(self, version, prog): - """Run profiling.""" - if self.ready_to_profile: - if self.get_args().roof_only: - console_log( - "roofline", "Generating pmc_perf.csv (roofline counters only)." - ) - # Log profiling options and setup filtering - super().run_profiling(version, prog) - else: - console_log("roofline", "Detected existing pmc_perf.csv") - - @demarcate - def post_processing(self): - """Perform any post-processing steps prior to profiling.""" - if self.ready_to_profile: - # Manually join each pmc_perf*.csv output - self.join_prof() - # Run roofline microbenchmark - super().post_processing() - # Replace timestamp data to solve a known rocprof bug - replace_timestamps(self.get_args().path) - else: - console_log("roofline", "Detected existing pmc_perf.csv") diff --git a/projects/rocprofiler-compute/src/rocprof_compute_soc/profile_configs/gfx908_metrics.xml b/projects/rocprofiler-compute/src/rocprof_compute_soc/profile_configs/gfx908_metrics.xml deleted file mode 100644 index c38a8f8118..0000000000 --- a/projects/rocprofiler-compute/src/rocprof_compute_soc/profile_configs/gfx908_metrics.xml +++ /dev/null @@ -1,737 +0,0 @@ - - # CPC counters - - - - - - - - - - - - - - - - - - - # GRBM counters - - - - - - - - - - - # SPI counters - - - - - - - - - - - - - - - - - - - # SQ counters - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # TA counters - - - - - - - - - - - - - - - - - # TCA counters - - - # TCC counters - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # TCP counters - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # TD counters - - - - - - - - diff --git a/projects/rocprofiler-compute/src/rocprof_compute_soc/profile_configs/metrics.xml b/projects/rocprofiler-compute/src/rocprof_compute_soc/profile_configs/metrics.xml deleted file mode 100644 index d16e9bca08..0000000000 --- a/projects/rocprofiler-compute/src/rocprof_compute_soc/profile_configs/metrics.xml +++ /dev/null @@ -1,163 +0,0 @@ -#include "gfx908_metrics.xml" - - - - - - - - - - - - - - - - - - - - - - - - - #xlu - TA - - - - - - - - - - - - - - - - - - - - - - - - #xlu -TD - - - - - - - - - #xlu -TCP - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #xlu - TCP - - - - - #xlu - TCC - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_base.py b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_base.py index 761c3cc113..bc8f756e14 100644 --- a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_base.py +++ b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_base.py @@ -49,14 +49,12 @@ from utils.parser import BUILD_IN_VARS, SUPPORTED_DENOM from utils.specs import MachineSpecs from utils.utils import ( add_counter_extra_config_input_yaml, - capture_subprocess_output, convert_metric_id_to_panel_info, detect_rocprof, get_submodules, is_tcc_channel_counter, mibench, parse_sets_yaml, - using_v3, ) @@ -370,8 +368,7 @@ class OmniSoC_Base: # Handle TCC channel counters: if hw_counter_matches has elems ending with '[' # Expand and interleve the TCC channel counters # e.g. TCC_HIT[0] TCC_ATOMIC[0] ... TCC_HIT[1] TCC_ATOMIC[1] ... - num_xcd_for_pmc_file = int(self._mspec.num_xcd) if using_v3() else 1 - + num_xcd_for_pmc_file = int(self._mspec.num_xcd) for counter_name in counters.copy(): if counter_name.startswith("TCC") and counter_name.endswith("["): counters.remove(counter_name) @@ -388,18 +385,6 @@ class OmniSoC_Base: """Filter default performance counter set based on user arguments""" counters, filter_blocks = self.detect_counters() - if not using_v3(): - # Counters not supported in rocprof v1 / v2 - counters = counters - { - "SQ_INSTS_VALU_MFMA_F8", - "SQ_INSTS_VALU_MFMA_MOPS_F8", - "SQC_DCACHE_INFLIGHT_LEVEL", - "SQC_ICACHE_INFLIGHT_LEVEL", - "SQ_VMEM_WR_TA_DATA_FIFO_FULL", - "SQ_VMEM_TA_ADDR_FIFO_FULL", - "SQ_VMEM_TA_CMD_FIFO_FULL", - } - # TCP_TCP_LATENCY_sum not supported for MI300 (gfx940, gfx941, gfx942) if self.__arch in ("gfx940", "gfx941", "gfx942"): counters = counters - {"TCP_TCP_LATENCY_sum"} @@ -467,84 +452,52 @@ class OmniSoC_Base: if rocprof_cmd != "rocprofiler-sdk": console_warning( - "rocprof v1/v2/v3 interfaces will be removed in favor of " - "rocprofiler-sdk interface in a future release. To use " - "rocprofiler-sdk, set ROCPROF to 'rocprofiler-sdk' and " - "optionally provide the path to librocprofiler-sdk.so via " - "--rocprofiler-sdk-library-path." + "rocprofv3 interface is deprecated and will be removed " + "in a future release." ) rocprof_counters: set[str] = set() - if rocprof_cmd.endswith("rocprof"): - for list_type in ["--list-basic", "--list-derived"]: - command = [rocprof_cmd, list_type] - success, output = capture_subprocess_output( - command, enable_logging=False - ) - # return code should be 1 so success should be False - if success: - console_error( - "Failed to list rocprof supported counters using command: " - f"{command}" - ) - - for line in output.splitlines(): - if "gpu-agent" in line: - counters, _ = self.parse_counters_text( - line.split(":")[1].strip() - ) - rocprof_counters.update(counters) - elif rocprof_cmd.endswith("rocprofv2"): - command = [rocprof_cmd, "--list-counters"] - success, output = capture_subprocess_output(command, enable_logging=False) - # return code should be 1 so success should be False - if success: - console_error( - "Failed to list rocprof supported counters using command: " - f"{command}" - ) - - for line in output.splitlines(): - if "gfx" in line: - counters, _ = self.parse_counters_text(line.split(":")[2].strip()) - rocprof_counters.update(counters) - elif rocprof_cmd.endswith("rocprofv3") or rocprof_cmd == "rocprofiler-sdk": - # Point to counter definition - old_rocprofiler_metrics_path = os.environ.get("ROCPROFILER_METRICS_PATH") - os.environ["ROCPROFILER_METRICS_PATH"] = str( - config.rocprof_compute_home / "rocprof_compute_soc" / "profile_configs" - ) - sys.path.append( - str(Path(args.rocprofiler_sdk_library_path).parent.parent / "bin") - ) - - from rocprofv3_avail_module import avail - - avail.loadLibrary.libname = str( - Path(args.rocprofiler_sdk_library_path).parent.parent - / "lib" - / "rocprofiler-sdk" - / "librocprofv3-list-avail.so" - ) - counters = avail.get_counters() - rocprof_counters = { - counter.name - for counter in counters[list(counters.keys())[0]] - if hasattr(counter, "block") or hasattr(counter, "expression") - } - # Reset env. var. - if old_rocprofiler_metrics_path is None: - del os.environ["ROCPROFILER_METRICS_PATH"] - else: - os.environ["ROCPROFILER_METRICS_PATH"] = old_rocprofiler_metrics_path - - else: + if not ( + str(rocprof_cmd).endswith("rocprofv3") + or str(rocprof_cmd) == "rocprofiler-sdk" + ): console_error( - f"Incompatible profiler: {rocprof_cmd}. Supported profilers include: " + f"Incompatible profiler: {rocprof_cmd}. " + "Supported profilers include: " f"{get_submodules('rocprof_compute_profile')}" ) + # Point to counter definition + old_rocprofiler_metrics_path = os.environ.get("ROCPROFILER_METRICS_PATH") + os.environ["ROCPROFILER_METRICS_PATH"] = str( + config.rocprof_compute_home / "rocprof_compute_soc" / "profile_configs" + ) + sys.path.append( + str( + Path(self.get_args().rocprofiler_sdk_library_path).parent.parent / "bin" + ) + ) + from rocprofv3_avail_module import avail + + avail.loadLibrary.libname = str( + Path(self.get_args().rocprofiler_sdk_library_path).parent.parent + / "lib" + / "rocprofiler-sdk" + / "librocprofv3-list-avail.so" + ) + counters = avail.get_counters() + rocprof_counters = { + counter.name + for counter in counters[list(counters.keys())[0]] + if hasattr(counter, "block") or hasattr(counter, "expression") + } + # Reset env. var. + if old_rocprofiler_metrics_path is None: + del os.environ["ROCPROFILER_METRICS_PATH"] + else: + os.environ["ROCPROFILER_METRICS_PATH"] = old_rocprofiler_metrics_path + return rocprof_counters @demarcate @@ -600,14 +553,7 @@ class OmniSoC_Base: CounterFile(counter + ".txt", self.__perfmon_config) ) output_files[-1].add(counter) - - if using_v3(): - # v3 does not support SQ_ACCUM_PREV_HIRES. Use custom counters - # defined in counter_defs.yaml that utilize accumulate(), - # with _ACCUM suffix. - output_files[-1].add(f"{counter}_ACCUM") - else: - output_files[-1].add("SQ_ACCUM_PREV_HIRES") + output_files[-1].add(f"{counter}_ACCUM") accu_file_count += 1 file_count = 0 @@ -708,12 +654,12 @@ class OmniSoC_Base: for ctr in f.blocks[block_name].elements ]: pmc.append(ctr) - if using_v3() and is_tcc_channel_counter(ctr): + # Add TCC channel counters definitions + if is_tcc_channel_counter(ctr): counter_name = ctr.split("[")[0] idx = int(ctr.split("[")[1].split("]")[0]) xcd_idx = idx // int(self._mspec.l2_banks) channel_idx = idx % int(self._mspec.l2_banks) - expression = ( f"select({counter_name}," f"[DIMENSION_XCC=[{xcd_idx}], " @@ -743,16 +689,6 @@ class OmniSoC_Base: with open(file_name_yaml, "w") as fp: fp.write(yaml.dump(counter_def, sort_keys=False)) - # Add a timestamp file - # TODO: Does v3 need this? - if not using_v3(): - timestamp_file = workload_perfmon_dir / "timestamps.txt" - with open(timestamp_file, "w") as fd: - fd.write("pmc:\n\n") - fd.write("gpu:\n") - fd.write("range:\n") - fd.write("kernel:\n") - # ---------------------------------------------------- # Required methods to be implemented by child classes # ---------------------------------------------------- diff --git a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx908.py b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx908.py index 6a83282b25..e79edd224b 100644 --- a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx908.py +++ b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx908.py @@ -36,8 +36,7 @@ class gfx908_soc(OmniSoC_Base): def __init__(self, args: argparse.Namespace, mspec: MachineSpecs) -> None: super().__init__(args, mspec) self.set_arch("gfx908") - - self.set_compatible_profilers(["rocprofv1", "rocprofv3", "rocprofiler-sdk"]) + self.set_compatible_profilers(["rocprofv3", "rocprofiler-sdk"]) # Per IP block max number of simultaneous counters. GFX IP Blocks self.set_perfmon_config(mi_gpu_specs.get_perfmon_config("gfx908")) diff --git a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx90a.py b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx90a.py index 4aeadc3843..6731b82d2c 100644 --- a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx90a.py +++ b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx90a.py @@ -37,8 +37,6 @@ class gfx90a_soc(OmniSoC_Base): super().__init__(args, mspec) self.set_arch("gfx90a") self.set_compatible_profilers([ - "rocprofv1", - "rocprofv2", "rocprofv3", "rocprofiler-sdk", ]) diff --git a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx940.py b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx940.py index 6e24dd0ebf..5f751fdc8b 100644 --- a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx940.py +++ b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx940.py @@ -37,8 +37,6 @@ class gfx940_soc(OmniSoC_Base): super().__init__(args, mspec) self.set_arch("gfx940") self.set_compatible_profilers([ - "rocprofv1", - "rocprofv2", "rocprofv3", "rocprofiler-sdk", ]) diff --git a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx941.py b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx941.py index 5b88162404..f3b01b45f3 100644 --- a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx941.py +++ b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx941.py @@ -37,8 +37,6 @@ class gfx941_soc(OmniSoC_Base): super().__init__(args, mspec) self.set_arch("gfx941") self.set_compatible_profilers([ - "rocprofv1", - "rocprofv2", "rocprofv3", "rocprofiler-sdk", ]) diff --git a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx942.py b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx942.py index 5aa7a29f3d..5354494b3a 100644 --- a/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx942.py +++ b/projects/rocprofiler-compute/src/rocprof_compute_soc/soc_gfx942.py @@ -37,8 +37,6 @@ class gfx942_soc(OmniSoC_Base): super().__init__(args, mspec) self.set_arch("gfx942") self.set_compatible_profilers([ - "rocprofv1", - "rocprofv2", "rocprofv3", "rocprofiler-sdk", ]) diff --git a/projects/rocprofiler-compute/src/utils/utils.py b/projects/rocprofiler-compute/src/utils/utils.py index 1356dedb7d..2ae5020d7c 100644 --- a/projects/rocprofiler-compute/src/utils/utils.py +++ b/projects/rocprofiler-compute/src/utils/utils.py @@ -58,7 +58,6 @@ from utils.logger import ( console_warning, demarcate, ) -from utils.mi_gpu_spec import mi_gpu_specs rocprof_cmd = "" rocprof_args = "" @@ -144,40 +143,6 @@ def add_counter_extra_config_input_yaml( return data -def extract_counter_info_extra_config_input_yaml( - data: dict[str, Any], counter_name: str -) -> Optional[dict]: - """ - Extract the full counter dictionary from 'data' for the given counter_name. - - Args: - data (dict): The source YAML dict. - counter_name (str): The counter to find. - - Returns: - Optional[dict]: The full counter dict if found, else None. - """ - counters = data.get("rocprofiler-sdk", {}).get("counters", []) - for counter in counters: - if counter.get("name") == counter_name: - return counter - return None - - -def using_v1() -> bool: - return "ROCPROF" in os.environ.keys() and os.environ["ROCPROF"].endswith("rocprof") - - -def using_v3() -> bool: - return "ROCPROF" not in os.environ.keys() or ( - "ROCPROF" in os.environ.keys() - and ( - os.environ["ROCPROF"].endswith("rocprofv3") - or os.environ["ROCPROF"] == "rocprofiler-sdk" - ) - ) - - def get_version(rocprof_compute_home: Path) -> dict[str, str]: """Return ROCm Compute Profiler versioning info""" @@ -240,7 +205,8 @@ def detect_rocprof(args: argparse.Namespace) -> str: """Detect loaded rocprof version. Resolve path and set cmd globally.""" global rocprof_cmd - if os.environ.get("ROCPROF") == "rocprofiler-sdk": + # Default is rocprofiler-sdk + if os.environ.get("ROCPROF", "rocprofiler-sdk") == "rocprofiler-sdk": if not Path(args.rocprofiler_sdk_library_path).exists(): console_error( "Could not find rocprofiler-sdk library at " @@ -249,45 +215,22 @@ def detect_rocprof(args: argparse.Namespace) -> str: rocprof_cmd = "rocprofiler-sdk" console_debug(f"rocprof_cmd is {rocprof_cmd}") console_debug(f"rocprofiler_sdk_path is {args.rocprofiler_sdk_library_path}") - return rocprof_cmd - - # detect rocprof - if not "ROCPROF" in os.environ.keys(): - # default rocprof - rocprof_cmd = "rocprofv3" else: + # If ROCPROF is not set to rocprofiler-sdk rocprof_cmd = os.environ["ROCPROF"] - - # resolve rocprof path - rocprof_path = shutil.which(rocprof_cmd) - - if not rocprof_path: - rocprof_cmd = "rocprofv3" - console_warning( - f"Unable to resolve path to {rocprof_cmd} binary. Reverting to default." - ) rocprof_path = shutil.which(rocprof_cmd) if not rocprof_path: console_error( - "Please verify installation or set ROCPROF environment variable " - "with full path." + f"Unable to resolve path to {rocprof_cmd} binary. " + "Please verify installation or set ROCPROF " + "environment variable with full path." ) - else: - # Resolve any sym links in file path rocprof_path = str(Path(rocprof_path.rstrip("\n")).resolve()) + console_debug(f"rocprof_cmd is {str(rocprof_cmd)}") console_debug(f"ROC Profiler: {rocprof_path}") - - console_debug(f"rocprof_cmd is {rocprof_cmd}") return rocprof_cmd -# TODO: v1/v2 function, to be removed -def store_app_cmd(args: argparse.Namespace) -> None: - global rocprof_args - rocprof_args = args - - -@demarcate def capture_subprocess_output( subprocess_args: list[str], new_env: Optional[dict[str, str]] = None, @@ -766,47 +709,40 @@ def run_prof( default_options = ["-i", fname] options = default_options + cast(list[str], profiler_options) - if using_v3(): - if rocprof_cmd == "rocprofiler-sdk": - options["ROCPROF_AGENT_INDEX"] = "absolute" - else: - options = ["-A", "absolute"] + options + if rocprof_cmd == "rocprofiler-sdk": + options["ROCPROF_AGENT_INDEX"] = "absolute" else: - if is_mode_live_attach: - console_error( - "The live attach/detach only supports rocprofv3 or rocprofiler-sdk" - ) + options = ["-A", "absolute"] + options new_env = os.environ.copy() - if using_v3(): - # Counter definitions - with open( - config.rocprof_compute_home - / "rocprof_compute_soc" - / "profile_configs" - / "counter_defs.yaml", - ) as file: - counter_defs = yaml.safe_load(file) - # Extra counter definitions - if fpath.with_suffix(".yaml").exists(): - with open(fpath.with_suffix(".yaml")) as file: - counter_defs["rocprofiler-sdk"]["counters"].extend( - yaml.safe_load(file)["rocprofiler-sdk"]["counters"] - ) - # Write counter definitions to a temporary file - tmpfile_path = ( - Path(tempfile.mkdtemp(prefix="rocprof_counter_defs_", dir="/tmp")) - / "counter_defs.yaml" - ) - with open(tmpfile_path, "w") as tmpfile: - yaml.dump(counter_defs, tmpfile, default_flow_style=False, sort_keys=False) - # Set counter definitions - new_env["ROCPROFILER_METRICS_PATH"] = str(tmpfile_path.parent) - console_debug( - "Adding env var for counter definitions: " - f"ROCPROFILER_METRICS_PATH={new_env['ROCPROFILER_METRICS_PATH']}" - ) + # Counter definitions + with open( + config.rocprof_compute_home + / "rocprof_compute_soc" + / "profile_configs" + / "counter_defs.yaml", + ) as file: + counter_defs = yaml.safe_load(file) + # Extra counter definitions + if Path(fname).with_suffix(".yaml").exists(): + with open(Path(fname).with_suffix(".yaml")) as file: + counter_defs["rocprofiler-sdk"]["counters"].extend( + yaml.safe_load(file)["rocprofiler-sdk"]["counters"] + ) + # Write counter definitions to a temporary file + tmpfile_path = ( + Path(tempfile.mkdtemp(prefix="rocprof_counter_defs_", dir="/tmp")) + / "counter_defs.yaml" + ) + with open(tmpfile_path, "w") as tmpfile: + yaml.dump(counter_defs, tmpfile, default_flow_style=False, sort_keys=False) + # Set counter definitions + new_env["ROCPROFILER_METRICS_PATH"] = str(tmpfile_path.parent) + console_debug( + "Adding env var for counter definitions: " + f"ROCPROFILER_METRICS_PATH={new_env['ROCPROFILER_METRICS_PATH']}" + ) # set required env var for >= mi300 if mspec.gpu_model.lower() not in ( @@ -910,92 +846,59 @@ def run_prof( results_files: list[str] = [] if format_rocprof_output == "rocpd": - if rocprof_cmd == "rocprofiler-sdk" or rocprof_cmd.endswith("v3"): - # Write results_fbase.csv - rocpd_data.convert_db_to_csv( - glob.glob(f"{workload_dir}/out/pmc_1/*/*.db")[0], - f"{workload_dir}/results_{fbase}.csv", + # Write results_fbase.csv + rocpd_data.convert_db_to_csv( + glob.glob(workload_dir + "/out/pmc_1/*/*.db")[0], + workload_dir + f"/results_{fbase}.csv", + ) + if retain_rocpd_output: + shutil.copyfile( + glob.glob(workload_dir + "/out/pmc_1/*/*.db")[0], + workload_dir + "/" + fbase + ".db", ) - if retain_rocpd_output: - shutil.copyfile( - glob.glob(f"{workload_dir}/out/pmc_1/*/*.db")[0], - f"{workload_dir}/{fbase}.db", - ) - console_warning( - f"Retaining large raw rocpd database: {workload_dir}/{fbase}.db" - ) - # Remove temp directory - shutil.rmtree(f"{workload_dir}/out") - return - else: - console_error( - "rocpd output format is only supported with " - "rocprofiler-sdk or rocprofv3." + console_warning( + f"Retaining large raw rocpd database: {workload_dir}/{fbase}.db" ) - elif rocprof_cmd.endswith("v2"): - # rocprofv2 has separate csv files for each process - results_files = glob.glob(f"{workload_dir}/out/pmc_1/results_*.csv") + # Remove temp directory + shutil.rmtree(workload_dir + "/" + "out") + return - if len(results_files) == 0: - return + # rocprofv3 requires additional processing for each process + results_files = process_rocprofv3_output( + format_rocprof_output, workload_dir, is_timestamps + ) - # Combine results into single CSV file - combined_results = pd.concat( - [pd.read_csv(f) for f in results_files], ignore_index=True - ) - - # Overwrite column to ensure unique IDs. - combined_results["Dispatch_ID"] = range(0, len(combined_results)) - - combined_results.to_csv( - f"{workload_dir}/out/pmc_1/results_{fbase}.csv", index=False - ) - elif rocprof_cmd.endswith("v3") or rocprof_cmd == "rocprofiler-sdk": - # rocprofv3 requires additional processing for each process - results_files = process_rocprofv3_output( - format_rocprof_output, workload_dir, is_timestamps - ) - - if rocprof_cmd == "rocprofiler-sdk": + if rocprof_cmd == "rocprofiler-sdk": + # TODO: as rocprofv3 --kokkos-trace feature improves, + # rocprof-compute should make updates accordingly + if "ROCPROF_HIP_RUNTIME_API_TRACE" in options: + process_hip_trace_output(workload_dir, fbase) + else: + if "--kokkos-trace" in options: # TODO: as rocprofv3 --kokkos-trace feature improves, # rocprof-compute should make updates accordingly - if "ROCPROF_HIP_RUNTIME_API_TRACE" in options: - process_hip_trace_output(workload_dir, fbase) - else: - if "--kokkos-trace" in options: - # TODO: as rocprofv3 --kokkos-trace feature improves, - # rocprof-compute should make updates accordingly - process_kokkos_trace_output(workload_dir, fbase) - elif "--hip-trace" in options: - process_hip_trace_output(workload_dir, fbase) + process_kokkos_trace_output(workload_dir, fbase) + elif "--hip-trace" in options: + process_hip_trace_output(workload_dir, fbase) - if not results_files: - console_warning( - f"Cannot write results for {fbase}.csv due to no counter " - "csv files generated." - ) - return - - # Combine results into single CSV file + # Combine results into single CSV file + if results_files: combined_results = pd.concat( [pd.read_csv(f) for f in results_files], ignore_index=True ) - - # Overwrite column to ensure unique IDs. - combined_results["Dispatch_ID"] = range(0, len(combined_results)) - - combined_results.to_csv( - f"{workload_dir}/out/pmc_1/results_{fbase}.csv", index=False + else: + console_warning( + f"Cannot write results for {fbase}.csv due to no counter " + "csv files generated." ) + return - if not using_v3() and not using_v1(): - # flatten tcc for applicable mi300 input - f = f"{workload_dir}/out/pmc_1/results_{fbase}.csv" - xcds = mi_gpu_specs.get_num_xcds( - mspec.gpu_arch, mspec.gpu_model, mspec.compute_partition - ) - df = flatten_tcc_info_across_xcds(f, xcds, int(mspec.l2_banks)) - df.to_csv(f, index=False) + # Overwrite column to ensure unique IDs. + combined_results["Dispatch_ID"] = range(0, len(combined_results)) + + combined_results.to_csv( + workload_dir + "/out/pmc_1/results_" + fbase + ".csv", index=False + ) if Path(f"{workload_dir}/out").exists(): # copy and remove out directory if needed @@ -1226,26 +1129,6 @@ def process_hip_trace_output(workload_dir: str, fbase: str) -> None: ) -def replace_timestamps(workload_dir: str) -> None: - ts_path = Path(workload_dir) / "timestamps.csv" - if not ts_path.is_file(): - return - - df_stamps = pd.read_csv(ts_path) - if "Start_Timestamp" in df_stamps.columns and "End_Timestamp" in df_stamps.columns: - # Update timestamps for all *.csv output files - for fname in glob.glob(f"{workload_dir}/*.csv"): - if Path(fname).name != "sysinfo.csv": - df_pmc_perf = pd.read_csv(fname) - df_pmc_perf["Start_Timestamp"] = df_stamps["Start_Timestamp"] - df_pmc_perf["End_Timestamp"] = df_stamps["End_Timestamp"] - df_pmc_perf.to_csv(fname, index=False) - else: - console_warning( - "Incomplete profiling data detected. Unable to update timestamps.\n" - ) - - @demarcate def gen_sysinfo( workload_name: str, @@ -1383,62 +1266,6 @@ def mibench(args: argparse.Namespace, mspec: Any) -> None: # noqa: ANN401 subprocess.run(my_args, check=True) -def flatten_tcc_info_across_xcds( - file: str, xcds: int, tcc_channel_per_xcd: int -) -> pd.DataFrame: - """ - Flatten TCC per channel counters across all XCDs in partition. - NB: This func highly depends on the default behavior of rocprofv2 on MI300, - which might be broken anytime in the future! - """ - df_orig = pd.read_csv(file) - - ### prepare column headers - tcc_cols_orig = [] - non_tcc_cols_orig = [] - for c in df_orig.columns.to_list(): - if "TCC" in c: - tcc_cols_orig.append(c) - else: - non_tcc_cols_orig.append(c) - - cols = non_tcc_cols_orig[:] - tcc_cols_in_group: dict[int, list[str]] = {i: [] for i in range(xcds)} - - for col in tcc_cols_orig: - for i in range(xcds): - # filter the channel index only - p = re.compile(r"\[(\d+)\]") - - # pick up the 1st element only - def replacement(match: re.Match[str]) -> str: - return f"[{int(match.group(1)) + i * tcc_channel_per_xcd}]" - - tcc_cols_in_group[i].append(re.sub(pattern=p, repl=replacement, string=col)) - - for i in range(xcds): - cols += tcc_cols_in_group[i] - - df = pd.DataFrame(columns=cols) - - ### Rearrange data with extended column names - for idx in range(0, len(df_orig.index), xcds): - # assume the front none TCC columns are the same for all XCCs - df_non_tcc = df_orig.iloc[idx].filter(regex=r"^(?!.*TCC).*$") - flatten_list = df_non_tcc.tolist() - - # extract all tcc from one dispatch - # NB: assuming default contiguous order might not be safe! - df_tcc_all = df_orig.iloc[idx : (idx + xcds)].filter(regex="TCC") - - for idx, row in df_tcc_all.iterrows(): - flatten_list += row.tolist() - # NB: It is not the best perf to append a row once a time - df.loc[len(df.index)] = flatten_list - - return df - - def get_submodules(package_name: str) -> list[str]: """List all submodules for a target package""" import importlib diff --git a/projects/rocprofiler-compute/tests/test_profile_general.py b/projects/rocprofiler-compute/tests/test_profile_general.py index f487ccd9ec..2b318465c6 100644 --- a/projects/rocprofiler-compute/tests/test_profile_general.py +++ b/projects/rocprofiler-compute/tests/test_profile_general.py @@ -108,7 +108,6 @@ ALL_CSVS_MI200 = sorted([ "pmc_perf_4.csv", "pmc_perf_5.csv", "sysinfo.csv", - "timestamps.csv", ]) ALL_CSVS_MI300 = sorted([ "SQC_DCACHE_INFLIGHT_LEVEL.csv", @@ -126,7 +125,6 @@ ALL_CSVS_MI300 = sorted([ "pmc_perf_4.csv", "pmc_perf_5.csv", "sysinfo.csv", - "timestamps.csv", ]) ALL_CSVS_MI350 = sorted([ "SQC_DCACHE_INFLIGHT_LEVEL.csv", @@ -155,13 +153,13 @@ ALL_CSVS_MI350 = sorted([ ROOF_ONLY_FILES = sorted([ "empirRoof_gpu-0_FP32.pdf", + "kernelName_legend.pdf", "pmc_perf.csv", "pmc_perf_0.csv", "pmc_perf_1.csv", "pmc_perf_2.csv", "roofline.csv", "sysinfo.csv", - "timestamps.csv", ]) PC_SAMPLING_HOST_TRAP_FILES = sorted([ @@ -364,18 +362,7 @@ def gpu_soc(): soc = gpu_soc() -os.environ["ROCPROF"] = "rocprofv3" - - -def using_v3(): - return "ROCPROF" not in os.environ.keys() or ( - "ROCPROF" in os.environ.keys() - and ( - os.environ["ROCPROF"].endswith("rocprofv3") - or os.environ["ROCPROF"] == "rocprofiler-sdk" - ) - ) - +os.environ["ROCPROF"] = "rocprofiler-sdk" Baseline_dir = str(Path("tests/workloads/vcopy/" + soc).resolve()) @@ -568,19 +555,11 @@ def test_path(binary_handler_profile_rocprof_compute): if soc == "MI100": assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 elif soc == "MI200": - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI200 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI200 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI200 elif "MI300" in soc: - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI300 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI300 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI300 elif "MI350" in soc: - assert sorted(list(file_dict.keys())) == sorted(ALL_CSVS_MI350) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI350 else: print(f"This test is not supported for {soc}") assert 0 @@ -628,15 +607,7 @@ def test_roof_kernel_names(binary_handler_profile_rocprof_compute): assert returncode == 0 file_dict = test_utils.check_csv_files(workload_dir, 1, num_kernels) - if soc == "MI100": - assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 - else: - expected_files = ( - [f for f in ROOF_ONLY_FILES if f != "timestamps.csv"] - if using_v3() - else ROOF_ONLY_FILES - ) + ["kernelName_legend.pdf"] - assert sorted(list(file_dict.keys())) == sorted(expected_files) + assert sorted(list(file_dict.keys())) == ROOF_ONLY_FILES validate( inspect.stack()[0][3], @@ -678,12 +649,7 @@ def test_roof_multiple_data_types(binary_handler_profile_rocprof_compute): assert os.path.exists(f"{workload_dir}/pmc_perf.csv") file_dict = test_utils.check_csv_files(workload_dir, 1, num_kernels) - expected_files = ( - [f for f in ROOF_ONLY_FILES if f != "timestamps.csv"] - if using_v3() - else ROOF_ONLY_FILES - ) + ["kernelName_legend.pdf"] - assert sorted(list(file_dict.keys())) == sorted(expected_files) + assert sorted(list(file_dict.keys())) == ROOF_ONLY_FILES else: pass finally: @@ -1200,19 +1166,11 @@ def test_device_filter(binary_handler_profile_rocprof_compute): if soc == "MI100": assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 elif soc == "MI200": - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI200 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI200 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI200 elif "MI300" in soc: - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI300 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI300 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI300 elif "MI350" in soc: - assert sorted(list(file_dict.keys())) == sorted(ALL_CSVS_MI350) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI350 else: print(f"Testing isn't supported yet for {soc}") assert 0 @@ -1238,19 +1196,11 @@ def test_kernel(binary_handler_profile_rocprof_compute): if soc == "MI100": assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 elif soc == "MI200": - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI200 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI200 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI200 elif "MI300" in soc: - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI300 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI300 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI300 elif "MI350" in soc: - assert sorted(list(file_dict.keys())) == sorted(ALL_CSVS_MI350) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI350 else: print(f"Testing isn't supported yet for {soc}") assert 0 @@ -1274,19 +1224,11 @@ def test_dispatch_0(binary_handler_profile_rocprof_compute): if soc == "MI100": assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 elif soc == "MI200": - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI200 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI200 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI200 elif "MI300" in soc: - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI300 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI300 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI300 elif "MI350" in soc: - assert sorted(list(file_dict.keys())) == sorted(ALL_CSVS_MI350) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI350 else: print(f"Testing isn't supported yet for {soc}") assert 0 @@ -1314,19 +1256,11 @@ def test_dispatch_0_1(binary_handler_profile_rocprof_compute): if soc == "MI100": assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 elif soc == "MI200": - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI200 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI200 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI200 elif "MI300" in soc: - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI300 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI300 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI300 elif "MI350" in soc: - assert sorted(list(file_dict.keys())) == sorted(ALL_CSVS_MI350) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI350 else: print(f"Testing isn't supported yet for {soc}") assert 0 @@ -1351,19 +1285,11 @@ def test_dispatch_2(binary_handler_profile_rocprof_compute): if soc == "MI100": assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 elif soc == "MI200": - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI200 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI200 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI200 elif "MI300" in soc: - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI300 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI300 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI300 elif "MI350" in soc: - assert sorted(list(file_dict.keys())) == sorted(ALL_CSVS_MI350) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI350 else: print(f"Testing isn't supported yet for {soc}") assert 0 @@ -1391,19 +1317,11 @@ def test_join_type_grid(binary_handler_profile_rocprof_compute): if soc == "MI100": assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 elif soc == "MI200": - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI200 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI200 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI200 elif "MI300" in soc: - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI300 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI300 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI300 elif "MI350" in soc: - assert sorted(list(file_dict.keys())) == sorted(ALL_CSVS_MI350) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI350 else: print(f"Testing isn't supported yet for {soc}") assert 0 @@ -1428,19 +1346,11 @@ def test_join_type_kernel(binary_handler_profile_rocprof_compute): if soc == "MI100": assert sorted(list(file_dict.keys())) == ALL_CSVS_MI100 elif soc == "MI200": - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI200 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI200 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI200 elif "MI300" in soc: - assert sorted(list(file_dict.keys())) == sorted( - [f for f in ALL_CSVS_MI300 if f != "timestamps.csv"] - if using_v3() - else ALL_CSVS_MI300 - ) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI300 elif "MI350" in soc: - assert sorted(list(file_dict.keys())) == sorted(ALL_CSVS_MI350) + assert sorted(list(file_dict.keys())) == ALL_CSVS_MI350 else: print(f"Testing isn't supported yet for {soc}") assert 0 @@ -1473,12 +1383,11 @@ def test_roof_sort_dispatches(binary_handler_profile_rocprof_compute): assert returncode == 0 file_dict = test_utils.check_csv_files(workload_dir, 1, num_kernels) - assert ( - sorted(list(file_dict.keys())) - == [f for f in ROOF_ONLY_FILES if f != "timestamps.csv"] - if using_v3() - else ROOF_ONLY_FILES - ) + + expected_files = ROOF_ONLY_FILES.copy() + expected_files.remove("kernelName_legend.pdf") + expected_files = sorted(expected_files) + assert sorted(list(file_dict.keys())) == expected_files validate( inspect.stack()[0][3], @@ -1508,12 +1417,10 @@ def test_roof_sort_kernels(binary_handler_profile_rocprof_compute): assert returncode == 0 file_dict = test_utils.check_csv_files(workload_dir, 1, num_kernels) - assert ( - sorted(list(file_dict.keys())) - == [f for f in ROOF_ONLY_FILES if f != "timestamps.csv"] - if using_v3() - else ROOF_ONLY_FILES - ) + expected_files = ROOF_ONLY_FILES.copy() + expected_files.remove("kernelName_legend.pdf") + expected_files = sorted(expected_files) + assert sorted(list(file_dict.keys())) == expected_files validate( inspect.stack()[0][3], @@ -1543,12 +1450,10 @@ def test_roof_mem_levels_vL1D(binary_handler_profile_rocprof_compute): assert returncode == 0 file_dict = test_utils.check_csv_files(workload_dir, 1, num_kernels) - assert ( - sorted(list(file_dict.keys())) - == [f for f in ROOF_ONLY_FILES if f != "timestamps.csv"] - if using_v3() - else ROOF_ONLY_FILES - ) + expected_files = ROOF_ONLY_FILES.copy() + expected_files.remove("kernelName_legend.pdf") + expected_files = sorted(expected_files) + assert sorted(list(file_dict.keys())) == expected_files validate( inspect.stack()[0][3], @@ -1578,12 +1483,10 @@ def test_roof_mem_levels_LDS(binary_handler_profile_rocprof_compute): assert returncode == 0 file_dict = test_utils.check_csv_files(workload_dir, 1, num_kernels) - assert ( - sorted(list(file_dict.keys())) - == [f for f in ROOF_ONLY_FILES if f != "timestamps.csv"] - if using_v3() - else ROOF_ONLY_FILES - ) + expected_files = ROOF_ONLY_FILES.copy() + expected_files.remove("kernelName_legend.pdf") + expected_files = sorted(expected_files) + assert sorted(list(file_dict.keys())) == expected_files validate( inspect.stack()[0][3], @@ -1873,10 +1776,6 @@ def test_pc_sampling_stochastic(binary_handler_profile_rocprof_compute): @pytest.mark.live_attach_detach def test_live_attach_detach_block(binary_handler_profile_rocprof_compute): - if not using_v3(): - assert True - return - options = ["--block", "3.1.1", "4.1.1", "5.1.1"] workload_dir = test_utils.get_output_dir() process_workload = subprocess.Popen(config["app_hip_dynamic_shared"]) @@ -1930,10 +1829,6 @@ def test_live_attach_detach_block(binary_handler_profile_rocprof_compute): def test_live_attach_detach_singlepath_launch_stats( binary_handler_profile_rocprof_compute, ): - if not using_v3(): - assert True - return - options = ["--set", "launch_stats"] workload_dir = test_utils.get_output_dir() process_workload = subprocess.Popen(config["app_hip_dynamic_shared"]) diff --git a/projects/rocprofiler-compute/tests/test_utils.py b/projects/rocprofiler-compute/tests/test_utils.py index 4d34623a11..866cdbf786 100644 --- a/projects/rocprofiler-compute/tests/test_utils.py +++ b/projects/rocprofiler-compute/tests/test_utils.py @@ -384,7 +384,7 @@ def test_detect_rocprof_env_rocprof_not_found(monkeypatch): rocprofiler_sdk_library_path = "/fake/path" # Set ROCPROF to 'rocprof' - monkeypatch.setenv("ROCPROF", "rocprof") + monkeypatch.setenv("ROCPROF", "rocprofv3") # shutil.which returns None for 'rocprof' monkeypatch.setattr("shutil.which", lambda cmd: None) # Track calls to console_warning and console_error @@ -403,7 +403,6 @@ def test_detect_rocprof_env_rocprof_not_found(monkeypatch): with pytest.raises(RuntimeError, match="console_error called"): utils_mod.detect_rocprof(DummyArgs()) - assert any("Unable to resolve path to rocprofv3 binary" in w for w in warnings) assert any( "Please verify installation or set ROCPROF environment variable" in e for e in errors @@ -452,10 +451,7 @@ def test_detect_rocprof_env_not_set(monkeypatch): rocprofiler_sdk_library_path = "/fake/path" monkeypatch.delenv("ROCPROF", raising=False) - monkeypatch.setattr( - "shutil.which", lambda cmd: "/usr/bin/rocprofv3" if cmd == "rocprofv3" else None - ) - monkeypatch.setattr("pathlib.Path.resolve", lambda self: self) + monkeypatch.setattr("pathlib.Path.exists", lambda _: True) logs = [] monkeypatch.setattr( "utils.utils.console_debug", lambda msg, *a, **k: logs.append(str(msg)) @@ -463,10 +459,10 @@ def test_detect_rocprof_env_not_set(monkeypatch): import utils.utils as utils_mod result = utils_mod.detect_rocprof(DummyArgs()) - assert result == "rocprofv3" + assert result == "rocprofiler-sdk" assert any( - "ROC Profiler: /usr/bin/rocprofv3" in log_entry - or "rocprof_cmd is rocprofv3" in log_entry + "rocprofiler_sdk_path is /fake/path" in log_entry + or "rocprof_cmd is rocprofiler-sdk" in log_entry for log_entry in logs ) @@ -2379,9 +2375,9 @@ def test_parse_text_file_not_found(): # ============================================================================= -def test_run_prof_success_v2(tmp_path, monkeypatch): +def test_run_prof_success_v3(tmp_path, monkeypatch): """ - Test run_prof with rocprofv2 successful execution. + Test run_prof with rocprofv3 successful execution. Args: tmp_path (Path): Temporary directory for test files. @@ -2395,7 +2391,13 @@ def test_run_prof_success_v2(tmp_path, monkeypatch): workload_dir = str(tmp_path / "workload") os.makedirs(workload_dir + "/out/pmc_1", exist_ok=True) - csv_content = "Dispatch_ID,GPU_ID,Kernel_Name\n0,0,test_kernel" + csv_content = ( + "Agent_Type,Node_Id,Wave_Front_Size,Correlation_Id,Dispatch_Id,Agent_Id,Queue_Id,Process_Id,Thread_Id," + "Grid_Size,Kernel_Id,Kernel_Name,Workgroup_Size,LDS_Block_Size," + "Scratch_Size,VGPR_Count,Accum_VGPR_Count,SGPR_Count,Start_Timestamp," + "End_Timestamp,Counter_Name,Counter_Value\n" + "GPU,0,0,0,0,0,0,0,0,0,0,test_kernel,0,0,0,0,0,0,0,1,SQ_WAVES,100" + ) with open(workload_dir + "/out/pmc_1/results_0.csv", "w") as f: f.write(csv_content) @@ -2408,12 +2410,10 @@ def test_run_prof_success_v2(tmp_path, monkeypatch): mspec = MockSpec() - monkeypatch.setattr("utils.utils.rocprof_cmd", "rocprofv2") + monkeypatch.setattr("utils.utils.rocprof_cmd", "rocprofv3") monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (True, "success") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: False) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) monkeypatch.setattr("utils.utils.console_debug", lambda *a, **k: None) monkeypatch.setattr("utils.utils.console_log", lambda *a, **k: None) monkeypatch.setattr( @@ -2458,8 +2458,6 @@ def test_run_prof_success_v3_csv(tmp_path, monkeypatch): monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (True, "success") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: True) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) monkeypatch.setattr("utils.utils.console_debug", lambda *a, **k: None) monkeypatch.setattr("utils.utils.console_log", lambda *a, **k: None) monkeypatch.setattr( @@ -2510,7 +2508,6 @@ def test_run_prof_success_rocprofiler_sdk(tmp_path, monkeypatch): monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (True, "success") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: True) monkeypatch.setattr("utils.utils.parse_text", lambda f: ["SQ_WAVES"]) monkeypatch.setattr("utils.utils.process_rocprofv3_output", lambda *a, **k: []) monkeypatch.setattr("utils.utils.console_debug", lambda *a, **k: None) @@ -2554,8 +2551,6 @@ def test_run_prof_with_yaml_config(tmp_path, monkeypatch): monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (True, "success") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: True) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) monkeypatch.setattr("utils.utils.process_rocprofv3_output", lambda *a, **k: []) monkeypatch.setattr("utils.utils.console_debug", lambda *a, **k: None) monkeypatch.setattr("utils.utils.console_log", lambda *a, **k: None) @@ -2597,8 +2592,6 @@ def test_run_prof_failure_subprocess(tmp_path, monkeypatch): monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (False, "error output") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: True) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) monkeypatch.setattr("utils.utils.console_debug", lambda *a, **k: None) monkeypatch.setattr("utils.utils.console_log", lambda *a, **k: None) @@ -2651,8 +2644,6 @@ def test_run_prof_mi300_environment_setup(tmp_path, monkeypatch): monkeypatch.setattr( "utils.utils.capture_subprocess_output", mock_capture_subprocess_output ) - monkeypatch.setattr("utils.utils.using_v3", lambda: True) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) monkeypatch.setattr("utils.utils.process_rocprofv3_output", lambda *a, **k: []) monkeypatch.setattr("utils.utils.console_debug", lambda *a, **k: None) monkeypatch.setattr("utils.utils.console_log", lambda *a, **k: None) @@ -2692,7 +2683,13 @@ def test_run_prof_timestamps_special_case(tmp_path, monkeypatch): mspec = MockSpec() - csv_content = "Dispatch_ID,Start_Timestamp,End_Timestamp\n0,100,200" + csv_content = ( + "Agent_Type,Node_Id,Wave_Front_Size,Correlation_Id,Dispatch_Id,Agent_Id,Queue_Id,Process_Id,Thread_Id," + "Grid_Size,Kernel_Id,Kernel_Name,Workgroup_Size,LDS_Block_Size," + "Scratch_Size,VGPR_Count,Accum_VGPR_Count,SGPR_Count,Start_Timestamp," + "End_Timestamp,Counter_Name,Counter_Value\n" + "GPU,0,0,0,0,0,0,0,0,0,0,test_kernel,0,0,0,0,0,0,0,1,SQ_WAVES,100" + ) with open(workload_dir + "/kernel_trace.csv", "w") as f: f.write(csv_content) @@ -2702,8 +2699,6 @@ def test_run_prof_timestamps_special_case(tmp_path, monkeypatch): monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (True, "success") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: True) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) monkeypatch.setattr( "utils.utils.process_rocprofv3_output", lambda *a, **k: csv_files ) @@ -2752,8 +2747,6 @@ def test_run_prof_no_results_files(tmp_path, monkeypatch): monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (True, "success") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: False) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) monkeypatch.setattr("glob.glob", lambda pattern: []) # No files found monkeypatch.setattr("utils.utils.console_debug", lambda *a, **k: None) monkeypatch.setattr("utils.utils.console_log", lambda *a, **k: None) @@ -2790,46 +2783,31 @@ def test_run_prof_header_standardization(tmp_path, monkeypatch): mspec = MockSpec() csv_content = ( - "KernelName,Index,grd,gpu-id,BeginNs,EndNs\ntest_kernel,0,64,0,100,200" + "Agent_Type,Node_Id,Wave_Front_Size,Correlation_Id,Dispatch_Id,Agent_Id,Queue_Id,Process_Id,Thread_Id," + "Grid_Size,Kernel_Id,Kernel_Name,Workgroup_Size,LDS_Block_Size," + "Scratch_Size,VGPR_Count,Accum_VGPR_Count,SGPR_Count,Start_Timestamp," + "End_Timestamp,Counter_Name,Counter_Value\n" + "GPU,0,0,0,0,0,0,0,0,0,0,test_kernel,0,0,0,0,0,0,0,1,SQ_WAVES,100" ) with open(workload_dir + "/out/pmc_1/results_test.csv", "w") as f: f.write(csv_content) - old_headers_df = pd.DataFrame({ - "KernelName": ["test_kernel"], - "Index": [0], - "grd": [64], - "gpu-id": [0], - "BeginNs": [100], - "EndNs": [200], - }) - - monkeypatch.setattr("utils.utils.rocprof_cmd", "rocprofv2") + monkeypatch.setattr("utils.utils.rocprof_cmd", "rocprofv3") monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (True, "success") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: False) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) monkeypatch.setattr( "glob.glob", lambda pattern: [workload_dir + "/out/pmc_1/results_test.csv"] ) monkeypatch.setattr("utils.utils.console_debug", lambda *a, **k: None) monkeypatch.setattr("utils.utils.console_log", lambda *a, **k: None) - read_calls = [] - - def mock_read_csv(path, **kwargs): - read_calls.append(path) - return old_headers_df.copy() - write_calls = [] def mock_to_csv(self, path, **kwargs): write_calls.append((path, self.columns.tolist())) - monkeypatch.setattr("pandas.read_csv", mock_read_csv) monkeypatch.setattr("pandas.DataFrame.to_csv", mock_to_csv) - monkeypatch.setattr("pandas.concat", lambda dfs, **k: old_headers_df.copy()) import utils.utils as utils_mod @@ -2837,9 +2815,8 @@ def test_run_prof_header_standardization(tmp_path, monkeypatch): final_headers = write_calls[-1][1] if write_calls else [] assert "Kernel_Name" in final_headers - assert "Dispatch_ID" in final_headers + assert "Dispatch_Id" in final_headers assert "Grid_Size" in final_headers - assert "GPU_ID" in final_headers assert "Start_Timestamp" in final_headers assert "End_Timestamp" in final_headers @@ -2868,28 +2845,12 @@ def test_run_prof_tcc_flattening_mi300(tmp_path, monkeypatch): mspec = MockSpec() - flatten_called = False - - def mock_flatten_tcc_info_across_xcds(file, xcds, l2_banks): - nonlocal flatten_called - flatten_called = True - return pd.DataFrame({ - "Dispatch_ID": [0], - "TCC_HIT[0]": [100], - "TCC_HIT[16]": [200], - }) - # Mock functions - monkeypatch.setattr("utils.utils.rocprof_cmd", "rocprofv2") + monkeypatch.setattr("utils.utils.rocprof_cmd", "rocprofv3") monkeypatch.setattr( "utils.utils.capture_subprocess_output", lambda *a, **k: (True, "success") ) - monkeypatch.setattr("utils.utils.using_v3", lambda: False) - monkeypatch.setattr("utils.utils.using_v1", lambda: False) - monkeypatch.setattr( - "utils.utils.flatten_tcc_info_across_xcds", mock_flatten_tcc_info_across_xcds - ) - monkeypatch.setattr("utils.utils.mi_gpu_specs.get_num_xcds", lambda *a: 2) + monkeypatch.setattr("utils.mi_gpu_spec.mi_gpu_specs.get_num_xcds", lambda *a: 2) monkeypatch.setattr( "glob.glob", lambda pattern: [workload_dir + "/results_test.csv"] ) @@ -2907,8 +2868,6 @@ def test_run_prof_tcc_flattening_mi300(tmp_path, monkeypatch): # Execute function utils_mod.run_prof(str(fname), ["--arg"], workload_dir, mspec, logging.INFO, "csv") - assert flatten_called - import utils.utils as utils_mod # noqa @@ -2934,7 +2893,6 @@ def test_run_prof_sdk_creates_new_env_copy(tmp_path, monkeypatch): workload_dir_str = str(tmp_path) monkeypatch.setattr("utils.utils.rocprof_cmd", "rocprofiler-sdk") - monkeypatch.setattr("utils.utils.using_v3", lambda: False) monkeypatch.setattr("utils.utils.process_rocprofv3_output", lambda *a, **k: []) capture_subprocess_called_with_env = None @@ -2957,10 +2915,12 @@ def test_run_prof_sdk_creates_new_env_copy(tmp_path, monkeypatch): "utils.utils.parse_text", lambda *a, **k: ["COUNTER1", "COUNTER2"] ) - mock_fname_path_obj = mock.Mock(spec=Path) + mock_fname_path_obj = mock.MagicMock(spec=Path) mock_fname_path_obj.stem = "counters" mock_fname_path_obj.name = "counters.txt" mock_fname_path_obj.with_suffix.return_value.exists.return_value = False + mock_fname_path_obj.__truediv__.return_value = mock.Mock(spec=Path) + mock_out_path_obj = mock.Mock(spec=Path) mock_out_path_obj.exists.return_value = False @@ -2999,6 +2959,7 @@ def test_run_prof_sdk_creates_new_env_copy(tmp_path, monkeypatch): monkeypatch.setattr("shutil.copyfile", lambda *a, **k: None) monkeypatch.setattr("shutil.rmtree", lambda *a, **k: None) monkeypatch.setattr("utils.utils.console_warning", lambda *a, **k: None) + monkeypatch.setattr("builtins.open", lambda *a, **k: io.StringIO("")) utils_mod.run_prof( fname_str, @@ -3030,7 +2991,7 @@ def test_run_prof_v3_sdk_and_cli_calls_trace_processing(tmp_path, monkeypatch): Line 5 (CLI): elif "--hip-trace" in options: process_hip_trace_output(...) """ - fname_str = str(tmp_path / "counters.txt") + fname_str = str(tmp_path) + "/counters.txt" Path(fname_str).touch() fbase_str = "counters" workload_dir_str = str(tmp_path) @@ -3041,7 +3002,7 @@ def test_run_prof_v3_sdk_and_cli_calls_trace_processing(tmp_path, monkeypatch): ) monkeypatch.setattr( "utils.utils.process_rocprofv3_output", - lambda *a, **k: [str(tmp_path / "results1.csv")], + lambda *a, **k: [str(tmp_path) + "/results1.csv"], ) hip_trace_called_with = None @@ -3096,15 +3057,13 @@ def test_run_prof_v3_sdk_and_cli_calls_trace_processing(tmp_path, monkeypatch): monkeypatch.setattr("shutil.copyfile", lambda *a, **k: None) monkeypatch.setattr("shutil.rmtree", lambda *a, **k: None) monkeypatch.setattr("builtins.open", lambda *a, **k: io.StringIO("")) - monkeypatch.setattr("utils.utils.flatten_tcc_info_across_xcds", lambda df, *a: df) - monkeypatch.setattr("utils.utils.mi_gpu_specs.get_num_xcds", lambda *a: 1) + monkeypatch.setattr("utils.mi_gpu_spec.mi_gpu_specs.get_num_xcds", lambda *a: 1) mspec = MockMSpec() loglevel = logging.INFO format_rocprof_output = True monkeypatch.setattr("utils.utils.rocprof_cmd", "rocprofiler-sdk") - monkeypatch.setattr("utils.utils.using_v3", lambda: True) profiler_options_sdk_hip = { "APP_CMD": "my_app", @@ -5458,306 +5417,6 @@ def test_mibench_console_log_called(tmp_path, monkeypatch): assert console_log_calls[0][1] == "No roofline data found. Generating..." -# ============================================================================= -# TESTS FOR flatten_tcc_info_across_xcds -# ============================================================================= -""" -Normal Functionality: - -Basic single XCD operation -Multiple XCD channel renumbering -Complex channel index patterns -Multiple dispatch handling -Edge Cases: - -Empty dataframes -Zero XCDs -Insufficient data -Large channel numbers -Column Handling: - -No TCC columns -TCC-only columns -Mixed TCC/non-TCC columns -Irregular TCC naming patterns -Error Conditions: - -File not found errors -Invalid input validation -Performance & Data Integrity: - -Large dataset handling -Data preservation validation -Regex pattern validation -""" - - -def test_flatten_tcc_info_across_xcds_zero_xcds(tmp_path): - """ - Test edge case with zero XCDs. - - Args: - tmp_path (Path): Temporary directory for test files. - - Returns: - None: Asserts function handles zero XCDs edge case by raising ValueError. - """ - columns = ["Kernel_Name", "TCC_HIT[0]"] - data = [["kernel1", 100]] - - df = pd.DataFrame(data, columns=columns) - csv_file = tmp_path / "test_zero_xcds.csv" - df.to_csv(csv_file, index=False) - - import utils.utils as utils_mod - - with pytest.raises(ValueError, match="range\\(\\) arg 3 must not be zero"): - utils_mod.flatten_tcc_info_across_xcds( - str(csv_file), xcds=0, tcc_channel_per_xcd=4 - ) - - -def test_flatten_tcc_info_across_xcds_insufficient_data(tmp_path): - """ - Test when there's insufficient data for the specified XCDs. - - Args: - tmp_path (Path): Temporary directory for test files. - - Returns: - None: Asserts function raises ValueError when trying - to process insufficient data. - """ - columns = ["Kernel_Name", "TCC_HIT[0]"] - data = [["kernel1", 100]] - - df = pd.DataFrame(data, columns=columns) - csv_file = tmp_path / "test_insufficient.csv" - df.to_csv(csv_file, index=False) - - import utils.utils as utils_mod - - with pytest.raises(ValueError, match="cannot set a row with mismatched columns"): - utils_mod.flatten_tcc_info_across_xcds( - str(csv_file), xcds=3, tcc_channel_per_xcd=4 - ) - - -def test_flatten_tcc_info_across_xcds_irregular_tcc_column_names(tmp_path): - """ - Test with irregular TCC column naming patterns. - - Args: - tmp_path (Path): Temporary directory for test files. - - Returns: - None: Asserts function handles various TCC column name - patterns but may fail with pandas Series ambiguity. - """ - columns = [ - "Kernel_Name", - "TCC_HIT_SPECIAL[0]", - "NOT_TCC_BUT_HAS_TCC", - "TCC_MISS[0]", - ] - data = [ - ["kernel1", 100, 50, 10], - ["kernel1", 200, 60, 20], - ] - - df = pd.DataFrame(data, columns=columns) - csv_file = tmp_path / "test_irregular.csv" - df.to_csv(csv_file, index=False) - - import utils.utils as utils_mod - - try: - result = utils_mod.flatten_tcc_info_across_xcds( - str(csv_file), xcds=2, tcc_channel_per_xcd=4 - ) - - assert len(result) == 1 - assert "TCC_HIT_SPECIAL[0]" in result.columns - assert "TCC_HIT_SPECIAL[4]" in result.columns - assert "TCC_MISS[0]" in result.columns - assert "TCC_MISS[4]" in result.columns - assert result.iloc[0]["NOT_TCC_BUT_HAS_TCC"] == 50 - - except ValueError as e: - if "The truth value of a Series is ambiguous" in str(e): - pytest.skip( - "Function has pandas Series ambiguity issue in boolean evaluation" - ) - else: - raise - - -def test_flatten_tcc_info_across_xcds_regex_pattern_validation(tmp_path): - """ - Test that regex pattern correctly identifies channel indices. - - Args: - tmp_path (Path): Temporary directory for test files. - - Returns: - None: Asserts regex pattern works for various channel - index formats but may fail with pandas Series ambiguity. - """ - columns = ["TCC_HIT[0]", "TCC_MISS[10]", "TCC_REQ[255]", "TCC_INVALID_NO_BRACKET"] - data = [ - [100, 200, 300, 400], # XCD 0 - [500, 600, 700, 800], # XCD 1 - ] - - df = pd.DataFrame(data, columns=columns) - csv_file = tmp_path / "test_regex.csv" - df.to_csv(csv_file, index=False) - - import utils.utils as utils_mod - - try: - result = utils_mod.flatten_tcc_info_across_xcds( - str(csv_file), xcds=2, tcc_channel_per_xcd=128 - ) - - assert len(result) == 1 - assert "TCC_HIT[0]" in result.columns - assert "TCC_HIT[128]" in result.columns # 0 + 1*128 - assert "TCC_MISS[10]" in result.columns - assert "TCC_MISS[138]" in result.columns # 10 + 1*128 - assert "TCC_REQ[255]" in result.columns - assert "TCC_REQ[383]" in result.columns # 255 + 1*128 - - assert result.iloc[0]["TCC_INVALID_NO_BRACKET"] == 400 - - except ValueError as e: - if "The truth value of a Series is ambiguous" in str(e): - pytest.skip( - "Function has pandas Series ambiguity issue in boolean evaluation" - ) - else: - raise - - -def test_flatten_tcc_info_across_xcds_edge_case_validation(tmp_path): - """ - Test edge cases and validation scenarios for - flatten_tcc_info_across_xcds. - - Args: - tmp_path (Path): Temporary directory for test files. - - Returns: - None: Asserts function behavior with various edge cases. - """ - import utils.utils as utils_mod - - columns = ["Kernel_Name", "TCC_HIT[0]"] - data = [["kernel1", 100]] - df = pd.DataFrame(data, columns=columns) - csv_file = tmp_path / "test_zero_xcds.csv" - df.to_csv(csv_file, index=False) - - with pytest.raises(ValueError): - utils_mod.flatten_tcc_info_across_xcds( - str(csv_file), xcds=0, tcc_channel_per_xcd=4 - ) - - try: - result = utils_mod.flatten_tcc_info_across_xcds( - str(csv_file), xcds=-1, tcc_channel_per_xcd=4 - ) - assert len(result) == 0 - except ValueError: - pass - - with pytest.raises(FileNotFoundError): - utils_mod.flatten_tcc_info_across_xcds( - "nonexistent.csv", xcds=2, tcc_channel_per_xcd=4 - ) - - -def test_flatten_tcc_info_across_xcds_pandas_filter_issue(tmp_path): - """ - Test demonstrating the pandas filter regex issue that causes Series ambiguity error. - - Args: - tmp_path (Path): Temporary directory for test files. - - Returns: - None: Documents the pandas boolean evaluation issue in the function. - """ - columns = ["Kernel_Name", "TCC_HIT[0]", "SQ_WAVES"] - data = [ - ["kernel1", 100, 50], - ["kernel1", 200, 60], - ] - - df = pd.DataFrame(data, columns=columns) - csv_file = tmp_path / "test_pandas_issue.csv" - df.to_csv(csv_file, index=False) - - import utils.utils as utils_mod - - try: - result = utils_mod.flatten_tcc_info_across_xcds( - str(csv_file), xcds=2, tcc_channel_per_xcd=4 - ) - - assert len(result) == 1 - assert "Kernel_Name" in result.columns - assert "TCC_HIT[0]" in result.columns - assert "TCC_HIT[4]" in result.columns - assert "SQ_WAVES" in result.columns - - except ValueError as e: - if "The truth value of a Series is ambiguous" in str(e): - pytest.skip( - "Known issue: pandas .filter() with regex causes " - "Series boolean ambiguity" - ) - else: - raise - - -def test_flatten_tcc_info_across_xcds_successful_cases_only(tmp_path): - """ - Test only the cases that are expected to work successfully. - - Args: - tmp_path (Path): Temporary directory for test files. - - Returns: - None: Asserts successful operation for known working scenarios. - """ - import utils.utils as utils_mod - - columns = ["TCC_HIT[0]", "TCC_MISS[0]"] - data = [ - [100, 10], # XCD 0 - [200, 20], # XCD 1 - ] - - df = pd.DataFrame(data, columns=columns) - csv_file = tmp_path / "test_simple_success.csv" - df.to_csv(csv_file, index=False) - - result = utils_mod.flatten_tcc_info_across_xcds( - str(csv_file), xcds=2, tcc_channel_per_xcd=4 - ) - - assert len(result) == 1 - assert "TCC_HIT[0]" in result.columns - assert "TCC_HIT[4]" in result.columns - assert "TCC_MISS[0]" in result.columns - assert "TCC_MISS[4]" in result.columns - assert result.iloc[0]["TCC_HIT[0]"] == 100 - assert result.iloc[0]["TCC_HIT[4]"] == 200 - - -# ============================================================================= -# TESTS FOR flatten_tcc_info_across_xcds -# ============================================================================= """ Normal Functionality: @@ -8517,173 +8176,6 @@ def test_add_counter_overwrite_existing(): updated_properties = ["P_UPDATED", "P_NEW"] # noqa -# ================================================================================= -# Test extract counter info extra config input yaml -# ================================================================================= - - -def test_extract_counter_info_returns_none_when_not_found(): - """ - Test that extract_counter_info_extra_config_input_yaml returns None - when the counter is not found or data structure is incomplete. - """ - data_empty = {} - assert ( - utils.extract_counter_info_extra_config_input_yaml(data_empty, "ANY_COUNTER") - is None - ) - - data_no_counters_key = {"rocprofiler-sdk": {}} - assert ( - utils.extract_counter_info_extra_config_input_yaml( - data_no_counters_key, "ANY_COUNTER" - ) - is None - ) - - data_empty_counters_list = {"rocprofiler-sdk": {"counters": []}} - assert ( - utils.extract_counter_info_extra_config_input_yaml( - data_empty_counters_list, "ANY_COUNTER" - ) - is None - ) - - data_with_other_counters = { - "rocprofiler-sdk": { - "counters": [ - {"name": "EXISTING_COUNTER_1", "value": "val1"}, - {"name": "EXISTING_COUNTER_2", "value": "val2"}, - ] - } - } - assert ( - utils.extract_counter_info_extra_config_input_yaml( - data_with_other_counters, "NON_EXISTENT_COUNTER" - ) - is None - ) - - data_with_malformed_counter = { - "rocprofiler-sdk": { - "counters": [ - {"value": "val1"}, # No 'name' key - {"name": "EXISTING_COUNTER_2", "value": "val2"}, - ] - } - } - assert ( - utils.extract_counter_info_extra_config_input_yaml( - data_with_malformed_counter, "EXISTING_COUNTER_1" - ) - is None - ) - assert ( - utils.extract_counter_info_extra_config_input_yaml( - data_with_malformed_counter, "EXISTING_COUNTER_2" - ) - is not None - ) - - -def test_extract_counter_info_returns_counter_when_found(): - """ - Test that extract_counter_info_extra_config_input_yaml returns the correct - counter dictionary when the counter is found. - """ - counter1_details = { - "name": "MY_COUNTER_1", - "description": "Desc 1", - "expression": "expr1", - } - counter2_details = { - "name": "MY_COUNTER_2", - "description": "Desc 2", - "expression": "expr2", - } - data = { - "rocprofiler-sdk": { - "counters-schema-version": 1, - "counters": [ - counter1_details, - counter2_details, - ], - } - } - - extracted_counter1 = utils.extract_counter_info_extra_config_input_yaml( - data, "MY_COUNTER_1" - ) - assert extracted_counter1 is not None - assert extracted_counter1 == counter1_details - - extracted_counter2 = utils.extract_counter_info_extra_config_input_yaml( - data, "MY_COUNTER_2" - ) - assert extracted_counter2 is not None - assert extracted_counter2 == counter2_details - - -# ============================================================================= -# test using_v1 function -# ============================================================================= - - -def test_using_v1_rocprof_set_and_ends_with_rocprof_returns_true(): - """ - Covers the case where "ROCPROF" is in os.environ and its value ends with "rocprof". - This makes the entire expression True, so the function returns True. - """ - with mock.patch.dict( - os.environ, {"ROCPROF": "/opt/rocm/bin/rocprof", "OTHER_VAR": "value"} - ): - assert utils.using_v1() is True - - -def test_using_v1_rocprof_set_but_not_ends_with_rocprof_returns_false(): - """ - Covers the case where "ROCPROF" is in os.environ, but its value does - NOT end with "rocprof". - - The second part of the 'and' (os.environ["ROCPROF"].endswith("rocprof")) is False. - So the function returns False. - """ - with mock.patch.dict( - os.environ, {"ROCPROF": "/opt/rocm/bin/rocprofv2", "OTHER_VAR": "value"} - ): - assert utils.using_v1() is False - - with mock.patch.dict( - os.environ, {"ROCPROF": "some/path/to/rocprof_tool", "OTHER_VAR": "value"} - ): - assert utils.using_v1() is False - - -def test_using_v1_rocprof_not_in_environ_returns_false(): - """ - Covers the case where "ROCPROF" is NOT in os.environ. - The first part of the 'and' ("ROCPROF" in os.environ.keys()) is False. - Due to short-circuiting, the second part is not evaluated. - So the function returns False. - """ - current_env = os.environ.copy() - if "ROCPROF" in current_env: - del current_env["ROCPROF"] - - with mock.patch.dict(os.environ, current_env, clear=True): - assert utils.using_v1() is False - - -def test_using_v1_rocprof_is_empty_string_returns_false(): - """ - Covers the case where "ROCPROF" is in os.environ but is an empty string. - The second part (os.environ["ROCPROF"].endswith("rocprof")) will be False. - So the function returns False. - """ - with mock.patch.dict(os.environ, {"ROCPROF": "", "OTHER_VAR": "value"}): - assert utils.using_v1() is False - - # ============================================================================= # additional test detect_rocprof console error # ============================================================================= @@ -8732,27 +8224,6 @@ class MockArgs: # noqa return self.__dict__ == other.__dict__ -def test_store_app_cmd_sets_global_rocprof_args(): - """ - Tests that store_app_cmd correctly assigns the passed 'args' - object to the global 'rocprof_args'. - """ - sample_args_object = MockArgs( - rocprofiler_sdk_library_path="/path/to/sdk", - input_file="input.txt", - some_other_option=True, - ) - - if hasattr(utils, "rocprof_args"): - utils.rocprof_args = None - else: - pass - utils.store_app_cmd(sample_args_object) - assert utils.rocprof_args is sample_args_object, ( - "Global rocprof_args should be the same object as the passed args" - ) - - # ============================================================================= # additional tests for v3_counter_csv_to_v2_csv function # ============================================================================= @@ -9112,155 +8583,6 @@ def test_pc_sampling_prof_empty_appcmd( mock_console_error.assert_not_called() -# ============================================================================= -# test replace_timestamps function -# ============================================================================= - - -def create_dummy_csv(filepath, data_dict): - df = pd.DataFrame(data_dict) - df.to_csv(filepath, index=False) - - -@mock.patch("utils.utils.console_warning") -def test_replace_timestamps_no_timestamps_csv_returns_early( - mock_console_warning, tmp_path -): - """ - Edge Case: timestamps.csv does not exist in workload_dir. - The function should return early. - Covers: if not path(workload_dir, "timestamps.csv").is_file(): return - """ - workload_dir = str(tmp_path) - - utils.replace_timestamps(workload_dir) - - # Since there's no timestamps.csv, function should return early - # and console_warning should not be called - mock_console_warning.assert_not_called() - - -@mock.patch("utils.utils.console_warning") -@mock.patch("glob.glob") -def test_replace_timestamps_timestamps_csv_missing_columns_warns( - mock_glob, mock_console_warning, tmp_path -): - """ - Edge Case: timestamps.csv exists but is missing - 'Start_Timestamp' or 'End_Timestamp'. - The function should call console_warning. - Covers: else: console_warning(...) - """ - workload_dir = str(tmp_path) - timestamps_csv_path_str = os.path.join(workload_dir, "timestamps.csv") - - # Create the actual CSV file with missing columns - create_dummy_csv(timestamps_csv_path_str, {"Some_Other_Column": [123]}) - - utils.replace_timestamps(workload_dir) - - # Verify console_warning was called - mock_console_warning.assert_called_once_with( - "Incomplete profiling data detected. Unable to update timestamps.\n" - ) - # Verify glob wasn't called (since we return early due to missing columns) - mock_glob.assert_not_called() - - -@mock.patch("utils.utils.console_warning") -@mock.patch("glob.glob") -def test_replace_timestamps_updates_other_csvs_skips_sysinfo( - mock_glob, mock_console_warning, tmp_path -): - """ - Edge Case: timestamps.csv is valid. Other CSVs exist, including sysinfo.csv. - Only non-sysinfo.csv files should be updated. - Covers: for fname in glob.glob(...): if path(fname).name != "sysinfo.csv": ... - """ - workload_dir = str(tmp_path) - timestamps_csv_path_str = os.path.join(workload_dir, "timestamps.csv") - data_csv_path_str = os.path.join(workload_dir, "data.csv") - sysinfo_csv_path_str = os.path.join(workload_dir, "sysinfo.csv") - - new_start_ts = [1000, 2000] - new_end_ts = [1500, 2500] - create_dummy_csv( - timestamps_csv_path_str, - {"Start_Timestamp": new_start_ts, "End_Timestamp": new_end_ts}, - ) - - create_dummy_csv( - data_csv_path_str, - {"Kernel_Name": ["A", "B"], "Start_Timestamp": [1, 2], "End_Timestamp": [3, 4]}, - ) - create_dummy_csv( - sysinfo_csv_path_str, - {"Info": ["CPU", "MEM"], "Start_Timestamp": [5, 6], "End_Timestamp": [7, 8]}, - ) - - # Mock glob to return the CSV files we created - mock_glob.return_value = [ - data_csv_path_str, - sysinfo_csv_path_str, - timestamps_csv_path_str, - ] - - utils.replace_timestamps(workload_dir) - - mock_console_warning.assert_not_called() - - # Verify data.csv was updated with new timestamps - df_data_updated = pd.read_csv(data_csv_path_str) - pd.testing.assert_series_equal( - df_data_updated["Start_Timestamp"], - pd.Series(new_start_ts, name="Start_Timestamp"), - ) - pd.testing.assert_series_equal( - df_data_updated["End_Timestamp"], pd.Series(new_end_ts, name="End_Timestamp") - ) - - # Verify sysinfo.csv was NOT updated (timestamps should remain original) - df_sysinfo_original = pd.read_csv(sysinfo_csv_path_str) - assert list(df_sysinfo_original["Start_Timestamp"]) == [5, 6] - assert list(df_sysinfo_original["End_Timestamp"]) == [7, 8] - - -@mock.patch("utils.utils.console_warning") -@mock.patch("glob.glob") -def test_replace_timestamps_no_other_csvs_to_update( - mock_glob, mock_console_warning, tmp_path -): - """ - Edge Case: timestamps.csv is valid, but no other *.csv files - (or only sysinfo.csv) exist. - The loop for updating files should not do anything or not run. - Covers: The for loop not iterating if glob returns empty or only sysinfo. - """ - workload_dir = str(tmp_path) - timestamps_csv_path_str = os.path.join(workload_dir, "timestamps.csv") - sysinfo_csv_path_str = os.path.join(workload_dir, "sysinfo.csv") - - create_dummy_csv( - timestamps_csv_path_str, {"Start_Timestamp": [100], "End_Timestamp": [200]} - ) - create_dummy_csv( - sysinfo_csv_path_str, - {"Info": ["CPU"], "Start_Timestamp": [5], "End_Timestamp": [7]}, - ) - - # Mock glob to return only timestamps.csv and sysinfo.csv - mock_glob.return_value = [timestamps_csv_path_str, sysinfo_csv_path_str] - - utils.replace_timestamps(workload_dir) - - mock_console_warning.assert_not_called() - - # Verify sysinfo.csv was NOT updated (timestamps should remain original) - df_sysinfo_original = pd.read_csv(sysinfo_csv_path_str) - assert list(df_sysinfo_original["Start_Timestamp"]) == [5] - assert list(df_sysinfo_original["End_Timestamp"]) == [7] - - def test_set_parser(): from utils.utils import parse_sets_yaml