Files
TheRock/rocm-systems/projects/rocprofiler-sdk/tests/rocprofv3/kernel-trace-duration/validate.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

273 lines
9.6 KiB
Python

#!/usr/bin/env python3
# MIT License
#
# Copyright (c) 2024-2025 Advanced Micro Devices, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import sys
import pytest
def extract_json_kernel_records(json_root):
"""Extract kernel dispatch records from JSON output."""
assert "rocprofiler-sdk-tool" in json_root, "missing rocprofiler-sdk-tool in JSON"
tool = json_root["rocprofiler-sdk-tool"]
if isinstance(tool, list) and len(tool) > 0:
tool = tool[0]
assert "buffer_records" in tool, "missing buffer_records in JSON"
buffer_records = tool["buffer_records"]
for key in ("kernel_dispatch", "kernel_trace", "kernel_dispatch_trace"):
if (
key in buffer_records
and isinstance(buffer_records[key], list)
and len(buffer_records[key]) > 0
):
return buffer_records[key]
raise AssertionError(
"no kernel dispatch records found in JSON buffer_records keys="
f"{list(buffer_records.keys())}"
)
def _as_int(value, *, field="value"):
assert value is not None, f"missing {field}"
try:
return int(value)
except Exception as exc:
raise AssertionError(
f"failed to parse int for {field}: {value!r} ({exc})"
) from exc
def _extract_dispatch_id_from_json_record(record):
"""
Prefer dispatch_info.dispatch_id.
Fallback to correlation_id.internal (or correlation_id if scalar).
Return int.
"""
dispatch_info = record.get("dispatch_info", {})
dispatch_id = None
if isinstance(dispatch_info, dict):
dispatch_id = dispatch_info.get("dispatch_id", None)
if dispatch_id is None:
correlation_id = record.get("correlation_id", {})
if isinstance(correlation_id, dict):
dispatch_id = correlation_id.get("internal", None)
else:
dispatch_id = correlation_id
return _as_int(dispatch_id, field="dispatch_id/correlation_id")
def build_json_duration_map(records):
"""
Build map:
key(int dispatch_id) -> (start, end, duration)
"""
result = {}
for record in records:
dispatch_id = _extract_dispatch_id_from_json_record(record)
start = _as_int(record.get("start_timestamp"), field="start_timestamp")
end = _as_int(record.get("end_timestamp"), field="end_timestamp")
assert start > 0 and end > 0, f"invalid timestamps start={start} end={end}"
assert end >= start, f"end before start: start={start} end={end}"
assert (
dispatch_id not in result
), f"duplicate dispatch_id {dispatch_id} found in JSON records"
result[dispatch_id] = (start, end, end - start)
assert len(result) > 0, "no kernel records extracted from JSON"
return result
def load_kernel_rows_via_rocpd(db_path):
"""
Use rocpd Python API and reuse rocpd.csv kernel query logic directly.
Returns list[dict].
"""
try:
import rocpd
from rocpd import csv as rocpd_csv
from rocpd import output_config as rocpd_output_config
except Exception as exc:
raise AssertionError(
"failed to import rocpd python modules. "
f"Ensure PYTHONPATH is set for rocprofiler-sdk build tree. ({exc})"
) from exc
data = rocpd.connect([db_path])
config = rocpd_output_config.output_config()
query = rocpd_csv.get_kernel_csv_query(config)
cursor = rocpd.execute(data, query)
columns = [desc[0] for desc in cursor.description]
rows = [dict(zip(columns, row)) for row in cursor.fetchall()]
assert len(rows) > 0, f"no rows returned from kernel query in db: {db_path}"
return rows
_DB_ROW_COLUMN_NAMES = ["dispatch_id", "start_timestamp", "end_timestamp", "duration"]
def _extract_values_from_db_row(row):
"""
Extract (dispatch_id, start, end, duration) from a DB row dict.
Builds a single case-insensitive lookup table for the row, then fetches
each required column. Column names reflect the output of
rocpd_csv.get_kernel_csv_query(), which aliases and extends the raw
rocpd_kernel_dispatch schema columns.
"""
lowered_row = {k.lower(): v for k, v in row.items()}
dispatch_id, start, end, duration = [
_as_int(lowered_row.get(name), field=name) for name in _DB_ROW_COLUMN_NAMES
]
return dispatch_id, start, end, duration
def test_rocpd_kernel_trace_duration(json_data, db_path):
"""
Validate that kernel trace Duration exists on the rocpd CSV path and matches JSON.
Strategy:
- JSON and rocpd DB are generated from the SAME rocprofv3 execution
(ROCPROF_OUTPUT_FORMAT=json,rocpd)
- Read kernel records from JSON
- Read kernel rows from rocpd DB using rocpd Python API
- Reuse rocpd.csv.get_kernel_csv_query(config) so test and CSV path stay aligned
- Enforce:
* Duration == End_Timestamp - Start_Timestamp
* Start/End/Duration EXACTLY match JSON for each dispatch_id
* All DB rows match JSON rows
"""
db_rows = load_kernel_rows_via_rocpd(db_path)
json_records = extract_json_kernel_records(json_data)
json_map = build_json_duration_map(json_records)
total_count = len(db_rows)
matched_count = 0
mismatches = []
missing_in_json = []
for row in db_rows:
dispatch_id, start, end, duration = _extract_values_from_db_row(row)
assert (
start > 0 and end > 0
), f"invalid DB timestamps: start={start} end={end} dispatch_id={dispatch_id}"
assert (
end >= start
), f"DB end before start: start={start} end={end} dispatch_id={dispatch_id}"
assert duration == (end - start), (
f"DB duration mismatch: duration={duration} != end-start={end - start} "
f"dispatch_id={dispatch_id}"
)
if dispatch_id not in json_map:
missing_in_json.append(dispatch_id)
continue
matched_count += 1
json_start, json_end, json_duration = json_map[dispatch_id]
start_diff = start - json_start
end_diff = end - json_end
duration_diff = duration - json_duration
if start_diff != 0 or end_diff != 0 or duration_diff != 0:
mismatches.append(
{
"dispatch_id": dispatch_id,
"db_start": start,
"json_start": json_start,
"start_diff": start_diff,
"db_end": end,
"json_end": json_end,
"end_diff": end_diff,
"db_duration": duration,
"json_duration": json_duration,
"duration_diff": duration_diff,
}
)
if missing_in_json:
sample = missing_in_json[:10]
raise AssertionError(
"Some DB kernel rows had dispatch_id not present in JSON records. "
"Since JSON and rocpd come from the same execution, dispatch IDs should align.\n"
f"Missing count: {len(missing_in_json)}/{total_count}\n"
f"Sample missing dispatch_ids: {sample}"
)
if mismatches:
lines = [
"",
"TIMESTAMP/DURATION MISMATCHES DETECTED",
f"{'Dispatch':<12} {'StartDiff':<12} {'EndDiff':<12} {'DurDiff':<12}",
"=" * 56,
]
for item in mismatches[:10]:
lines.append(
f"{item['dispatch_id']:<12} "
f"{item['start_diff']:<12} "
f"{item['end_diff']:<12} "
f"{item['duration_diff']:<12}"
)
if len(mismatches) > 10:
lines.append(f"... and {len(mismatches) - 10} more mismatches")
first = mismatches[0]
detail = (
f"\n\nExample mismatch for dispatch {first['dispatch_id']}:\n"
f" DB: start={first['db_start']}, end={first['db_end']}, "
f"duration={first['db_duration']}\n"
f" JSON: start={first['json_start']}, end={first['json_end']}, "
f"duration={first['json_duration']}\n"
f" Diff: start={first['start_diff']}, end={first['end_diff']}, "
f"duration={first['duration_diff']}\n"
f"Total mismatches: {len(mismatches)}/{total_count}\n"
"NOTE: Since JSON and rocpd come from the same execution, these should be identical."
)
raise AssertionError("\n".join(lines) + detail)
assert matched_count > 0, "No DB rows matched JSON records"
assert (
matched_count == total_count
), f"Only {matched_count}/{total_count} DB rows matched JSON"
if __name__ == "__main__":
rc = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(rc)