[rocpd] Refactor to use python to convert rocpd to CSV + add CSV tests + remove old cpp implementation (#159)

* Write agent info to CSV

* Write kernel to CSV

* Write memory copy to CSV

* Write memory allocation to CSV

* Write hip api to CSV

* Write hsa api to CSV

* Write marker api to CSV

* Write counters to CSV

* Write scratch memory to CSV

* Write rccl api to CSV

* Write rocdecode api to CSV

* Write rocjpeg api to CSV

* Remove info_process joins

* Format agent id

* Compose full file name is sql writer function

* Add missing fields to kernel traces csv

* Rename vgpr_count to arch_vgpr_count

* Fix kernel name

* Skip empty query results

* Format csv.py

* Delete c++ CSV writer

* Add CSV header comparison test

* Fix comment spacing in csv.py

* Change ALLOC to ALLOCATE in memory allocation writer

* Do not append trace to agent info file name

* Revert changes for VGPR_Count

* Fix csv validation test

* Add sorting by guid

* Use EXISTS to check query results are not empty

* Merge API-specific queries

* Optimize regions query

* Column name mapping for agent info

* Pass config to sql writer

* Move agent id string building to a separate function

* add titled_headers argument

* Remove titled-columns argument

* Improvements for regions csv

* fix CSV validation test

* improve CSV validation test

* remove roctxMarkA from csv validation test

* fix capability field titles in agent info

* remove filter.py from query as that is still experimental

* Remove some aliases, now that query will auto-title the column headers

---------

Co-authored-by: Aleksei Tumakaev <atumakae@amd.com>
Co-authored-by: Young Hui <young.hui@amd.com>
Co-authored-by: Young Hui - AMD <145490163+yhuiYH@users.noreply.github.com>
Этот коммит содержится в:
systems-assistant[bot]
2025-09-19 10:15:57 -04:00
коммит произвёл GitHub
родитель 9def133275
Коммит 3f001b0305
10 изменённых файлов: 628 добавлений и 986 удалений
+382 -2
Просмотреть файл
@@ -2,7 +2,7 @@
###############################################################################
# MIT License
#
# Copyright (c) 2023 Advanced Micro Devices, Inc.
# Copyright (c) 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
@@ -23,14 +23,394 @@
# THE SOFTWARE.
###############################################################################
import os
import re
from .importer import RocpdImportData
from .query import export_sqlite_query
from .time_window import apply_time_window
from . import output_config
from . import libpyrocpd
def write_sql_query_to_csv(
connection: RocpdImportData,
config,
query,
filename="",
postfix="trace",
) -> None:
"""Write the contents of a SQL query to a CSV file in the specified output path."""
query_not_empty = f"""
SELECT EXISTS (
{query}
)
"""
# just return if the result is empty
if not connection.execute(query_not_empty).fetchone()[0]:
return
# call query module to export to csv
file_prefix = config.output_file + "_" if config.output_file else ""
file_postfix = "_" + postfix if postfix else ""
export_path = os.path.join(
config.output_path, f"{file_prefix}{filename}{file_postfix}.csv"
)
kwargs = {"title_columns": True}
export_sqlite_query(
connection, query, export_format="csv", export_path=export_path, **kwargs
)
def write_agent_info_csv(importData, config) -> None:
# Define mapping of output column name to JSON key
json_keys = [
"node_id",
"logical_node_id",
"cpu_cores_count",
"simd_count",
"cpu_core_id_base",
"simd_id_base",
"max_waves_per_simd",
"lds_size_in_kb",
"gds_size_in_kb",
"num_gws",
"wave_front_size",
"num_xcc",
"cu_count",
"array_count",
"num_shader_banks",
"simd_arrays_per_engine",
"cu_per_simd_array",
"simd_per_cu",
"max_slots_scratch_cu",
"gfx_target_version",
"vendor_id",
"device_id",
"location_id",
"domain",
"drm_render_minor",
"num_sdma_engines",
"num_sdma_xgmi_engines",
"num_sdma_queues_per_engine",
"num_cp_queues",
"max_engine_clk_ccompute",
"max_engine_clk_fcompute",
"sdma_fw_version.uCodeSDMA AS Sdma_Fw_Version",
"fw_version.uCode AS Fw_Version",
"cu_per_engine",
"max_waves_per_cu",
"workgroup_max_size",
"family_id",
"grid_max_size",
"local_mem_size",
"hive_id",
"gpu_id",
"workgroup_max_dim.x AS Workgroup_Max_Dim_X",
"workgroup_max_dim.y AS Workgroup_Max_Dim_Y",
"workgroup_max_dim.z AS Workgroup_Max_Dim_Z",
"grid_max_dim.x AS Grid_Max_Dim_X",
"grid_max_dim.y AS Grid_Max_Dim_Y",
"grid_max_dim.z AS Grid_Max_Dim_Z",
"vendor_name",
"product_name",
]
# Build SELECT clause for json_extract columns
select_json = []
for column in json_keys:
match = re.match(r"(.+?)\s+AS\s+(.+)", column, re.IGNORECASE)
column_name, column_alias = (
(match.group(1), match.group(2)) if match else (column, column)
)
select_json.append(f"json_extract(extdata, '$.{column_name}') AS {column_alias}")
capabilities = [
"HotPluggable",
"HSAMMUPresent",
"SharedWithGraphics",
"QueueSizePowerOfTwo",
"QueueSize32bit",
"QueueIdleEvent",
"VALimit",
"WatchPointsSupported",
"WatchPointsTotalBits",
"DoorbellType",
"AQLQueueDoubleMap",
"DebugTrapSupported",
"WaveLaunchTrapOverrideSupported",
"WaveLaunchModeSupported",
"PreciseMemoryOperationsSupported",
"DEPRECATED_SRAM_EDCSupport",
"Mem_EDCSupport",
"RASEventNotify",
"ASICRevision",
"SRAM_EDCSupport",
"SVMAPISupported",
"CoherentHostAccess",
"DebugSupportedFirmware",
"PreciseALUOperationsSupported",
"PerQueueResetSupported",
]
# Build SELECT clause for Capability columns
select_capability = []
for capability in capabilities:
select_capability.append(
f"json_extract(extdata, '$.capability.{capability}') AS Cap_{capability}"
)
# Add non-JSON columns
fixed_keys = [
"guid",
"type AS Agent_Type",
"name",
"model_name",
]
# to keep the right order
select_clause = (
fixed_keys[:1]
+ select_json[:2]
+ fixed_keys[1:2]
+ select_json[2:33]
+ select_capability
+ select_json[33:47]
+ fixed_keys[2:3]
+ select_json[47:]
+ fixed_keys[3:4]
)
select_clause = ",\n ".join(select_clause)
query = f"""
SELECT
{select_clause}
FROM "rocpd_info_agent"
"""
write_sql_query_to_csv(importData, config, query, "agent_info", "")
def build_agent_id_string(agent_index_value, prefix=""):
agent_prefix = prefix + "_" if prefix else ""
if agent_index_value == libpyrocpd.agent_indexing.node: # absolute
return f"'Agent ' || {agent_prefix}agent_abs_index"
elif (
agent_index_value == libpyrocpd.agent_indexing.logical_node
): # relative (default)
return f"'Agent ' || {agent_prefix}agent_log_index"
elif (
agent_index_value == libpyrocpd.agent_indexing.logical_node_type
): # type-relative
return f"{agent_prefix}agent_type || ' ' || {agent_prefix}agent_type_index"
else:
return ""
def write_kernel_csv(importData, config) -> None:
agent_id = build_agent_id_string(config.agent_index_value)
if config.kernel_rename:
kernel_name = "region"
else:
kernel_name = "name"
select_columns = [
"guid",
"'KERNEL_DISPATCH' AS Kind",
f"{agent_id} AS Agent_Id",
"queue_id",
"stream_id",
"tid AS Thread_Id",
"dispatch_id",
"kernel_Id",
f"{kernel_name} AS Kernel_Name",
"stack_id AS Correlation_Id",
"start AS Start_Timestamp",
"end AS End_Timestamp",
"lds_size AS Lds_Block_Size",
"scratch_size",
"vgpr_count",
"accum_vgpr_count",
"sgpr_count",
"workgroup_x AS Workgroup_Size_X",
"workgroup_y AS Workgroup_Size_Y",
"workgroup_z AS Workgroup_Size_Z",
"grid_x AS Grid_Size_X",
"grid_y AS Grid_Size_Y",
"grid_z AS Grid_Size_Z",
]
aliased_headers = []
for column in select_columns:
aliased_headers.append(column)
select_clause = ",\n".join(aliased_headers)
query = f"""
SELECT
{select_clause}
FROM "kernels"
ORDER BY
guid ASC, start ASC, end DESC
"""
write_sql_query_to_csv(importData, config, query, "kernel")
def write_memory_copy_csv(importData, config) -> None:
src_agent_id = build_agent_id_string(config.agent_index_value, "src")
dst_agent_id = build_agent_id_string(config.agent_index_value, "dst")
query = f"""
SELECT
guid,
'MEMORY_COPY' AS Kind,
name AS Direction,
stream_id,
{src_agent_id} AS Source_Agent_Id,
{dst_agent_id} AS Destination_Agent_Id,
stack_id AS Correlation_Id,
start AS Start_Timestamp,
end AS End_Timestamp
FROM "memory_copies"
ORDER BY
guid ASC, start ASC, end DESC
"""
write_sql_query_to_csv(importData, config, query, "memory_copy")
def write_memory_allocation_csv(importData, config) -> None:
agent_id = build_agent_id_string(config.agent_index_value)
query = f"""
SELECT
guid,
'MEMORY_ALLOCATION' AS Kind,
CASE
WHEN type = 'ALLOC'
THEN 'MEMORY_ALLOCATION_ALLOCATE'
ELSE 'MEMORY_ALLOCATION_' || type
END AS Operation,
CASE
WHEN type != 'FREE'
THEN {agent_id}
ELSE '"'
END AS Agent_Id,
size AS Allocation_Size,
'0x' || printf('%016X', address) AS Address,
stack_id AS Correlation_Id,
start AS Start_Timestamp,
end AS End_Timestamp
FROM "memory_allocations"
ORDER BY
guid ASC, start ASC, end DESC
"""
write_sql_query_to_csv(importData, config, query, "memory_allocation")
def write_counters_csv(importData, config) -> None:
agent_id = build_agent_id_string(config.agent_index_value)
select_columns = [
"guid",
"stack_id AS Correlation_Id",
"dispatch_id",
f"{agent_id} AS Agent_Id",
"queue_id",
"pid AS Process_Id",
"tid AS Thread_Id",
"grid_size",
"kernel_id",
"kernel_name",
"workgroup_size",
"lds_block_size AS Lds_Block_Size",
"scratch_size",
"vgpr_count",
"accum_vgpr_count",
"sgpr_count",
"counter_name",
"value AS Counter_Value",
"start AS Start_Timestamp",
"end AS End_Timestamp",
]
aliased_headers = []
for column in select_columns:
aliased_headers.append(column)
select_clause = ",\n".join(aliased_headers)
query = f"""
SELECT
{select_clause}
FROM "counters_collection"
ORDER BY
guid ASC, start ASC, end DESC
"""
write_sql_query_to_csv(importData, config, query, "counter_collection")
def write_scratch_memory_csv(importData, config) -> None:
agent_id = build_agent_id_string(config.agent_index_value)
query = f"""
SELECT
guid,
'SCRATCH_MEMORY' AS Kind,
'SCRATCH_MEMORY_' || operation AS Operation,
{agent_id} AS Agent_Id,
queue_id,
tid AS Thread_Id,
alloc_flags,
start AS Start_Timestamp,
end AS End_Timestamp
FROM "scratch_memory"
ORDER BY
guid ASC, start ASC, end DESC
"""
write_sql_query_to_csv(importData, config, query, "scratch_memory")
def write_region_csv(importData, config) -> None:
query = """
SELECT
guid,
category AS Domain,
name AS Function,
pid AS Process_Id,
tid AS Thread_Id,
stack_id AS Correlation_Id,
start AS Start_Timestamp,
end AS End_Timestamp
FROM "regions"
ORDER BY
guid ASC, start ASC, end DESC
"""
write_sql_query_to_csv(importData, config, query, "regions")
def write_csv(importData, config):
return libpyrocpd.write_csv(importData, config)
write_agent_info_csv(importData, config)
write_counters_csv(importData, config)
write_kernel_csv(importData, config)
write_memory_allocation_csv(importData, config)
write_memory_copy_csv(importData, config)
write_region_csv(importData, config)
write_scratch_memory_csv(importData, config)
def execute(input, config=None, window_args=None, **kwargs):
-107
Просмотреть файл
@@ -23,7 +23,6 @@
#include "libpyrocpd.hpp"
#include "lib/output/format_path.hpp"
#include "lib/python/rocpd/source/common.hpp"
#include "lib/python/rocpd/source/csv.hpp"
#include "lib/python/rocpd/source/functions.hpp"
#include "lib/python/rocpd/source/interop.hpp"
#include "lib/python/rocpd/source/otf2.hpp"
@@ -505,112 +504,6 @@ PYBIND11_MODULE(libpyrocpd, pyrocpd)
},
"Write pftrace output file from rocpd SQLite3 database");
pyrocpd.def(
"write_csv",
[](rocpd::RocpdImportData& data, const rocprofiler::tool::output_config& output_cfg) {
auto sqlgen_csv = common::simple_timer{
fmt::format("CSV generation from {} SQL database(s)", data.size())};
if(data.empty()) return;
auto csv_manager = rocpd::output::CsvManager{output_cfg};
for(auto obj : {data.connection})
{
auto* conn = rocpd::interop::get_connection(std::move(obj));
auto nodes = rocpd::read<rocpd::types::node>(conn);
for(const auto& nitr : nodes)
{
auto agents = rocpd::read<rocpd::types::agent>(
conn, fmt::format("WHERE guid = '{}' AND nid = {}", nitr.guid, nitr.id));
auto processes = rocpd::read<rocpd::types::process>(
conn, fmt::format("WHERE guid = '{}' AND nid = {}", nitr.guid, nitr.id));
for(const auto& pitr : processes)
{
ROCP_FATAL_IF(pitr.nid != nitr.id || pitr.guid != nitr.guid)
<< fmt::format("Found process with a mismatched nid/guid. process: "
"{}/{} vs. node: {}/{}",
pitr.nid,
pitr.guid,
nitr.id,
nitr.guid);
auto _sqlgen_csv = common::simple_timer{fmt::format(
"CSV generation from SQL for process {} (total)", pitr.pid)};
auto select_guid_nid_pid = [&nitr, &pitr](std::string_view tbl,
std::string_view
where_extra_condition = {}) {
return fmt::format(
"SELECT * FROM {} WHERE guid = '{}' AND nid = {} AND pid = {} {}",
tbl,
pitr.guid,
nitr.id,
pitr.pid,
where_extra_condition);
};
rocpd::output::write_agent_info_csv(csv_manager, agents);
constexpr auto region_order_by = "start ASC, end DESC";
auto kernels = rocpd::sql_generator<rocpd::types::kernel_dispatch>{
conn, select_guid_nid_pid("kernels"), region_order_by};
auto memory_copies = rocpd::sql_generator<rocpd::types::memory_copies>{
conn, select_guid_nid_pid("memory_copies"), region_order_by};
auto memory_allocations =
rocpd::sql_generator<rocpd::types::memory_allocation>{
conn, select_guid_nid_pid("memory_allocations"), region_order_by};
auto hip_api_calls = rocpd::sql_generator<rocpd::types::region>{
conn,
select_guid_nid_pid("regions", "AND category LIKE 'HIP_%'"),
region_order_by};
auto hsa_api_calls = rocpd::sql_generator<rocpd::types::region>{
conn,
select_guid_nid_pid("regions", "AND category LIKE 'HSA_%'"),
region_order_by};
auto marker_api_calls = rocpd::sql_generator<rocpd::types::region>{
conn,
select_guid_nid_pid("regions_and_samples",
"AND category LIKE 'MARKER_%'"),
region_order_by};
auto counters_calls = rocpd::sql_generator<rocpd::types::counter>{
conn, select_guid_nid_pid("counters_collection"), region_order_by};
auto scratch_memory_calls =
rocpd::sql_generator<rocpd::types::scratch_memory>{
conn, select_guid_nid_pid("scratch_memory"), region_order_by};
auto rccl_calls = rocpd::sql_generator<rocpd::types::region>{
conn,
select_guid_nid_pid("regions", "AND category LIKE 'RCCL_%'"),
region_order_by};
auto rocdecode_calls = rocpd::sql_generator<rocpd::types::region>{
conn,
select_guid_nid_pid("regions", "AND category LIKE 'ROCDECODE_%'"),
region_order_by};
auto rocjpeg_calls = rocpd::sql_generator<rocpd::types::region>{
conn,
select_guid_nid_pid("regions", "AND category LIKE 'ROCJPEG_%'"),
region_order_by};
rocpd::output::write_csvs(csv_manager,
kernels,
memory_copies,
memory_allocations,
hip_api_calls,
hsa_api_calls,
marker_api_calls,
counters_calls,
scratch_memory_calls,
rccl_calls,
rocdecode_calls,
rocjpeg_calls);
}
}
}
},
"Write trace data to CSV files");
pyrocpd.def(
"write_otf2",
[](rocpd::RocpdImportData& data, const tool::output_config& output_cfg) {
+16 -1
Просмотреть файл
@@ -42,6 +42,7 @@ def export_sqlite_query(
export_format: Optional[str] = None,
export_path: Optional[str] = None,
dashboard_template_path: Optional[str] = None,
**kwargs: Optional[dict],
) -> Optional[str]:
"""
Execute a SQLite query and print it to console.
@@ -99,7 +100,21 @@ def export_sqlite_query(
# 3) Export based on format
if export_format == "csv":
df.to_csv(export_path, index=False)
import csv
cols = [f"{itr}" for itr in df.columns.tolist()]
col_names = (
[f"{itr}".title() for itr in cols]
if kwargs.get("title_columns", True)
else cols[:]
)
df.to_csv(
export_path,
index=False,
columns=cols,
header=col_names,
quoting=csv.QUOTE_NONNUMERIC,
)
elif export_format == "html":
write_export(df.to_html(index=False))
+3 -12
Просмотреть файл
@@ -2,19 +2,10 @@
# libpyrocpd python binding sources
#
set(libpyrocpd_source_headers
common.hpp
functions.hpp
interop.hpp
perfetto.hpp
csv.hpp
otf2.hpp
sql_generator.hpp
pysqlite_Connection.h
types.hpp)
set(libpyrocpd_source_headers common.hpp functions.hpp interop.hpp perfetto.hpp otf2.hpp
sql_generator.hpp pysqlite_Connection.h types.hpp)
set(libpyrocpd_source_sources csv.cpp functions.cpp interop.cpp otf2.cpp perfetto.cpp
types.cpp)
set(libpyrocpd_source_sources functions.cpp interop.cpp otf2.cpp perfetto.cpp types.cpp)
foreach(_PYTHON_VERSION ${ROCPROFILER_PYTHON_VERSIONS})
rocprofiler_rocpd_python_bindings_target_sources(
-743
Просмотреть файл
@@ -1,743 +0,0 @@
// MIT License
//
// Copyright (c) 2025 Advanced Micro Devices, Inc. All Rights Reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "lib/python/rocpd/source/csv.hpp"
#include "lib/common/defines.hpp"
#include "lib/common/hasher.hpp"
#include "lib/common/mpl.hpp"
#include "lib/output/csv.hpp"
#include "lib/output/csv_output_file.hpp"
#include "lib/output/generator.hpp"
#include "lib/output/metadata.hpp"
#include "lib/output/node_info.hpp"
#include "lib/output/output_config.hpp"
#include "lib/output/output_stream.hpp"
#include "lib/output/sql/common.hpp"
#include "lib/output/stream_info.hpp"
#include "lib/rocprofiler-sdk-tool/config.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <fmt/format.h>
#include <atomic>
#include <filesystem>
#include <future>
#include <map>
#include <mutex>
#include <string>
#include <vector>
namespace fs = std::filesystem;
namespace
{
const std::string STATS_HEADER = "\"Name\",\"Calls\",\"TotalDurationNs\","
"\"AverageNs\",\"Percentage\",\"MinNs\",\"MaxNs\",\"StdDev\"";
const std::string API_TRACE_HEADER =
"\"Guid\",\"Domain\",\"Function\",\"Process_Id\",\"Thread_Id\","
"\"Correlation_Id\",\"Start_Timestamp\",\"End_Timestamp\"";
} // namespace
namespace rocpd
{
namespace output
{
CsvManager::CsvManager(rocprofiler::tool::output_config output_cfg)
: config{std::move(output_cfg)}
{
if(!ensure_output_directory())
{
ROCP_ERROR << "Failed to create csv output directory: " << config.output_path;
return;
}
this->csv_configs = {
{CsvType::KERNEL_DISPATCH,
{"kernel_trace.csv",
"\"Guid\",\"Kind\",\"Agent_Id\",\"Queue_Id\",\"Stream_Id\",\"Thread_Id\",\"Dispatch_Id\","
"\"Kernel_Id\",\"Kernel_Name\",\"Correlation_Id\",\"Start_Timestamp\",\"End_Timestamp\","
"\"LDS_Block_Size\",\"Scratch_Size\",\"VGPR_Count\",\"Accum_VGPR_Count\",\"SGPR_Count\","
"\"Workgroup_Size_X\",\"Workgroup_Size_Y\",\"Workgroup_Size_Z\","
"\"Grid_Size_X\",\"Grid_Size_Y\",\"Grid_Size_Z\""}},
{CsvType::MEMORY_COPY,
{"memory_copy_trace.csv",
"\"Guid\",\"Kind\",\"Direction\",\"Stream_Id\",\"Source_Agent_Id\","
"\"Destination_Agent_"
"Id\","
"\"Correlation_Id\",\"Start_Timestamp\",\"End_Timestamp\""}},
{CsvType::MEMORY_ALLOCATION,
{"memory_allocation_trace.csv",
"\"Guid\",\"Kind\",\"Operation\",\"Agent_Id\",\"Allocation_Size\","
"\"Address\","
"\"Correlation_Id\",\"Start_Timestamp\",\"End_Timestamp\""}},
{CsvType::SCRATCH_MEMORY,
{"scratch_memory_trace.csv",
"\"Kind\",\"Operation\",\"Agent_Id\",\"Queue_Id\",\"Thread_Id\","
"\"Alloc_Flags\",\"Start_"
"Timestamp\",\"End_Timestamp\""}},
{CsvType::HIP_API, {"hip_api_trace.csv", API_TRACE_HEADER}},
{CsvType::HSA_CSV_API, {"hsa_api_trace.csv", API_TRACE_HEADER}},
{CsvType::MARKER, {"marker_api_trace.csv", API_TRACE_HEADER}},
{CsvType::RCCL_API, {"rccl_api_trace.csv", API_TRACE_HEADER}},
{CsvType::ROCDECODE_API, {"rocdecode_api_trace.csv", API_TRACE_HEADER}},
{CsvType::ROCJPEG_API, {"rocjpeg_api_trace.csv", API_TRACE_HEADER}},
{CsvType::COUNTER,
{"counter_collection.csv",
"\"Pid\",\"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\","
"\"Counter_Name\",\"Counter_Value\",\"Start_Timestamp\",\"End_"
"Timestamp\""}},
};
}
bool
CsvManager::ensure_output_directory() const
{
try
{
fs::create_directories(config.output_path);
return true;
} catch(const std::exception& e)
{
ROCP_ERROR << "Failed to create directory: " << e.what();
return false;
}
}
CsvManager::~CsvManager()
{
for(auto& [type, stream] : streams)
{
if(stream.is_open())
{
stream.flush();
stream.close();
}
}
}
std::ofstream&
CsvManager::get_stream(CsvType type)
{
return streams[type];
}
bool
CsvManager::has_stream(CsvType type) const
{
return streams.count(type) != 0u && streams.at(type).is_open();
}
bool
CsvManager::initialize_csv_file(CsvType type)
{
if(has_stream(type)) return true;
if(csv_configs.find(type) == csv_configs.end())
{
ROCP_ERROR << "No CSV configuration found for type: " << static_cast<int>(type);
return false;
}
const auto& cfg = csv_configs[type];
fs::path output_dir = config.output_path;
fs::path filename =
config.output_file.empty() ? cfg.filename : config.output_file + "_" + cfg.filename;
file_paths[type] = (output_dir / filename).string();
auto& path = file_paths[type];
auto& stream = streams[type];
stream.open(path, std::ios::out);
if(!stream.is_open())
{
ROCP_ERROR << "Failed to open CSV output file: " << path;
return false;
}
stream << cfg.header << '\n';
return true;
}
template <typename DataType>
bool
has_any_data(const rocprofiler::tool::generator<DataType>& data_gen)
{
for(auto ditr : data_gen)
{
auto gen = data_gen.get(ditr);
if(begin(gen) != end(gen))
{
return true;
}
}
return false;
}
template <typename DataType, typename Processor>
void
process_data_to_csv(CsvManager& csv_manager,
CsvType csv_type,
const rocprofiler::tool::generator<DataType>& data_gen,
Processor process_func)
{
if(!has_any_data(data_gen)) return;
if(!csv_manager.initialize_csv_file(csv_type)) return;
for(auto ditr : data_gen)
{
auto gen = data_gen.get(ditr);
for(auto it = begin(gen); it != end(gen); ++it)
{
process_func(csv_manager, csv_type, *it);
}
}
}
void
write_kernel_csv(
CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::kernel_dispatch>& kernel_dispatch_gen)
{
process_data_to_csv(
csv_manager,
CsvType::KERNEL_DISPATCH,
kernel_dispatch_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::kernel_dispatch& kernel) {
std::string kernel_identifier =
(cm.config.kernel_rename && !kernel.region.empty()) ? kernel.region : kernel.name;
std::string agent_identifier = create_agent_index(cm.config.agent_index_value,
kernel.agent_abs_index,
kernel.agent_log_index,
kernel.agent_type_index,
std::string_view(kernel.agent_type))
.as_string();
cm.write_line(type,
fmt::format("\"{}\"", kernel.guid),
fmt::format("\"{}\"", "KERNEL_DISPATCH"),
fmt::format("\"{}\"", agent_identifier),
kernel.queue_id,
kernel.stream_id,
kernel.tid,
kernel.dispatch_id,
kernel.kernel_id,
fmt::format("\"{}\"", kernel_identifier),
kernel.stack_id,
kernel.start,
kernel.end,
kernel.lds_size,
kernel.scratch_size,
kernel.vgpr_count,
kernel.accum_vgpr_count,
kernel.sgpr_count,
kernel.workgroup_size.x,
kernel.workgroup_size.y,
kernel.workgroup_size.z,
kernel.grid_size.x,
kernel.grid_size.y,
kernel.grid_size.z);
});
}
void
write_memory_copy_csv(
CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::memory_copies>& memory_copies_gen)
{
process_data_to_csv(csv_manager,
CsvType::MEMORY_COPY,
memory_copies_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::memory_copies& mcopy) {
std::string src_agent_identifier =
create_agent_index(cm.config.agent_index_value,
mcopy.src_agent_abs_index,
mcopy.src_agent_log_index,
mcopy.src_agent_type_index,
std::string_view(mcopy.src_agent_type))
.as_string();
std::string dst_agent_identifier =
create_agent_index(cm.config.agent_index_value,
mcopy.dst_agent_abs_index,
mcopy.dst_agent_log_index,
mcopy.dst_agent_type_index,
std::string_view(mcopy.dst_agent_type))
.as_string();
cm.write_line(type,
fmt::format("\"{}\"", mcopy.guid),
fmt::format("\"{}\"", "MEMORY_COPY"),
fmt::format("\"{}\"", mcopy.name),
mcopy.stream_id,
fmt::format("\"{}\"", src_agent_identifier),
fmt::format("\"{}\"", dst_agent_identifier),
mcopy.stack_id,
mcopy.start,
mcopy.end);
});
}
void
write_memory_allocation_csv(
CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::memory_allocation>& memory_alloc_gen)
{
process_data_to_csv(
csv_manager,
CsvType::MEMORY_ALLOCATION,
memory_alloc_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::memory_allocation& malloc) {
std::string normalized_type = malloc.type;
if(normalized_type == "ALLOC")
{
normalized_type = "ALLOCATE";
}
std::string operation = fmt::format("MEMORY_ALLOCATION_{}", normalized_type);
std::string agent_identifier = create_agent_index(cm.config.agent_index_value,
malloc.agent_abs_index,
malloc.agent_log_index,
malloc.agent_type_index,
std::string_view(malloc.agent_type))
.as_string();
std::string agent_id =
operation != "MEMORY_ALLOCATION_FREE" ? agent_identifier : "\"\"";
std::string address = fmt::format("\"0x{:016x}\"", malloc.address);
cm.write_line(type,
fmt::format("\"{}\"", malloc.guid),
fmt::format("\"{}\"", "MEMORY_ALLOCATION"),
fmt::format("\"{}\"", operation),
fmt::format("\"{}\"", agent_id),
malloc.size,
address,
malloc.stack_id,
malloc.start,
malloc.end);
});
}
void
write_scratch_memory_csv(
CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::scratch_memory>& scratch_memory_gen)
{
process_data_to_csv(
csv_manager,
CsvType::SCRATCH_MEMORY,
scratch_memory_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::scratch_memory& scratch_mem) {
std::string agent_identifier =
create_agent_index(cm.config.agent_index_value,
scratch_mem.agent_abs_index,
scratch_mem.agent_log_index,
scratch_mem.agent_type_index,
std::string_view(scratch_mem.agent_type))
.as_string();
cm.write_line(type,
fmt::format("\"{}\"", "SCRATCH_MEMORY"),
fmt::format("\"SCRATCH_MEMORY_{}\"", scratch_mem.operation),
fmt::format("\"{}\"", agent_identifier),
scratch_mem.queue_id,
scratch_mem.tid,
scratch_mem.alloc_flags,
scratch_mem.start,
scratch_mem.end);
});
}
void
write_hip_api_csv(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::region>& hip_api_gen)
{
process_data_to_csv(csv_manager,
CsvType::HIP_API,
hip_api_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::region& api) {
// Skip entries that are not HIP API calls
if(api.category.find("HIP_") != 0) return;
cm.write_line(type,
fmt::format("\"{}\"", api.guid),
fmt::format("\"{}\"", api.category),
fmt::format("\"{}\"", api.name),
api.pid,
api.tid,
api.stack_id,
api.start,
api.end);
});
}
void
write_hsa_api_csv(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::region>& hsa_api_gen)
{
process_data_to_csv(csv_manager,
CsvType::HSA_CSV_API,
hsa_api_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::region& api) {
// Skip entries that are not HSA API calls
if(api.category.find("HSA_") != 0) return;
cm.write_line(type,
fmt::format("\"{}\"", api.guid),
fmt::format("\"{}\"", api.category),
fmt::format("\"{}\"", api.name),
api.pid,
api.tid,
api.stack_id,
api.start,
api.end);
});
}
void
write_marker_api_csv(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::region>& marker_api_gen)
{
namespace tool = ::rocprofiler::tool;
if(marker_api_gen.empty()) return;
using marker_csv_encoder = tool::csv::csv_encoder<8>;
auto ofs = tool::csv_output_file{csv_manager.config,
domain_type::MARKER,
marker_csv_encoder{},
{"Guid",
"Domain",
"Function",
"Process_Id",
"Thread_Id",
"Correlation_Id",
"Start_Timestamp",
"End_Timestamp"}};
// write samples first and ignore the timestamp ordering w.r.t. regions for now
for(auto ditr : marker_api_gen)
{
for(const auto& record : marker_api_gen.get(ditr))
{
auto row_ss = std::stringstream{};
auto _name = record.name;
if(record.has_extdata())
{
if(auto _extdata = record.get_extdata(); !_extdata.message.empty())
_name = _extdata.message;
}
marker_csv_encoder::write_row(row_ss,
record.guid,
record.category,
_name,
record.pid,
record.tid,
record.stack_id,
record.start,
record.end);
ofs << row_ss.str();
}
}
}
void
write_rccl_api_csv(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::region>& rccl_api_gen)
{
process_data_to_csv(csv_manager,
CsvType::RCCL_API,
rccl_api_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::region& api) {
if(api.category.find("RCCL_") != 0) return;
cm.write_line(type,
fmt::format("\"{}\"", api.guid),
fmt::format("\"{}\"", api.category),
fmt::format("\"{}\"", api.name),
api.pid,
api.tid,
api.stack_id,
api.start,
api.end);
});
}
void
write_rocdecode_api_csv(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::region>& rocdecode_api_gen)
{
process_data_to_csv(csv_manager,
CsvType::ROCDECODE_API,
rocdecode_api_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::region& api) {
if(api.category.find("ROCDECODE_") != 0) return;
cm.write_line(type,
fmt::format("\"{}\"", api.guid),
fmt::format("\"{}\"", api.category),
fmt::format("\"{}\"", api.name),
api.pid,
api.tid,
api.stack_id,
api.start,
api.end);
});
}
void
write_rocjpeg_api_csv(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::region>& rocjpeg_api_gen)
{
process_data_to_csv(csv_manager,
CsvType::ROCJPEG_API,
rocjpeg_api_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::region& api) {
if(api.category.find("ROCJPEG_") != 0) return;
cm.write_line(type,
fmt::format("\"{}\"", api.guid),
fmt::format("\"{}\"", api.category),
fmt::format("\"{}\"", api.name),
api.pid,
api.tid,
api.stack_id,
api.start,
api.end);
});
}
void
write_agent_info_csv(CsvManager& csv_manager, const std::vector<rocpd::types::agent>& agents)
{
if(agents.empty()) return;
namespace tool = ::rocprofiler::tool;
using agent_info_csv_encoder = tool::csv::csv_encoder<54>;
auto ofs = tool::csv_output_file{csv_manager.config,
"agent_info",
agent_info_csv_encoder{},
{"Guid",
"Node_Id",
"Logical_Node_Id",
"Agent_Type",
"Cpu_Cores_Count",
"Simd_Count",
"Cpu_Core_Id_Base",
"Simd_Id_Base",
"Max_Waves_Per_Simd",
"Lds_Size_In_Kb",
"Gds_Size_In_Kb",
"Num_Gws",
"Wave_Front_Size",
"Num_Xcc",
"Cu_Count",
"Array_Count",
"Num_Shader_Banks",
"Simd_Arrays_Per_Engine",
"Cu_Per_Simd_Array",
"Simd_Per_Cu",
"Max_Slots_Scratch_Cu",
"Gfx_Target_Version",
"Vendor_Id",
"Device_Id",
"Location_Id",
"Domain",
"Drm_Render_Minor",
"Num_Sdma_Engines",
"Num_Sdma_Xgmi_Engines",
"Num_Sdma_Queues_Per_Engine",
"Num_Cp_Queues",
"Max_Engine_Clk_Ccompute",
"Max_Engine_Clk_Fcompute",
"Sdma_Fw_Version",
"Fw_Version",
"Capability",
"Cu_Per_Engine",
"Max_Waves_Per_Cu",
"Family_Id",
"Workgroup_Max_Size",
"Grid_Max_Size",
"Local_Mem_Size",
"Hive_Id",
"Gpu_Id",
"Workgroup_Max_Dim_X",
"Workgroup_Max_Dim_Y",
"Workgroup_Max_Dim_Z",
"Grid_Max_Dim_X",
"Grid_Max_Dim_Y",
"Grid_Max_Dim_Z",
"Name",
"Vendor_Name",
"Product_Name",
"Model_Name"}};
for(const auto& itr : agents)
{
auto row_ss = std::stringstream{};
agent_info_csv_encoder::write_row(row_ss,
itr.guid,
itr.node_id,
itr.logical_node_id,
itr.type,
itr.cpu_cores_count,
itr.simd_count,
itr.cpu_core_id_base,
itr.simd_id_base,
itr.max_waves_per_simd,
itr.lds_size_in_kb,
itr.gds_size_in_kb,
itr.num_gws,
itr.wave_front_size,
itr.num_xcc,
itr.cu_count,
itr.array_count,
itr.num_shader_banks,
itr.simd_arrays_per_engine,
itr.cu_per_simd_array,
itr.simd_per_cu,
itr.max_slots_scratch_cu,
itr.gfx_target_version,
itr.vendor_id,
itr.device_id,
itr.location_id,
itr.domain,
itr.drm_render_minor,
itr.num_sdma_engines,
itr.num_sdma_xgmi_engines,
itr.num_sdma_queues_per_engine,
itr.num_cp_queues,
itr.max_engine_clk_ccompute,
itr.max_engine_clk_fcompute,
itr.sdma_fw_version.Value,
itr.fw_version.Value,
itr.capability.Value,
itr.cu_per_engine,
itr.max_waves_per_cu,
itr.family_id,
itr.workgroup_max_size,
itr.grid_max_size,
itr.local_mem_size,
itr.hive_id,
itr.gpu_id,
itr.workgroup_max_dim.x,
itr.workgroup_max_dim.y,
itr.workgroup_max_dim.z,
itr.grid_max_dim.x,
itr.grid_max_dim.y,
itr.grid_max_dim.z,
itr.name,
itr.vendor_name,
itr.product_name,
itr.model_name);
ofs << row_ss.str();
}
}
void
write_counters_csv(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::counter>& counter_gen)
{
process_data_to_csv(csv_manager,
CsvType::COUNTER,
counter_gen,
[](CsvManager& cm, CsvType type, const rocpd::types::counter& counter) {
std::string agent_identifier =
create_agent_index(cm.config.agent_index_value,
counter.agent_abs_index,
counter.agent_log_index,
counter.agent_type_index,
std::string_view(counter.agent_type))
.as_string();
cm.write_line(type,
counter.guid,
counter.stack_id,
counter.dispatch_id,
fmt::format("\"{}\"", agent_identifier),
counter.queue_id,
counter.pid,
counter.tid,
counter.grid_size,
counter.kernel_id,
fmt::format("\"{}\"", counter.kernel_name),
counter.workgroup_size,
counter.lds_block_size,
counter.scratch_size,
counter.vgpr_count,
counter.accum_vgpr_count,
counter.sgpr_count,
fmt::format("\"{}\"", counter.counter_name),
counter.value,
counter.start,
counter.end);
});
}
void
write_csvs(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::kernel_dispatch>& kernel_dispatch,
const rocprofiler::tool::generator<rocpd::types::memory_copies>& memory_copies,
const rocprofiler::tool::generator<rocpd::types::memory_allocation>& memory_allocations,
const rocprofiler::tool::generator<rocpd::types::region>& hip_api_calls,
const rocprofiler::tool::generator<rocpd::types::region>& hsa_api_calls,
const rocprofiler::tool::generator<rocpd::types::region>& marker_api_calls,
const rocprofiler::tool::generator<rocpd::types::counter>& counters_calls,
const rocprofiler::tool::generator<rocpd::types::scratch_memory>& scratch_memory_calls,
const rocprofiler::tool::generator<rocpd::types::region>& rccl_calls,
const rocprofiler::tool::generator<rocpd::types::region>& rocdecode_calls,
const rocprofiler::tool::generator<rocpd::types::region>& rocjpeg_calls)
{
rocpd::output::write_kernel_csv(csv_manager, kernel_dispatch);
rocpd::output::write_memory_copy_csv(csv_manager, memory_copies);
rocpd::output::write_memory_allocation_csv(csv_manager, memory_allocations);
rocpd::output::write_hip_api_csv(csv_manager, hip_api_calls);
rocpd::output::write_hsa_api_csv(csv_manager, hsa_api_calls);
rocpd::output::write_marker_api_csv(csv_manager, marker_api_calls);
rocpd::output::write_counters_csv(csv_manager, counters_calls);
rocpd::output::write_scratch_memory_csv(csv_manager, scratch_memory_calls);
rocpd::output::write_rccl_api_csv(csv_manager, rccl_calls);
rocpd::output::write_rocdecode_api_csv(csv_manager, rocdecode_calls);
rocpd::output::write_rocjpeg_api_csv(csv_manager, rocjpeg_calls);
}
} // namespace output
} // namespace rocpd
-120
Просмотреть файл
@@ -1,120 +0,0 @@
// MIT License
//
// Copyright (c) 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.
#pragma once
#include "lib/python/rocpd/source/types.hpp"
#include "lib/common/defines.hpp"
#include "lib/output/generateStats.hpp"
#include "lib/output/generator.hpp"
#include "lib/output/metadata.hpp"
#include "lib/output/node_info.hpp"
#include "lib/output/output_config.hpp"
#include "lib/output/sql/common.hpp"
#include "lib/output/stream_info.hpp"
#include "lib/rocprofiler-sdk-tool/config.hpp"
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <filesystem>
#include <fstream>
#include <map>
#include <vector>
namespace rocpd
{
namespace output
{
using rocprofiler::tool::float_type;
struct CsvFileConfig
{
std::string filename;
std::string header;
};
enum class CsvType
{
KERNEL_DISPATCH,
MEMORY_COPY,
MEMORY_ALLOCATION,
SCRATCH_MEMORY,
HIP_API,
HSA_CSV_API,
MARKER,
COUNTER,
RCCL_API,
ROCDECODE_API,
ROCJPEG_API,
};
class CsvManager
{
public:
CsvManager(rocprofiler::tool::output_config output_cfg);
~CsvManager();
rocprofiler::tool::output_config config;
std::map<CsvType, CsvFileConfig> csv_configs;
std::ofstream& get_stream(CsvType type);
bool has_stream(CsvType type) const;
bool initialize_csv_file(CsvType type);
template <typename... Args>
void write_line(CsvType type, Args&&... args)
{
auto& stream = get_stream(type);
if(!stream.is_open()) return;
std::vector<std::string> items;
(items.push_back(fmt::format("{}", std::forward<Args>(args))), ...);
stream << fmt::format("{}\n", fmt::join(items, ","));
}
private:
std::map<CsvType, std::ofstream> streams;
std::map<CsvType, std::string> file_paths;
bool ensure_output_directory() const;
};
void
write_agent_info_csv(CsvManager& csv_manager, const std::vector<rocpd::types::agent>& agents);
void
write_csvs(CsvManager& csv_manager,
const rocprofiler::tool::generator<rocpd::types::kernel_dispatch>& kernel_dispatch,
const rocprofiler::tool::generator<rocpd::types::memory_copies>& memory_copies,
const rocprofiler::tool::generator<rocpd::types::memory_allocation>& memory_allocations,
const rocprofiler::tool::generator<rocpd::types::region>& hip_api_calls,
const rocprofiler::tool::generator<rocpd::types::region>& hsa_api_calls,
const rocprofiler::tool::generator<rocpd::types::region>& marker_api_calls,
const rocprofiler::tool::generator<rocpd::types::counter>& counters_calls,
const rocprofiler::tool::generator<rocpd::types::scratch_memory>& scratch_memory_calls,
const rocprofiler::tool::generator<rocpd::types::region>& rccl_calls,
const rocprofiler::tool::generator<rocpd::types::region>& rocdecode_calls,
const rocprofiler::tool::generator<rocpd::types::region>& rocjpeg_calls);
} // namespace output
} // namespace rocpd
+189
Просмотреть файл
@@ -231,3 +231,192 @@ def test_rocpd_data(
assert len(_rpd_data) == len(
_js_data
), f"query: {_rpd_query}\n{rpd_category} ({len(_rpd_data)}):\n\t{_rpd_data}\n{js_category} ({len(_js_data)}):\n\t{_js_data}"
def _perform_time_sanity_checks(data):
"""Helper function to perform time sanity checks on data."""
columns = data[0].keys()
start_columns = [c for c in columns if "start" in c.lower()]
end_columns = [c for c in columns if "end" in c.lower()]
if not start_columns or not end_columns:
return None, None
for record in data:
start_time = record[start_columns[0]]
end_time = record[end_columns[0]]
assert int(start_time) >= 0, f"Time error: Start time ({start_time}) < 0)."
assert int(end_time) >= 0, f"Time error: End time ({end_time}) < 0)."
assert int(end_time) >= int(
start_time
), f"Time error: End time ({end_time}) < Start time ({start_time})."
return start_columns[0], end_columns[0]
def _perform_csv_json_match(csv_row, json_row, mapping, json_data):
def get_nested(d, path):
"""Helper to get nested dict values using dot notation."""
keys = path.split(".")
for k in keys:
if isinstance(d, dict):
d = d.get(k)
else:
return None
return d
for csv_key, json_info in mapping.items():
if json_info is None:
continue
csv_value = csv_row[csv_key]
if csv_key == "Operation":
json_value = json_data["rocprofiler-sdk-tool"]["strings"]["buffer_records"][
json_row["kind"]
]["operations"][json_row["operation"]]
assert str(csv_value) in str(
json_value
), f"Mismatch for {csv_key}: CSV={csv_value} JSON={json_value}"
continue
if csv_key == "Function":
json_value = json_data["rocprofiler-sdk-tool"]["strings"]["buffer_records"][
json_row["kind"]
]["operations"][json_row["operation"]]
else:
json_path, subkey = json_info
json_value = get_nested(json_row, json_path)
if subkey:
json_value = (
json_value.get(subkey) if isinstance(json_value, dict) else None
)
assert str(csv_value) == str(
json_value
), f"Mismatch for {csv_key}: CSV={csv_value} JSON={json_value}"
def test_csv_data(
csv_data,
json_data,
categories=(
"agent",
"counter_collection",
"kernel",
"memory_allocation",
"memory_copy",
"regions",
),
):
mapping = {
"counter_collection": "counter_collection",
"kernel": "kernel_dispatch",
"memory_allocation": "memory_allocation",
"memory_copy": "memory_copy",
}
keys_mapping = {
"kernel": {
"Thread_Id": ("thread_id", None),
"Correlation_Id": ("correlation_id", "internal"),
"Start_Timestamp": ("start_timestamp", None),
"End_Timestamp": ("end_timestamp", None),
"Queue_Id": ("dispatch_info.queue_id.handle", None),
"Kernel_Id": ("dispatch_info.kernel_id", None),
"Dispatch_Id": ("dispatch_info.dispatch_id", None),
"Stream_Id": ("stream_id.handle", None),
"Workgroup_Size_X": ("dispatch_info.workgroup_size.x", None),
"Workgroup_Size_Y": ("dispatch_info.workgroup_size.y", None),
"Workgroup_Size_Z": ("dispatch_info.workgroup_size.z", None),
"Grid_Size_X": ("dispatch_info.grid_size.x", None),
"Grid_Size_Y": ("dispatch_info.grid_size.y", None),
"Grid_Size_Z": ("dispatch_info.grid_size.z", None),
},
"memory_allocation": {
"Operation": (), # Special case
"Correlation_Id": ("correlation_id", "internal"),
"Start_Timestamp": ("start_timestamp", None),
"End_Timestamp": ("end_timestamp", None),
},
"memory_copy": {
"Correlation_Id": ("correlation_id", "internal"),
"Start_Timestamp": ("start_timestamp", None),
"End_Timestamp": ("end_timestamp", None),
},
"regions": {
"Thread_Id": ("thread_id", None),
"Correlation_Id": ("correlation_id", "internal"),
"Start_Timestamp": ("start_timestamp", None),
"End_Timestamp": ("end_timestamp", None),
},
}
for data in csv_data:
filename, _csv_data = data
file_category = [category for category in categories if category in filename]
assert len(file_category) > 0, f"{filename} is not a valid csv filename"
category = file_category[0]
if category == "counter_collection":
_js_data = json_data["rocprofiler-sdk-tool"]["callback_records"][category]
elif category == "agent":
_js_data = json_data["rocprofiler-sdk-tool"]["agents"]
elif category == "regions":
buffer_records = json_data["rocprofiler-sdk-tool"]["buffer_records"]
_js_data = []
for key, value in buffer_records.items():
if key == "marker_api":
string_records = []
marker_records = json_data["rocprofiler-sdk-tool"]["buffer_records"][
"marker_api"
]
for item in json_data["rocprofiler-sdk-tool"]["strings"][
"buffer_records"
]:
if item["kind"] == "MARKER_CORE_RANGE_API":
string_records = item["operations"]
exclude_ops = {"roctxGetThreadId"}
for entry in marker_records:
# exclude records where start and end times are the same
if entry["start_timestamp"] == entry["end_timestamp"]:
continue
# excludes roctxMarkA and roctxGetThreadId operations
if (
string_records
and string_records[entry["operation"]] not in exclude_ops
):
_js_data.append(entry)
else:
if key.endswith("_api"):
_js_data.extend(value)
else:
json_records_key = mapping[category]
_js_data = json_data["rocprofiler-sdk-tool"]["buffer_records"][
json_records_key
]
assert len(_js_data) == len(
_csv_data
), f"Size mismatch for {category}: JSON size= {len(_js_data)} rows, CSV size= {len(_csv_data)} rows."
if not _csv_data:
continue # Exit if there is no data to validate
csv_start_col, csv_end_col = _perform_time_sanity_checks(_csv_data)
json_start_col, json_end_col = _perform_time_sanity_checks(_js_data)
if None in (csv_start_col, json_start_col, csv_end_col, json_end_col):
continue
_csv_data_sorted = sorted(
_csv_data, key=lambda x: (int(x[csv_start_col]), int(x[csv_end_col]))
)
_js_data_sorted = sorted(
_js_data, key=lambda x: (int(x[json_start_col]), int(x[json_end_col]))
)
for a, b in zip(_csv_data_sorted, _js_data_sorted):
_perform_csv_json_match(a, b, keys_mapping[category], json_data)
+6 -1
Просмотреть файл
@@ -290,7 +290,12 @@ add_test(
${Python3_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/validate.py --json-input
${CMAKE_CURRENT_BINARY_DIR}/rocpd-input-data/out_results.json --otf2-input
${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_results.otf2 --pftrace-input
${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_results.pftrace)
${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_results.pftrace --csv-input
${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_agent_info.csv
${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_counter_collection_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_kernel_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_memory_allocation_trace.csv
${CMAKE_CURRENT_BINARY_DIR}/rocpd-output-data/out_regions_trace.csv)
set_tests_properties(
rocprofv3-test-rocpd-validation
+15
Просмотреть файл
@@ -22,6 +22,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
import csv
import pandas as pd
import pytest
import json
@@ -50,6 +51,12 @@ def pytest_addoption(parser):
action="store",
help="Path to OTF2 trace file.",
)
parser.addoption(
"--csv-input",
action="store",
nargs="+",
help="Paths to CSV files.",
)
parser.addoption(
"--summary-input",
action="store",
@@ -83,6 +90,14 @@ def otf2_data(request):
return OTF2Reader(filename).read()[0]
@pytest.fixture
def csv_data(request):
filenames = request.config.getoption("--csv-input")
return [
(filename, list(csv.DictReader(open(filename, "r")))) for filename in filenames
]
@pytest.fixture
def summary_data(request):
filename = request.config.getoption("--summary-input")
+17
Просмотреть файл
@@ -46,6 +46,23 @@ def test_otf2_data(otf2_data, json_data):
)
def test_csv_data(csv_data, json_data):
import rocprofiler_sdk.tests.rocprofv3 as rocprofv3
rocprofv3.test_csv_data(
csv_data,
json_data,
(
"agent",
"counter_collection",
"kernel",
"memory_allocation",
"memory_copy",
"regions",
),
)
if __name__ == "__main__":
exit_code = pytest.main(["-x", __file__] + sys.argv[1:])
sys.exit(exit_code)