Remove rocprofv1/v2 in favour of rocprofiler-sdk (#673)

* Set default rocprof interface as rocprofiler-sdk

* Remove rocrprofv1 and rocprofv2 interfaces

* Remove deprecation notice for rocprof v1/v2/v3 interfaces
  * Make rocprofiler-sdk the default interface and make rocprofv3 interface opt-in using ROCPROF=rocprofv3

* Add deprecation notice for rocprofv3
This commit is contained in:
vedithal-amd
2025-09-24 10:37:01 -04:00
committed by GitHub
orang tua 7df02745eb
melakukan bd7a1de879
16 mengubah file dengan 235 tambahan dan 2365 penghapusan
@@ -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"])
@@ -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