[rocprofiler-compute] Add database output format to analyze mode (#748)

Analysis data dump

* Add `--output-format` and `--output-name` option to analyze mode

* Remove `--output` and `-save-dfs` option to analyze mode

* Add documentation on `rocpd` output format and analysis database file

* Create sqlite3 database using object relation mapping (ORM) provided
  by sqlalchemy library

* Fix metrics config to remove metrics marked as `null`, fix `Unit` header, add
  missing `title`

* Add test cases to ensure analysis data dump work
This commit is contained in:
vedithal-amd
2025-08-26 14:15:05 -04:00
committed by GitHub
parent 09cfa97156
commit 323d06c79c
41 changed files with 1130 additions and 117 deletions
@@ -147,7 +147,8 @@ def test_L1_cache_counters(
base = Path(test_utils.get_output_dir())
for app_name in app_names:
workload_dir = str(base / app_name)
workload_dir = f"{base}/{app_name}"
workload_dir_output = f"{base}_{app_name}"
# 1. profile the app
return_code = binary_handler_profile_rocprof_compute(
@@ -167,15 +168,17 @@ def test_L1_cache_counters(
workload_dir,
"-b",
"16.3",
"--save-dfs",
workload_dir,
"--output-format",
"csv",
"--output-name",
workload_dir_output,
])
assert return_code == 0
# 3. save results in local
# FIXME: customize file name to avoid hardcode
csv_path = workload_dir + "/16.3_vL1D_cache_access_metrics.csv"
csv_path = workload_dir_output + "/16.3_vL1D_cache_access_metrics.csv"
data = load_metrics(csv_path)
for metric in metrics:
@@ -185,6 +188,7 @@ def test_L1_cache_counters(
# 4. clean local output
test_utils.clean_output_dir(config["cleanup"], workload_dir)
test_utils.clean_output_dir(config["cleanup"], workload_dir_output)
test_utils.clean_output_dir(config["cleanup"], base)
# 5. check results are expected
@@ -25,6 +25,7 @@
import os
import shutil
from pathlib import Path
from unittest.mock import Mock
import pandas as pd
@@ -608,14 +609,16 @@ def test_decimal_3(binary_handler_analyze_rocprof_compute):
@pytest.mark.misc
def test_save_dfs(binary_handler_analyze_rocprof_compute):
output_path = "tests/workloads/vcopy/saved_analysis"
output_path = test_utils.get_output_dir()
for dir in indirs:
workload_dir = test_utils.setup_workload_dir(dir)
code = binary_handler_analyze_rocprof_compute([
"analyze",
"--path",
workload_dir,
"--save-dfs",
"--output-format",
"csv",
"--output-name",
output_path,
])
assert code == 0
@@ -627,6 +630,7 @@ def test_save_dfs(binary_handler_analyze_rocprof_compute):
shutil.rmtree(output_path)
test_utils.clean_output_dir(config["cleanup"], workload_dir)
test_utils.clean_output_dir(config["cleanup"], output_path)
@pytest.mark.col
@@ -860,7 +864,6 @@ def test_dependency_MI100(binary_handler_analyze_rocprof_compute):
def test_parser_utility_functions():
"""Test parser utility functions edge cases"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
@@ -969,7 +972,6 @@ def test_parser_utility_functions():
def test_parser_error_handling():
"""Test parser error handling paths"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
@@ -1009,7 +1011,6 @@ def test_missing_file_handling(binary_handler_analyze_rocprof_compute):
def test_ast_transformer_edge_cases():
"""Simplified test focusing on the actual code paths"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
@@ -1051,7 +1052,6 @@ def test_ast_transformer_edge_cases():
def test_analyze_with_debug_mode(binary_handler_analyze_rocprof_compute):
"""Test analyze to cover debug paths in eval_metric - using direct function call"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
@@ -1138,7 +1138,6 @@ def test_filter_combinations_coverage(binary_handler_analyze_rocprof_compute):
def test_apply_filters_direct():
"""Test apply_filters function directly to cover filter branches"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
@@ -1213,7 +1212,6 @@ def test_missing_files_scenarios(binary_handler_analyze_rocprof_compute):
def test_pc_sampling_basic_coverage():
"""Test PC sampling functions with minimal data"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
@@ -1245,7 +1243,6 @@ def test_pc_sampling_basic_coverage():
def test_build_dfs_edge_cases():
"""Test build_dfs and gen_counter_list with various configurations"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
@@ -1275,7 +1272,6 @@ def test_build_dfs_edge_cases():
def test_update_functions_coverage():
"""Test update_denom_string and update_normUnit_string branches"""
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
@@ -766,6 +766,32 @@ def test_roof_rocpd(binary_handler_profile_rocprof_compute):
test_utils.clean_output_dir(config["cleanup"], workload_dir)
@pytest.mark.misc
def test_analyze_rocpd(
binary_handler_profile_rocprof_compute, binary_handler_analyze_rocprof_compute
):
workload_dir = test_utils.get_output_dir()
options = ["--device", "0", "--format-rocprof-output", "rocpd"]
binary_handler_profile_rocprof_compute(config, workload_dir, options, roof=True)
db_name = "test"
code = binary_handler_analyze_rocprof_compute([
"analyze",
"--output-format",
"db",
"--output-name",
f"{db_name}",
"--path",
workload_dir,
])
assert code == 0
assert os.path.isfile(f"{db_name}.db")
# Remove test.db
os.remove(f"{db_name}.db")
test_utils.clean_output_dir(config["cleanup"], workload_dir)
@pytest.mark.misc
def test_roofline_workload_dir_not_set_error():
"""