Add 'projects/rocprofiler-sdk/' from commit 'bf0fad1d5406fbc51403ba1aa9621a9d4a9bce2b'

git-subtree-dir: projects/rocprofiler-sdk
git-subtree-mainline: 50a90550e9
git-subtree-split: bf0fad1d54
이 커밋은 다음에 포함됨:
systems-assistant[bot]
2025-07-22 22:52:46 +00:00
1272개의 변경된 파일230117개의 추가작업 그리고 0개의 파일을 삭제
+25
파일 보기
@@ -0,0 +1,25 @@
#
# 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_sources csv.cpp functions.cpp interop.cpp otf2.cpp perfetto.cpp
types.cpp)
foreach(_PYTHON_VERSION ${ROCPROFILER_PYTHON_VERSIONS})
rocprofiler_rocpd_python_bindings_target_sources(
${_PYTHON_VERSION} PRIVATE ${libpyrocpd_source_sources}
${libpyrocpd_source_headers})
endforeach()
add_subdirectory(serialization)
+55
파일 보기
@@ -0,0 +1,55 @@
// MIT License
//
// Copyright (c) 2023 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/output/agent_info.hpp"
#include "lib/output/kernel_symbol_info.hpp"
#include "lib/output/metadata.hpp"
#include <sqlite3.h>
#include <cereal/archives/json.hpp>
#include <cstdint>
#include <deque>
#include <sstream>
#include <string>
#include <utility>
namespace rocpd
{
namespace common
{
template <typename FuncT, typename... Args>
void
read_json_string(const std::string& inp, FuncT&& _func, Args&&... _args)
{
using json_archive = cereal::JSONInputArchive;
{
auto json_ss = std::stringstream{inp};
auto ar = json_archive{json_ss};
std::forward<FuncT>(_func)(ar, std::forward<Args>(_args)...);
}
}
} // namespace common
} // namespace rocpd
+738
파일 보기
@@ -0,0 +1,738 @@
// 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\",\"Private_Segment_Size\",\"Group_"
"Segment_Size\",\"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_trace.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 : 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.scratch_size,
kernel.lds_size,
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 operation = fmt::format("MEMORY_ALLOCATION_{}", malloc.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
파일 보기
@@ -0,0 +1,120 @@
// 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
+138
파일 보기
@@ -0,0 +1,138 @@
// MIT License
//
// Copyright (c) 2022 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/functions.hpp"
#include "lib/common/logging.hpp"
#include "lib/common/utility.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/cxx/serialization.hpp>
#include <fmt/format.h>
#include <sqlite3.h>
#include <cereal/cereal.hpp>
#include <cstdint>
namespace rocpd
{
namespace functions
{
namespace
{
// Custom SQL function: rocpd_get_string(common_string_id, unique_string_id, nid, pid)
void
rocpd_get_string(sqlite3_context* context, int argc, sqlite3_value** argv)
{
if(argc != 4)
{
ROCP_WARNING << "rocpd_get_string requires exactly 4 arguments (common_string_id, "
"unique_string_id, nid, pid)";
sqlite3_result_null(context);
return;
}
auto* db = static_cast<sqlite3*>(sqlite3_user_data(context));
// common and unique name ids passed in
auto c_name_id = sqlite3_value_int64(argv[0]);
auto u_name_id = sqlite3_value_int64(argv[1]);
auto execute_query = [&](std::string_view _query, std::initializer_list<int64_t>&& _args) {
// char query[256];
// snprintf(query, sizeof(query), "SELECT value FROM %s WHERE id = ?", table);
sqlite3_stmt* stmt = nullptr;
if(int rc = sqlite3_prepare_v2(db, _query.data(), -1, &stmt, nullptr); rc != SQLITE_OK)
{
ROCP_WARNING << fmt::format("SQL prepare failed: {}", sqlite3_errmsg(db));
sqlite3_result_error(context, "SQL prepare failed", -1);
return;
}
int64_t idx = 1;
for(auto itr : _args)
sqlite3_bind_int64(stmt, idx++, itr);
if(auto rc = sqlite3_step(stmt); rc == SQLITE_ROW)
{
const unsigned char* result = sqlite3_column_text(stmt, 0);
sqlite3_result_text(
context, reinterpret_cast<const char*>(result), -1, SQLITE_TRANSIENT);
}
else if(rc == SQLITE_DONE)
{
ROCP_WARNING << fmt::format("No row found for query '{}'", _query);
sqlite3_result_null(context);
}
else
{
ROCP_WARNING << fmt::format("SQL step failed: {}", sqlite3_errmsg(db));
sqlite3_result_error(context, "SQL step failed", -1);
}
sqlite3_finalize(stmt);
};
if(c_name_id != 0)
{
execute_query("SELECT string FROM rocpd_common_string WHERE id == ?",
std::initializer_list<int64_t>{c_name_id});
}
else if(u_name_id != 0)
{
auto u_nid = sqlite3_value_int64(argv[2]);
auto u_pid = sqlite3_value_int64(argv[3]);
execute_query(
"SELECT string FROM rocpd_unique_string WHERE id == ? AND nid = ? AND pid = ?",
std::initializer_list<int64_t>{u_name_id, u_nid, u_pid});
}
else
{
sqlite3_result_null(context);
}
}
} // namespace
void
define_for_database(sqlite3* conn)
{
if(false)
{
sqlite3_create_function_v2(conn,
"rocpd_get_string",
4,
SQLITE_UTF8,
conn,
rocpd_get_string,
nullptr,
nullptr,
nullptr);
}
rocprofiler::common::consume_args(conn);
}
} // namespace functions
} // namespace rocpd
+38
파일 보기
@@ -0,0 +1,38 @@
// 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/common/defines.hpp"
#include "lib/common/logging.hpp"
#include <pybind11/pybind11.h>
#include <sqlite3.h>
namespace rocpd
{
namespace functions
{
void
define_for_database(sqlite3* conn);
} // namespace functions
} // namespace rocpd
+209
파일 보기
@@ -0,0 +1,209 @@
// 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 "interop.hpp"
#include "pysqlite_Connection.h"
#include "lib/common/defines.hpp"
#include "lib/common/logging.hpp"
#include "lib/common/static_object.hpp"
#include "lib/common/utility.hpp"
#include "lib/output/timestamps.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/cxx/utility.hpp>
#include <fmt/format.h>
#include <gotcha/gotcha.h>
#include <gotcha/gotcha_types.h>
#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>
#include <sqlite3.h>
#include <algorithm>
namespace rocpd
{
namespace interop
{
namespace
{
namespace sdk = ::rocprofiler::sdk;
namespace common = ::rocprofiler::common;
// namespace tool = ::rocprofiler::tool;
// namespace sql = ::rocprofiler::tool::sql;
using sqlite3_open_func_t = int (*)(const char*, sqlite3**);
using sqlite3_open_v2_func_t = int (*)(const char*, sqlite3**, int, const char*);
using sqlite3_close_func_t = int (*)(sqlite3*);
gotcha_wrappee_handle_t orig_sqlite3_open = {};
gotcha_wrappee_handle_t orig_sqlite3_open_v2 = {};
gotcha_wrappee_handle_t orig_sqlite3_close = {};
gotcha_wrappee_handle_t orig_sqlite3_close_v2 = {};
using sqlite_object_map_t = std::unordered_map<PyObject*, sqlite3*>;
thread_local sqlite3* last_sqlite3 = nullptr;
auto sqlite_obj_mapping = sqlite_object_map_t{};
auto*
get_mapping()
{
static auto*& _v = common::static_object<sqlite_object_map_t>::construct();
return _v;
}
void
erase_connection(sqlite3* pDb)
{
if(!get_mapping()) return;
if(last_sqlite3 && last_sqlite3 == pDb) last_sqlite3 = nullptr;
auto itr = std::find_if(get_mapping()->begin(), get_mapping()->end(), [pDb](const auto& val) {
return (val.second == pDb);
});
if(itr != get_mapping()->end()) get_mapping()->erase(itr);
}
namespace impl
{
int
sqlite3_open(const char* filename, /* Database filename (UTF-8) */
sqlite3** ppDb /* OUT: SQLite db handle */
)
{
ROCP_TRACE << fmt::format("invoking {}... {}", __FUNCTION__, filename);
auto func = reinterpret_cast<sqlite3_open_func_t>(gotcha_get_wrappee(orig_sqlite3_open));
auto ret = func(filename, ppDb);
if(ppDb) last_sqlite3 = *ppDb;
return ret;
}
int
sqlite3_open_v2(const char* filename, sqlite3** ppDb, int flags, const char* zVfs)
{
ROCP_TRACE << fmt::format("invoking {}... {}", __FUNCTION__, filename);
auto func = reinterpret_cast<sqlite3_open_v2_func_t>(gotcha_get_wrappee(orig_sqlite3_open_v2));
auto ret = func(filename, ppDb, flags, zVfs);
if(ppDb) last_sqlite3 = *ppDb;
return ret;
}
int
sqlite3_close(sqlite3* pDb)
{
ROCP_TRACE << fmt::format("invoking {}... ", __FUNCTION__);
auto func = reinterpret_cast<sqlite3_close_func_t>(gotcha_get_wrappee(orig_sqlite3_close));
auto ret = func(pDb);
erase_connection(pDb);
return ret;
}
int
sqlite3_close_v2(sqlite3* pDb)
{
ROCP_TRACE << fmt::format("invoking {}... ", __FUNCTION__);
auto func = reinterpret_cast<sqlite3_close_func_t>(gotcha_get_wrappee(orig_sqlite3_close_v2));
auto ret = func(pDb);
erase_connection(pDb);
return ret;
}
} // namespace impl
auto bindings = std::array<gotcha_binding_t, 4>{
gotcha_binding_t{"sqlite3_open",
reinterpret_cast<void*>(impl::sqlite3_open),
&orig_sqlite3_open},
gotcha_binding_t{"sqlite3_close",
reinterpret_cast<void*>(impl::sqlite3_close),
&orig_sqlite3_close},
gotcha_binding_t{"sqlite3_open_v2",
reinterpret_cast<void*>(impl::sqlite3_open_v2),
&orig_sqlite3_open_v2},
gotcha_binding_t{"sqlite3_close_v2",
reinterpret_cast<void*>(impl::sqlite3_close_v2),
&orig_sqlite3_close_v2},
};
} // namespace
void
activate_gotcha_bindings()
{
// activate the gotcha wrappers
auto _err = gotcha_wrap(bindings.data(), bindings.size(), "rocpd.sqlite3");
ROCP_WARNING_IF(_err != GOTCHA_SUCCESS) << "gotcha error for rocpd.sqlite3";
}
sqlite3*
map_connection(py::object obj)
{
if(!get_mapping()) return nullptr;
get_mapping()->emplace(obj.ptr(), last_sqlite3);
auto* _ret = get_mapping()->at(obj.ptr());
ROCP_INFO << "[pyrocpd][mapping] " << obj.ptr() << " <-> " << _ret;
return get_mapping()->at(obj.ptr());
}
sqlite3*
get_connection(py::object&& obj)
{
if(!obj) return nullptr;
if(get_mapping())
{
if(auto itr = get_mapping()->find(obj.ptr());
itr != get_mapping()->end() && itr->second != nullptr)
{
ROCP_INFO << fmt::format(
"sqlite3 python connection ({}) mapped to sqlite3* ({}) safely "
"via gotcha capture of sqlite3_open",
sdk::utility::as_hex(obj.ptr(), 16),
sdk::utility::as_hex(itr->second, 16));
return itr->second;
}
}
ROCP_CI_LOG(WARNING) << fmt::format(
"sqlite3 python connection ({}) not captured by gotcha... accessing sqlite3* via "
"reinterpret_cast<pysqlite_Connection>(PyObject*&)->db :: [unsafe]",
sdk::utility::as_hex(obj.ptr(), 16));
return reinterpret_cast<pysqlite_Connection*&>(obj.ptr())->db;
}
} // namespace interop
} // namespace rocpd
+46
파일 보기
@@ -0,0 +1,46 @@
// 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/common/defines.hpp"
#include "lib/common/logging.hpp"
#include <pybind11/pybind11.h>
#include <sqlite3.h>
namespace rocpd
{
namespace interop
{
namespace py = ::pybind11;
void
activate_gotcha_bindings();
sqlite3*
map_connection(py::object obj);
sqlite3*
get_connection(py::object&& obj);
} // namespace interop
} // namespace rocpd
+905
파일 보기
@@ -0,0 +1,905 @@
// MIT License
//
// Copyright (c) 2023-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/otf2.hpp"
#include "lib/common/defines.hpp"
#include "lib/common/filesystem.hpp"
#include "lib/common/hasher.hpp"
#include "lib/common/mpl.hpp"
#include "lib/common/units.hpp"
#include "lib/common/utility.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/output/timestamps.hpp"
#include "lib/rocprofiler-sdk-tool/config.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/marker/api_id.h>
#include <rocprofiler-sdk/rocprofiler.h>
#include <rocprofiler-sdk/cxx/hash.hpp>
#include <rocprofiler-sdk/cxx/operators.hpp>
#include <rocprofiler-sdk/cxx/perfetto.hpp>
#include <fmt/format.h>
#include <otf2/OTF2_AttributeList.h>
#include <otf2/OTF2_AttributeValue.h>
#include <otf2/OTF2_Definitions.h>
#include <otf2/OTF2_GeneralDefinitions.h>
#include <otf2/OTF2_Pthread_Locks.h>
#include <otf2/otf2.h>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <ctime>
#include <future>
#include <map>
#include <mutex>
#include <thread>
#include <unordered_map>
#include <utility>
#include <vector>
#define OTF2_CHECK(result) \
{ \
OTF2_ErrorCode ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__) = result; \
if(ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__) != OTF2_SUCCESS) \
{ \
auto _err_name = OTF2_Error_GetName(ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__)); \
auto _err_msg = \
OTF2_Error_GetDescription(ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__)); \
ROCP_FATAL << #result << " failed with error code " << _err_name \
<< " (code=" << ROCPROFILER_VARIABLE(CHECKSTATUS, __LINE__) \
<< ") :: " << _err_msg; \
} \
}
namespace rocpd
{
namespace output
{
namespace
{
template <typename Tp, size_t N>
struct array_hash
{
size_t operator()(const std::array<Tp, N>& _data) const
{
constexpr size_t seed = 0x9e3779b9;
size_t _val = 0;
for(const auto& itr : _data)
_val ^= std::hash<Tp>{}(itr) + seed + (_val << 6) + (_val >> 2);
return _val;
}
template <typename... Up>
size_t operator()(Up... _data) const
{
static_assert(sizeof...(Up) == N, "Insufficient data");
return operator()(std::array<Tp, N>{std::forward<Up>(_data)...});
}
};
struct region_info
{
std::string name = {};
OTF2_RegionRole_enum region_role = OTF2_REGION_ROLE_FUNCTION;
OTF2_Paradigm_enum paradigm = OTF2_PARADIGM_HIP;
};
OTF2_FlushType
pre_flush(void* userData,
OTF2_FileType fileType,
OTF2_LocationRef location,
void* callerData,
bool fini);
OTF2_TimeStamp
post_flush(void* userData, OTF2_FileType fileType, OTF2_LocationRef location);
template <typename... Args>
void
consume_variables(Args&&...)
{}
using event_writer_t = OTF2_EvtWriter;
using archive_t = OTF2_Archive;
using attribute_list_t = OTF2_AttributeList;
using hash_value_t = size_t;
using hash_map_t = std::unordered_map<hash_value_t, region_info>;
auto main_tid = rocprofiler::common::get_tid();
archive_t* archive = nullptr;
auto flush_callbacks = OTF2_FlushCallbacks{pre_flush, post_flush};
OTF2_GlobalDefWriter* global_def_writer = nullptr; // shared between data bases (processes)
enum rocprofiler_location_type_t
{
ROCPROFILER_AGENT_NO_TYPE = 0,
ROCPROFILER_AGENT_MEMORY_COPY_TYPE,
ROCPROFILER_AGENT_DISPATCH_TYPE,
ROCPROFILER_AGENT_MEMORY_ALLOC_TYPE,
ROCPROFILER_AGENT_MEMORY_DEALLOC_TYPE
};
struct location_base
{
uint64_t pid = 0;
uint64_t tid = 0;
uint64_t agent_handle = 0;
uint64_t queue_id = 0;
rocprofiler_location_type_t type = ROCPROFILER_AGENT_NO_TYPE;
location_base(pid_t _pid,
pid_t _tid,
uint64_t _agent_handle = 0,
rocprofiler_location_type_t _type = ROCPROFILER_AGENT_NO_TYPE,
uint64_t _queue_id = 0)
: pid{static_cast<uint64_t>(_pid)}
, tid{static_cast<uint64_t>(_tid)}
, agent_handle{_agent_handle}
, queue_id{_queue_id}
, type{_type}
{}
auto hash() const
{
return array_hash<uint64_t, 5>{}(pid, tid, agent_handle + 1, queue_id + 1, type);
}
};
bool
operator<(const location_base& lhs, const location_base& rhs)
{
return std::tie(lhs.pid, lhs.tid, lhs.agent_handle, lhs.queue_id, lhs.type) <
std::tie(rhs.pid, rhs.tid, rhs.agent_handle, rhs.queue_id, rhs.type);
}
struct location_data : location_base
{
location_data(pid_t _pid,
pid_t _tid,
uint64_t _agent_handle = 0,
rocprofiler_location_type_t _type = ROCPROFILER_AGENT_NO_TYPE,
uint64_t _queue_id = 0)
: location_base{_pid, _tid, _agent_handle, _type, _queue_id}
, index{++index_counter}
, event_writer{OTF2_Archive_GetEvtWriter(CHECK_NOTNULL(archive), index)}
{
CHECK_NOTNULL(event_writer);
}
using location_base::hash;
static uint64_t index_counter;
uint64_t index = 0;
event_writer_t* event_writer = nullptr;
bool operator==(const location_base& rhs) const { return (hash() == rhs.hash()); }
};
uint64_t location_data::index_counter = 0;
OTF2_TimeStamp
get_time()
{
auto _ts = rocprofiler_timestamp_t{};
rocprofiler_get_timestamp(&_ts);
return static_cast<OTF2_TimeStamp>(_ts);
}
auto&
get_locations()
{
static auto _v = std::vector<std::unique_ptr<location_data>>{};
return _v;
}
const location_data*
get_location(const location_base& _location, bool _init = false)
{
for(auto& itr : get_locations())
if(*itr == _location) return itr.get();
if(_init)
return get_locations()
.emplace_back(std::make_unique<location_data>(_location.pid,
_location.tid,
_location.agent_handle,
_location.type,
_location.queue_id))
.get();
return nullptr;
}
event_writer_t*
get_event_writer(const location_base& _location, bool _init = false)
{
const auto* _loc = get_location(_location, _init);
return (_loc) ? _loc->event_writer : nullptr;
}
OTF2_FlushType
pre_flush(void* userData,
OTF2_FileType fileType,
OTF2_LocationRef location,
void* callerData,
bool fini)
{
consume_variables(userData, fileType, location, callerData, fini);
return OTF2_FLUSH;
}
OTF2_TimeStamp
post_flush(void* userData, OTF2_FileType fileType, OTF2_LocationRef location)
{
consume_variables(userData, fileType, location);
return get_time();
}
template <typename Tp>
size_t
get_hash_id(Tp&& _val)
{
using value_type = rocprofiler::common::mpl::unqualified_type_t<Tp>;
if constexpr(!std::is_pointer<Tp>::value)
return std::hash<value_type>{}(std::forward<Tp>(_val));
else if constexpr(std::is_same<value_type, const char*>::value ||
std::is_same<value_type, char*>::value)
return get_hash_id(std::string_view{_val});
else
return get_hash_id(*_val);
}
template <typename... Args>
auto
add_event(std::string_view name,
const location_base& _location,
rocprofiler_callback_phase_t _phase,
OTF2_TimeStamp _ts,
attribute_list_t* _attributes = nullptr)
{
auto* evt_writer = get_event_writer(_location, true);
auto _hash = get_hash_id(name);
if(_phase == ROCPROFILER_CALLBACK_PHASE_ENTER)
OTF2_CHECK(OTF2_EvtWriter_Enter(evt_writer, _attributes, _ts, _hash))
else if(_phase == ROCPROFILER_CALLBACK_PHASE_EXIT)
OTF2_CHECK(OTF2_EvtWriter_Leave(evt_writer, _attributes, _ts, _hash))
else
ROCP_FATAL << "otf2::add_event phase is not enter or exit";
}
void
setup(const rocprofiler::tool::output_config& cfg, uint64_t min_start, uint64_t max_fini)
{
namespace fs = rocprofiler::common::filesystem;
auto _filename = rocprofiler::tool::get_output_filename(cfg, "results", std::string_view{});
auto _filepath = fs::path{_filename};
auto _name = _filepath.filename().string();
auto _path = _filepath.parent_path().string();
if(fs::exists(_filepath)) fs::remove_all(_filepath);
constexpr uint64_t evt_chunk_size = 2 * rocprofiler::common::units::MB;
constexpr uint64_t def_chunk_size = 8 * rocprofiler::common::units::MB;
archive = OTF2_Archive_Open(_path.c_str(),
_name.c_str(),
OTF2_FILEMODE_WRITE,
evt_chunk_size, // event chunk size
def_chunk_size, // def chunk size
OTF2_SUBSTRATE_POSIX,
OTF2_COMPRESSION_NONE);
OTF2_CHECK(OTF2_Archive_SetFlushCallbacks(archive, &flush_callbacks, nullptr));
OTF2_CHECK(OTF2_Archive_SetSerialCollectiveCallbacks(archive));
OTF2_CHECK(OTF2_Pthread_Archive_SetLockingCallbacks(archive, nullptr));
OTF2_CHECK(OTF2_Archive_OpenEvtFiles(archive));
ROCP_ERROR << "Opened result file: " << _filename << ".otf2";
auto _timer_resolution =
rocprofiler::common::get_clock_period_ns_impl(rocprofiler::common::default_clock_id) *
std::nano::den;
auto _global_offset = min_start;
auto _max_trace_length = (max_fini - min_start);
global_def_writer = OTF2_Archive_GetGlobalDefWriter(archive);
OTF2_CHECK(OTF2_GlobalDefWriter_WriteClockProperties(
global_def_writer,
_timer_resolution,
_global_offset,
_max_trace_length,
std::chrono::system_clock::now().time_since_epoch().count()));
OTF2_CHECK(OTF2_GlobalDefWriter_WriteString(global_def_writer, 0, ""));
auto add_write_string = [](size_t _hash, std::string_view _name_strv) {
static auto _existing = std::unordered_set<size_t>{};
if(_hash > 0 && _existing.count(_hash) == 0)
{
OTF2_CHECK(
OTF2_GlobalDefWriter_WriteString(global_def_writer, _hash, _name_strv.data()));
_existing.emplace(_hash);
}
};
auto add_write_string_val = [&add_write_string](std::string_view _name_v) {
auto _hash_v = get_hash_id(_name_v);
add_write_string(_hash_v, _name_v);
return _hash_v;
};
//(must be shared between processes)
auto _attr_name = std::string_view{"category"};
auto _attr_desc = std::string_view{"tracing category"};
auto _attr_name_hash = add_write_string_val(_attr_name);
auto _attr_desc_hash = add_write_string_val(_attr_desc);
OTF2_CHECK(OTF2_GlobalDefWriter_WriteAttribute(
global_def_writer, 0, _attr_name_hash, _attr_desc_hash, OTF2_TYPE_STRING));
}
void
shutdown()
{
OTF2_CHECK(OTF2_Archive_Close(archive));
}
struct event_info
{
explicit event_info(location_base&& _loc)
: m_location{output::get_location(std::forward<location_base>(_loc), true)}
{}
auto id() const { return m_location->index; }
auto hash() const { return m_location->hash(); }
const location_base* get_location() const { return m_location; }
std::string name = {};
uint64_t event_count = 0;
private:
const location_data* m_location = nullptr;
};
attribute_list_t*
create_attribute_list_for_name(const char* name)
{
auto* _val = OTF2_AttributeList_New();
auto _hash = get_hash_id(name);
auto _attr_value = OTF2_AttributeValue{};
_attr_value.stringRef = _hash;
OTF2_AttributeList_AddAttribute(_val, 0, OTF2_TYPE_STRING, _attr_value);
return _val;
}
} // namespace
OTF2Session::OTF2Session(const tool::output_config& output_cfg,
uint64_t min_start,
uint64_t max_fini)
: config{output_cfg}
{
setup(output_cfg, min_start, max_fini);
}
OTF2Session::~OTF2Session() { shutdown(); }
void
write_otf2(const OTF2Session& otf2_session,
const types::process& process,
const uint16_t tree_node_id,
const std::unordered_map<uint64_t, extended_agent>& agent_data,
const tool::generator<types::thread>& thread_gen,
const tool::generator<types::region>& api_gen,
const tool::generator<types::kernel_dispatch>& kernel_dispatch_gen,
const tool::generator<types::memory_copies>& memory_copy_gen,
const tool::generator<types::memory_allocation>& memory_allocation_gen)
{
const uint64_t _no_agent_handle = 0;
// std::numeric_limits<uint64_t>::max() - 1;
const auto& ocfg = otf2_session.config;
auto _app_ts = rocprofiler::tool::timestamps_t{process.start, process.fini};
auto thread_event_info = std::map<pid_t, event_info>{};
auto agent_memcpy_info =
std::map<pid_t, std::map<uint64_t, event_info>>{}; // tid -> agent_handle ->evt
auto agent_memalloc_info =
std::map<pid_t, std::map<uint64_t, event_info>>{}; // // tid -> agent_handle ->evt
auto agent_dispatch_info =
std::map<pid_t,
std::map<uint64_t, std::map<uint64_t, event_info>>>{}; // tid -> agent_handle
// -> quieue_id -> evt
auto _get_alloc_level_type_name = [](const std::string& level,
const std::string& type) -> std::string {
static const std::unordered_map<std::string, std::unordered_map<std::string, std::string>>
name_map = {
{"REAL",
{{"ALLOC", "MEMORY_ALLOCATION_ALLOCATE"}, {"FREE", "MEMORY_ALLOCATION_FREE"}}},
{"VIRTUAL",
{{"ALLOC", "MEMORY_ALLOCATION_VMEM_ALLOCATE"},
{"FREE", "MEMORY_ALLOCATION_VMEM_FREE"}}},
{"SCRATCH", {{"ALLOC", "SCRATCH_MEMORY_ALLOC"}, {"FREE", "SCRATCH_MEMORY_FREE"}}}};
if((name_map.count(level) != 0u) && (name_map.at(level).count(type) != 0u))
return name_map.at(level).at(type);
return level == "SCRATCH" || level == "REAL" || level == "VIRTUAL" ? level + "_MEMORY_NONE"
: "UNKNOWN_LEVEL";
};
for(auto ditr : thread_gen)
for(const auto& itr : thread_gen.get(ditr))
{
auto _evt_info = event_info{location_base{process.pid, itr.tid}};
_evt_info.name = fmt::format("Thread {}", itr.tid);
thread_event_info.emplace(itr.tid, _evt_info);
}
auto _hash_data = hash_map_t{};
struct evt_data
{
rocprofiler_callback_phase_t phase = ROCPROFILER_CALLBACK_PHASE_NONE;
std::string name = {};
const location_base* location = nullptr;
uint64_t timestamp = 0;
OTF2_AttributeList* attributes = nullptr;
};
auto _data = std::deque<evt_data>{};
auto _attr_str = std::unordered_map<size_t, std::string_view>{};
// copypatse from perfetto. TODO: Move to a common place?
auto get_category_string = [](std::string_view _category) {
static auto buffer_names = rocprofiler::sdk::get_buffer_tracing_names();
auto _category_idx = ROCPROFILER_BUFFER_TRACING_NONE;
for(const auto& citr : buffer_names)
{
if(_category == citr.name) _category_idx = citr.value;
}
return rocprofiler::sdk::get_perfetto_category(_category_idx);
};
auto get_category_attribute = [&get_category_string,
&_attr_str](const std::string& category) -> OTF2_AttributeList* {
const auto* _perfetto_category = get_category_string(category);
_attr_str.emplace(get_hash_id(_perfetto_category), _perfetto_category);
return create_attribute_list_for_name(_perfetto_category);
};
for(auto ditr : api_gen)
for(const auto& itr : api_gen.get(ditr))
{
std::string _name = itr.name;
_hash_data.emplace(get_hash_id(_name),
region_info{_name, OTF2_REGION_ROLE_FUNCTION, OTF2_PARADIGM_HIP});
auto& _evt_info = thread_event_info.at(itr.tid);
_evt_info.event_count += 1;
auto* attributes = get_category_attribute(itr.category);
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
_name,
_evt_info.get_location(),
itr.start,
attributes});
if(!attributes)
{
ROCP_FATAL << "Undefined attributes for api call " << itr.name;
}
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
_name,
_evt_info.get_location(),
itr.end,
nullptr});
}
for(auto ditr : memory_copy_gen)
for(const auto& itr : memory_copy_gen.get(ditr))
{
std::string _name = itr.name;
_hash_data.emplace(
get_hash_id(_name),
region_info{_name, OTF2_REGION_ROLE_DATA_TRANSFER, OTF2_PARADIGM_HIP});
auto _extended_agent = agent_data.at(itr.dst_agent_abs_index);
auto _agent_handle = _extended_agent.types_agent.id.handle;
auto _evt_info = event_info{location_base{
process.pid, itr.tid, _agent_handle, ROCPROFILER_AGENT_MEMORY_COPY_TYPE}};
auto agent_index_info = _extended_agent.agent_index;
_evt_info.name = fmt::format("Thread {}, Copy to {} {}",
itr.tid,
agent_index_info.type,
agent_index_info.as_string("-"));
_evt_info.event_count += 1;
agent_memcpy_info[itr.tid].emplace(_agent_handle, _evt_info);
const auto* _perfetto_name =
rocprofiler::sdk::perfetto_category<rocprofiler::sdk::category::memory_copy>::name;
_attr_str.emplace(get_hash_id(_perfetto_name), _perfetto_name);
auto* _attrs = create_attribute_list_for_name(_perfetto_name);
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
_name,
_evt_info.get_location(),
itr.start,
_attrs});
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
_name,
_evt_info.get_location(),
itr.end,
nullptr});
};
for(auto ditr : memory_allocation_gen)
for(const auto& itr : memory_allocation_gen.get(ditr))
{
std::string _alloc_operation = _get_alloc_level_type_name(itr.level, itr.type);
const auto* _perfetto_name = rocprofiler::sdk::perfetto_category<
rocprofiler::sdk::category::memory_allocation>::name;
_attr_str.emplace(get_hash_id(_perfetto_name), _perfetto_name);
auto* _attrs = create_attribute_list_for_name(_perfetto_name);
if(itr.type == "ALLOC")
{
_hash_data.emplace(
get_hash_id(_alloc_operation),
region_info{_alloc_operation, OTF2_REGION_ROLE_ALLOCATE, OTF2_PARADIGM_HIP});
auto _extended_agent = agent_data.at(itr.agent_abs_index);
auto _handle = _extended_agent.types_agent.id.handle;
auto _evt_info = event_info{location_base{
process.pid, itr.tid, _handle, ROCPROFILER_AGENT_MEMORY_ALLOC_TYPE}};
auto agent_index_info = _extended_agent.agent_index;
_evt_info.name = fmt::format("Thread {}, Memory Allocate at {} {}",
itr.tid,
agent_index_info.type,
agent_index_info.as_string("-"));
agent_memalloc_info[itr.tid].emplace(_handle, _evt_info);
_evt_info.event_count += 1;
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
_alloc_operation,
_evt_info.get_location(),
itr.start,
_attrs});
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
_alloc_operation,
_evt_info.get_location(),
itr.end,
nullptr});
}
else if(itr.type == "FREE") //
{
_hash_data.emplace(
get_hash_id(_alloc_operation),
region_info{_alloc_operation, OTF2_REGION_ROLE_DEALLOCATE, OTF2_PARADIGM_HIP});
auto _evt_info = event_info{location_base{
process.pid, itr.tid, _no_agent_handle, ROCPROFILER_AGENT_MEMORY_DEALLOC_TYPE}};
_evt_info.name = fmt::format("Thread {}, Memory Deallocate (Free)", itr.tid);
agent_memalloc_info[itr.tid].emplace(_no_agent_handle, _evt_info);
_evt_info.event_count += 1;
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
_alloc_operation,
_evt_info.get_location(),
itr.start,
_attrs});
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
_alloc_operation,
_evt_info.get_location(),
itr.end,
nullptr});
}
else
{
auto _evt_info = event_info{location_base{process.pid, itr.tid}};
_evt_info.name = fmt::format("Thread {}, Memory Operation UNK", itr.tid);
_evt_info.event_count += 1;
agent_memalloc_info[itr.tid].emplace(_no_agent_handle, _evt_info);
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
_alloc_operation,
_evt_info.get_location(),
itr.start,
_attrs});
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
_alloc_operation,
_evt_info.get_location(),
itr.end,
nullptr});
}
}
auto _queue_ids = std::map<uint64_t, uint64_t>{};
for(auto ditr : kernel_dispatch_gen)
for(const auto& itr : kernel_dispatch_gen.get(ditr))
{
auto _name = fmt::format(
"{}", (ocfg.kernel_rename && !itr.region.empty()) ? itr.region : itr.name);
_hash_data.emplace(get_hash_id(_name),
region_info{_name, OTF2_REGION_ROLE_FUNCTION, OTF2_PARADIGM_HIP});
const auto* _perfetto_name = rocprofiler::sdk::perfetto_category<
rocprofiler::sdk::category::kernel_dispatch>::name;
_attr_str.emplace(get_hash_id(_perfetto_name), _perfetto_name);
auto* _attrs = create_attribute_list_for_name(_perfetto_name);
auto _extended_agent = agent_data.at(itr.agent_abs_index);
auto _handle = _extended_agent.types_agent.id.handle;
auto agent_index_info = _extended_agent.agent_index;
auto _evt_info = event_info{location_base{
process.pid, itr.tid, _handle, ROCPROFILER_AGENT_DISPATCH_TYPE, itr.queue_id}};
if(_queue_ids.count(itr.queue_id) == 0)
{
_queue_ids.emplace(itr.queue_id, _queue_ids.size());
}
_evt_info.name = fmt::format("Thread {}, Compute on {} {}, Queue {}",
itr.tid,
agent_index_info.type,
agent_index_info.as_string("-"),
_queue_ids.at(itr.queue_id));
_evt_info.event_count += 1;
agent_dispatch_info[itr.tid][_handle].emplace(itr.queue_id, _evt_info);
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_ENTER,
_name,
_evt_info.get_location(),
itr.start,
_attrs});
_data.emplace_back(evt_data{ROCPROFILER_CALLBACK_PHASE_EXIT,
_name,
_evt_info.get_location(),
itr.end,
nullptr});
}
std::sort(_data.begin(), _data.end(), [](const evt_data& lhs, const evt_data& rhs) {
if(lhs.timestamp != rhs.timestamp) return (lhs.timestamp < rhs.timestamp);
if(lhs.phase != rhs.phase) return (lhs.phase > rhs.phase);
return (*lhs.location < *rhs.location);
});
for(const auto& itr : _data)
{
add_event(itr.name, *itr.location, itr.phase, itr.timestamp, itr.attributes);
ROCP_ERROR_IF(itr.timestamp < _app_ts.app_start_time)
<< "event found with timestamp < app start time by "
<< (_app_ts.app_start_time - itr.timestamp) << " nsec :: " << itr.name;
ROCP_ERROR_IF(itr.timestamp > _app_ts.app_end_time)
<< "event found with timestamp > app end time by "
<< (itr.timestamp - _app_ts.app_end_time) << " nsec :: " << itr.name;
}
for(const auto& itr : _data)
{
if(itr.attributes) OTF2_AttributeList_Delete(itr.attributes);
}
OTF2_CHECK(OTF2_Archive_CloseEvtFiles(archive));
OTF2_CHECK(OTF2_Archive_OpenDefFiles(archive));
for(auto& itr : get_locations())
{
OTF2_DefWriter* def_writer = OTF2_Archive_GetDefWriter(archive, itr->index);
OTF2_Archive_CloseDefWriter(archive, def_writer);
}
OTF2_CHECK(OTF2_Archive_CloseDefFiles(archive));
for(const auto& itr : _hash_data)
{
if(itr.first != 0)
OTF2_CHECK(OTF2_GlobalDefWriter_WriteString(
global_def_writer, itr.first, itr.second.name.c_str()));
}
for(const auto& itr : _hash_data)
{
if(itr.first != 0)
OTF2_CHECK(OTF2_GlobalDefWriter_WriteRegion(global_def_writer,
itr.first,
itr.first,
0,
0,
itr.second.region_role,
itr.second.paradigm,
OTF2_REGION_FLAG_NONE,
0,
0,
0));
}
auto add_write_string = [](size_t _hash, std::string_view _name) {
static auto _existing = std::unordered_set<size_t>{};
if(_hash > 0 && _existing.count(_hash) == 0)
{
OTF2_CHECK(OTF2_GlobalDefWriter_WriteString(global_def_writer, _hash, _name.data()));
_existing.emplace(_hash);
}
};
for(const auto& itr : _attr_str)
add_write_string(itr.first, itr.second);
std::istringstream command_line(process.command);
std::string _exe_name;
command_line >> _exe_name; // Extracts characters until whitespace
_exe_name = fmt::format("{}", _exe_name);
auto _exe_hash = get_hash_id(_exe_name);
add_write_string(_exe_hash, _exe_name);
auto _node_name = std::string{"node"};
{
if(!process.hostname.empty())
{
if(process.hostname.length() >= PATH_MAX)
{
_node_name = process.hostname.substr(0, PATH_MAX - 1);
}
else
{
_node_name = process.hostname;
}
}
}
_node_name = fmt::format("{}", _node_name);
auto _node_hash = get_hash_id(_node_name);
add_write_string(_node_hash, _node_name);
// debug
OTF2_CHECK(OTF2_GlobalDefWriter_WriteSystemTreeNode(
global_def_writer, tree_node_id, _exe_hash, _node_hash, OTF2_UNDEFINED_SYSTEM_TREE_NODE));
// Process
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocationGroup(global_def_writer,
tree_node_id,
_exe_hash,
OTF2_LOCATION_GROUP_TYPE_PROCESS,
tree_node_id,
OTF2_UNDEFINED_LOCATION_GROUP));
// Accelerators (must be shared between the processes)
for(const auto& [abs_idx, extended_agent] : agent_data)
{
auto _handle = extended_agent.types_agent.id.handle;
const auto _name = std::string_view{extended_agent.labeled_name};
auto _hash = get_hash_id(_name);
add_write_string(_hash, _name);
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocationGroup(global_def_writer,
_handle,
_hash,
OTF2_LOCATION_GROUP_TYPE_ACCELERATOR,
tree_node_id,
OTF2_UNDEFINED_LOCATION_GROUP));
}
// Thread Events
for(auto& [tid, evt] : thread_event_info)
{
auto _hash = get_hash_id(evt.name);
add_write_string(_hash, evt.name);
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocation(global_def_writer,
evt.id(), // id
_hash,
OTF2_LOCATION_TYPE_CPU_THREAD,
2 * evt.event_count, // # events
tree_node_id // location group
));
}
// Memcpy Events
for(auto& [tid, itr] : agent_memcpy_info)
{
for(auto& [agent_handle, evt] : itr)
{
auto _hash = get_hash_id(evt.name);
add_write_string(_hash, evt.name);
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocation(global_def_writer,
evt.id(), // id
_hash,
OTF2_LOCATION_TYPE_ACCELERATOR_STREAM,
2 * evt.event_count, // # events
agent_handle // location group
));
}
}
// Memalloc Events
for(auto& [tid, itr] : agent_memalloc_info)
{
for(auto& [agent_handle, evt] : itr)
{
auto _hash = get_hash_id(evt.name);
add_write_string(_hash, evt.name);
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocation(global_def_writer,
evt.id(), // id
_hash,
OTF2_LOCATION_TYPE_ACCELERATOR_STREAM,
2 * evt.event_count, // # events
agent_handle // location group
));
}
}
// Dispatch Events
for(auto& [tid, itr] : agent_dispatch_info)
{
for(auto& [agent_handle, qitr] : itr)
{
for(auto& [queue_id, evt] : qitr)
{
auto _hash = get_hash_id(evt.name);
add_write_string(_hash, evt.name);
OTF2_CHECK(OTF2_GlobalDefWriter_WriteLocation(global_def_writer,
evt.id(), // id
_hash,
OTF2_LOCATION_TYPE_ACCELERATOR_STREAM,
2 * evt.event_count, // # events
agent_handle // location group
));
}
}
}
}
} // namespace output
} // namespace rocpd
+71
파일 보기
@@ -0,0 +1,71 @@
// 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/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 <vector>
namespace rocpd
{
namespace output
{
namespace tool = rocprofiler::tool;
struct extended_agent
{
const rocpd::types::agent& types_agent;
const tool::agent_index agent_index;
const std::string labeled_name;
};
struct OTF2Session
{
OTF2Session(const tool::output_config& output_cfg, uint64_t min_start, uint64_t max_fini);
~OTF2Session();
const tool::output_config& config;
};
void
write_otf2(const OTF2Session& session,
const types::process& process,
const uint16_t tree_node_id,
const std::unordered_map<uint64_t, extended_agent>& agent_data,
const tool::generator<types::thread>& thread_gen,
const tool::generator<types::region>& api_gen,
const tool::generator<types::kernel_dispatch>& kernel_dispatch_gen,
const tool::generator<types::memory_copies>& memory_copy_gen,
const tool::generator<types::memory_allocation>& memory_allocation_gen);
} // namespace output
} // namespace rocpd
+954
파일 보기
@@ -0,0 +1,954 @@
// 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/perfetto.hpp"
#include "lib/common/defines.hpp"
#include "lib/common/hasher.hpp"
#include "lib/common/mpl.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 <fmt/format.h>
#include <atomic>
#include <future>
#include <mutex>
#include <vector>
namespace rocpd
{
namespace output
{
namespace
{
auto
to_string(rocprofiler_dim3_t v)
{
return fmt::format("{} (x={}, y={}, z={})", (v.x * v.y * v.z), v.x, v.y, v.z);
}
template <typename Tp>
size_t
get_hash_id(Tp&& _val)
{
using value_type = rocprofiler::common::mpl::unqualified_type_t<Tp>;
if constexpr(!std::is_pointer<Tp>::value)
return std::hash<value_type>{}(std::forward<Tp>(_val));
else if constexpr(std::is_same<Tp, const char*>::value)
return get_hash_id(std::string_view{_val});
else
return get_hash_id(*_val);
}
} // namespace
PerfettoSession::PerfettoSession(const tool::output_config& output_cfg)
: config{output_cfg}
{
auto args = ::perfetto::TracingInitArgs{};
auto track_event_cfg = ::perfetto::protos::gen::TrackEventConfig{};
auto cfg = ::perfetto::TraceConfig{};
// environment settings
auto shmem_size_hint = config.perfetto_shmem_size_hint;
auto buffer_size_kb = config.perfetto_buffer_size;
auto* buffer_config = cfg.add_buffers();
buffer_config->set_size_kb(buffer_size_kb);
args.supports_multiple_data_source_instances = true;
// track_event_cfg.clear_disabled_categories();
// track_event_cfg.clear_disabled_tags();
if(config.perfetto_buffer_fill_policy == "discard" ||
config.perfetto_buffer_fill_policy.empty())
buffer_config->set_fill_policy(
::perfetto::protos::gen::TraceConfig_BufferConfig_FillPolicy_DISCARD);
else if(config.perfetto_buffer_fill_policy == "ring_buffer")
buffer_config->set_fill_policy(
::perfetto::protos::gen::TraceConfig_BufferConfig_FillPolicy_RING_BUFFER);
else
ROCP_FATAL << "Unsupport perfetto buffer fill policy: '"
<< config.perfetto_buffer_fill_policy << "'. Supported: discard, ring_buffer";
auto* ds_cfg = cfg.add_data_sources()->mutable_config();
ds_cfg->set_name("track_event"); // this MUST be track_event
ds_cfg->set_track_event_config_raw(track_event_cfg.SerializeAsString());
args.shmem_size_hint_kb = shmem_size_hint;
if(config.perfetto_backend == "inprocess" || config.perfetto_backend.empty())
args.backends |= ::perfetto::kInProcessBackend;
else if(config.perfetto_backend == "system")
args.backends |= ::perfetto::kSystemBackend;
else
ROCP_FATAL << "Unsupport perfetto backend: '" << config.perfetto_backend
<< "'. Supported: inprocess, system";
::perfetto::Tracing::Initialize(args);
::perfetto::TrackEvent::Register();
tracing_session = ::perfetto::Tracing::NewTrace();
tracing_session->Setup(cfg);
tracing_session->StartBlocking();
}
PerfettoSession::~PerfettoSession()
{
tracing_session->StopBlocking();
auto filename = std::string{"results"};
auto ofs = tool::get_output_stream(config, filename, ".pftrace", std::ios::binary);
auto amount_read = std::atomic<size_t>{0};
auto is_done = std::promise<void>{};
auto _mtx = std::mutex{};
auto _reader = [&ofs, &_mtx, &is_done, &amount_read](
::perfetto::TracingSession::ReadTraceCallbackArgs _args) {
auto _lk = std::unique_lock<std::mutex>{_mtx};
if(_args.data && _args.size > 0)
{
ROCP_TRACE << "Writing " << _args.size << " B to trace...";
// Write the trace data into file
ofs.stream->write(_args.data, _args.size);
amount_read += _args.size;
}
ROCP_INFO_IF(!_args.has_more && amount_read > 0)
<< "Wrote " << amount_read << " B to perfetto trace file";
if(!_args.has_more) is_done.set_value();
};
for(size_t i = 0; i < 2; ++i)
{
ROCP_TRACE << "Reading trace...";
amount_read = 0;
is_done = std::promise<void>{};
tracing_session->ReadTrace(_reader);
is_done.get_future().wait();
}
ROCP_TRACE << "Destroying tracing session...";
tracing_session.reset();
ROCP_TRACE << "Flushing trace output stream...";
(*ofs.stream) << std::flush;
ROCP_TRACE << "Destroying trace output stream...";
ofs.close();
}
void
write_perfetto(
const PerfettoSession& perfetto_session,
const types::process& process,
const std::unordered_map<uint64_t, std::pair<rocpd::types::agent, tool::agent_index>>&
agent_data,
const tool::generator<types::thread>& thread_gen,
const tool::generator<types::region>& region_gen,
const tool::generator<types::sample>& sample_gen,
const tool::generator<types::kernel_dispatch>& kernel_dispatch_gen,
const tool::generator<types::memory_copies>& memory_copy_gen,
const tool::generator<types::memory_allocation>& memory_allocation_gen,
const tool::generator<types::counter>& counter_collection_gen)
{
namespace sdk = ::rocprofiler::sdk;
namespace common = ::rocprofiler::common;
static auto orig_process_track = ::perfetto::ProcessTrack::Current();
static auto orig_process_desc = orig_process_track.Serialize();
const auto& tracing_session = perfetto_session.tracing_session;
const auto& ocfg = perfetto_session.config;
const uint64_t this_pid = process.pid;
const uint64_t this_pid_init_ns = process.init;
const uint64_t this_nid = process.nid;
auto command_line = ::rocprofiler::sdk::parse::tokenize(process.command, " ");
auto uuid_pid = common::fnv1a_hasher::combine(this_nid, this_pid_init_ns, this_pid);
auto this_pid_track = ::perfetto::Track{uuid_pid, ::perfetto::Track{}};
{
auto desc = orig_process_desc;
desc.set_uuid(uuid_pid);
desc.set_parent_uuid(0);
desc.mutable_process()->set_pid(this_pid);
desc.mutable_process()->set_start_timestamp_ns(this_pid_init_ns);
desc.mutable_process()->set_process_name(command_line.front());
desc.mutable_process()->clear_cmdline();
for(const auto& itr : command_line)
desc.mutable_process()->add_cmdline(itr);
::perfetto::TrackEvent::SetTrackDescriptor(this_pid_track, desc);
}
auto agent_thread_ids = std::unordered_map<uint64_t, std::set<uint64_t>>{};
auto agent_thread_ids_alloc = std::unordered_map<uint64_t, std::set<uint64_t>>{};
auto agent_queue_ids =
std::unordered_map<uint64_t, std::unordered_set<rocprofiler_queue_id_t>>{};
auto agent_stream_ids = std::unordered_set<rocprofiler_stream_id_t>{};
auto thread_indexes = std::unordered_map<uint64_t, uint64_t>{};
auto thread_tracks = std::unordered_map<uint64_t, ::perfetto::Track>{};
auto agent_thread_tracks =
std::unordered_map<uint64_t, std::unordered_map<uint64_t, ::perfetto::Track>>{};
auto agent_queue_tracks =
std::unordered_map<uint64_t,
std::unordered_map<rocprofiler_queue_id_t, ::perfetto::Track>>{};
auto stream_tracks = std::unordered_map<rocprofiler_stream_id_t, ::perfetto::Track>{};
{
for(auto ditr : memory_copy_gen)
for(const auto& itr : memory_copy_gen.get(ditr))
{
auto stream_id = rocprofiler_stream_id_t{.handle = itr.stream_id};
agent_stream_ids.emplace(stream_id);
if(ocfg.group_by_queue)
{
agent_thread_ids[itr.dst_agent_abs_index].emplace(itr.tid);
}
}
}
for(auto ditr : memory_allocation_gen)
for(const auto& itr : memory_allocation_gen.get(ditr))
{
agent_thread_ids_alloc[itr.agent_abs_index].emplace(itr.tid);
}
{
for(auto ditr : kernel_dispatch_gen)
for(const auto& itr : kernel_dispatch_gen.get(ditr))
{
auto stream_id = rocprofiler_stream_id_t{.handle = itr.stream_id};
auto queue_id = rocprofiler_queue_id_t{.handle = itr.queue_id};
agent_stream_ids.emplace(stream_id);
if(ocfg.group_by_queue)
{
agent_queue_ids[itr.agent_abs_index].emplace(queue_id);
}
}
}
uint64_t nthrn = 0;
for(auto ditr : thread_gen)
for(const auto& itr : thread_gen.get(ditr))
{
auto is_main_thread = (static_cast<uint64_t>(itr.tid) == this_pid);
auto _idx = (is_main_thread) ? 0 : ++nthrn;
thread_indexes.emplace(itr.tid, _idx);
auto _track = ::perfetto::Track{static_cast<uint64_t>(itr.tid), this_pid_track};
auto _desc = _track.Serialize();
if(is_main_thread)
_desc.set_name(fmt::format("{}", ::basename(command_line.front().c_str())));
else
_desc.set_name(fmt::format("THREAD {}", _idx));
_desc.mutable_thread()->set_pid(this_pid);
_desc.mutable_thread()->set_tid(itr.tid);
if(is_main_thread)
_desc.mutable_thread()->set_thread_name(
fmt::format("{}", ::basename(command_line.front().c_str())));
else
_desc.mutable_thread()->set_thread_name(fmt::format("THREAD {}", _idx));
::perfetto::TrackEvent::SetTrackDescriptor(_track, _desc);
thread_tracks.emplace(itr.tid, _track);
}
for(const auto& [abs_index, thread_ids] : agent_thread_ids)
{
const auto _agent = agent_data.at(abs_index).first;
for(auto titr : thread_ids)
{
auto _namess = std::stringstream{};
_namess << "COPY to AGENT [" << _agent.logical_node_id << "] THREAD ["
<< thread_indexes.at(titr) << "] ";
if(_agent.type == "CPU")
_namess << "(CPU)";
else if(_agent.type == "GPU")
_namess << "(GPU)";
else
_namess << "(UNK)";
auto _track = ::perfetto::Track{get_hash_id(_namess.str()), this_pid_track};
auto _desc = _track.Serialize();
_desc.set_name(_namess.str());
perfetto::TrackEvent::SetTrackDescriptor(_track, _desc);
agent_thread_tracks[abs_index].emplace(titr, _track);
}
}
for(const auto& [abs_index, queue_ids] : agent_queue_ids)
{
uint32_t nqueue = 0;
const auto _agent = agent_data.at(abs_index).first;
auto agent_index_info = agent_data.at(abs_index).second;
for(auto qitr : queue_ids)
{
auto _namess = std::stringstream{};
_namess << "COMPUTE " << agent_index_info.label << " [" << agent_index_info.index
<< "] QUEUE [" << nqueue++ << "] ";
_namess << agent_index_info.type;
auto _track = ::perfetto::Track{get_hash_id(_namess.str()), this_pid_track};
auto _desc = _track.Serialize();
_desc.set_name(_namess.str());
::perfetto::TrackEvent::SetTrackDescriptor(_track, _desc);
agent_queue_tracks[abs_index].emplace(qitr, _track);
}
}
for(const auto& sitr : agent_stream_ids)
{
const auto stream_id = sitr.handle;
auto _name = fmt::format("STREAM [{}]", stream_id);
auto _track = ::perfetto::Track{get_hash_id(_name), this_pid_track};
auto _desc = _track.Serialize();
_desc.set_name(_name);
::perfetto::TrackEvent::SetTrackDescriptor(_track, _desc);
stream_tracks.emplace(sitr, _track);
}
// Fetch counter values
auto counter_id_value = std::map<uint32_t, double>{};
auto counter_id_name = std::map<uint32_t, std::string>{};
for(auto ditr : counter_collection_gen)
for(const auto& record : counter_collection_gen.get(ditr))
{
// Accumulate counters based on ID
counter_id_value[record.counter_id] += record.value;
counter_id_name[record.counter_id] = std::string{record.counter_name};
}
// trace events
{
auto get_category_string = [](std::string_view _category) {
static auto buffer_names = sdk::get_buffer_tracing_names();
auto _category_idx = ROCPROFILER_BUFFER_TRACING_NONE;
for(const auto& citr : buffer_names)
{
if(_category == citr.name) _category_idx = citr.value;
}
return sdk::get_perfetto_category(_category_idx);
};
for(auto ditr : region_gen)
{
for(auto itr : region_gen.get(ditr))
{
auto& track = thread_tracks.at(itr.tid);
auto _name = itr.name;
if(itr.has_extdata())
{
if(auto _extdata = itr.get_extdata(); !_extdata.message.empty())
_name = _extdata.message;
}
auto _category = ::perfetto::DynamicCategory{get_category_string(itr.category)};
TRACE_EVENT_BEGIN(_category,
::perfetto::DynamicString{_name},
track,
itr.start,
::perfetto::Flow::Global(itr.stack_id ^ uuid_pid),
"begin_ns",
itr.start,
"end_ns",
itr.end,
"delta_ns",
(itr.end - itr.start),
"tid",
itr.tid,
"kind",
itr.category,
"operation",
_name,
"corr_id",
itr.stack_id,
"ancestor_id",
itr.parent_stack_id,
[&](::perfetto::EventContext ctx) { (void) ctx; });
TRACE_EVENT_END(_category, track, itr.end);
tracing_session->FlushBlocking();
}
}
for(auto ditr : sample_gen)
{
for(auto itr : sample_gen.get(ditr))
{
auto& track = thread_tracks.at(itr.tid);
auto _name = itr.name;
if(itr.has_extdata())
{
if(auto _extdata = itr.get_extdata(); !_extdata.message.empty())
_name = _extdata.message;
}
auto _category = ::perfetto::DynamicCategory{get_category_string(itr.category)};
TRACE_EVENT_INSTANT(_category,
::perfetto::DynamicString{_name},
track,
itr.timestamp,
::perfetto::Flow::Global(itr.stack_id ^ uuid_pid),
"begin_ns",
itr.timestamp,
"end_ns",
itr.timestamp,
"delta_ns",
0,
"tid",
itr.tid,
"kind",
itr.category,
"operation",
_name,
"corr_id",
itr.stack_id,
"ancestor_id",
itr.parent_stack_id,
[&](::perfetto::EventContext ctx) { (void) ctx; });
tracing_session->FlushBlocking();
}
}
for(auto ditr : memory_copy_gen)
{
for(auto itr : memory_copy_gen.get(ditr))
{
::perfetto::Track* _track = nullptr;
if(ocfg.group_by_queue)
{
_track = &agent_thread_tracks.at(itr.dst_agent_abs_index).at(itr.tid);
}
else
{
auto stream_id = rocprofiler_stream_id_t{.handle = itr.stream_id};
_track = &stream_tracks.at(stream_id);
}
auto src_agent_index = agent_data.at(itr.src_agent_abs_index).second;
auto dst_agent_index = agent_data.at(itr.dst_agent_abs_index).second;
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::memory_copy>::name,
::perfetto::DynamicString{itr.name},
*_track,
itr.start,
::perfetto::Flow::Global(itr.stack_id ^ uuid_pid),
"begin_ns",
itr.start,
"end_ns",
itr.end,
"delta_ns",
(itr.end - itr.start),
"kind",
itr.category,
"operation",
itr.name,
"src_agent",
src_agent_index.as_string("-"),
"dst_agent",
dst_agent_index.as_string("-"),
"copy_bytes",
itr.size,
"corr_id",
itr.stack_id,
"tid",
itr.tid,
"stream_id",
itr.stream_id);
TRACE_EVENT_END(
sdk::perfetto_category<sdk::category::memory_copy>::name, *_track, itr.end);
}
tracing_session->FlushBlocking();
}
for(auto ditr : kernel_dispatch_gen)
{
auto gen = kernel_dispatch_gen.get(ditr);
for(auto it = begin(gen); it != end(gen); ++it)
{
auto& current = *it;
::perfetto::Track* _track = nullptr;
auto agent_id = current.agent_abs_index;
auto queue_id = rocprofiler_queue_id_t{.handle = current.queue_id};
auto stream_id = rocprofiler_stream_id_t{.handle = current.stream_id};
if(ocfg.group_by_queue)
{
_track = &agent_queue_tracks.at(agent_id).at(queue_id);
}
else
{
_track = &stream_tracks.at(stream_id);
}
// Temporary fix until timestamp issues are resolved: Set timestamps to be
// halfway between ending timestamp and starting timestamp of overlapping
// kernel dispatches. Perfetto displays slices incorrectly if overlapping
// slices on the same track are not completely enveloped.
auto next = std::next(it);
if(next != end(gen) && next->agent_abs_index == it->agent_abs_index &&
((ocfg.group_by_queue && next->queue_id == it->queue_id) ||
(!ocfg.group_by_queue && next->stream_id == it->stream_id)) &&
next->start < it->end)
{
auto start = next->start;
auto end = it->end;
auto mid = start + (end - start) / 2;
// Report changed timestamps to ROCP INFO
ROCP_INFO << fmt::format(
"Kernel ending timestamp increased by {} ns to {} ns with "
"following kernel starting timestamp decreased by {} ns to {} ns "
"due to firmware timestamp error.",
(it->end - mid),
mid,
(mid - next->start),
mid);
it->end = mid;
next->start = mid;
}
auto agent_index = agent_data.at(current.agent_abs_index).second;
auto _name =
(ocfg.kernel_rename && !current.region.empty()) ? current.region : current.name;
TRACE_EVENT_BEGIN(sdk::perfetto_category<sdk::category::kernel_dispatch>::name,
::perfetto::DynamicString{_name},
*_track,
current.start,
::perfetto::Flow::Global(current.stack_id ^ uuid_pid),
"begin_ns",
current.start,
"end_ns",
current.end,
"delta_ns",
(current.end - current.start),
"kind",
current.category,
"agent",
agent_index.as_string("-"),
"corr_id",
current.stack_id,
"queue",
current.queue_id,
"tid",
current.tid,
"kernel_id",
current.kernel_id,
"Scratch_Size",
current.scratch_size,
"LDS_Block_Size",
current.lds_size,
"workgroup_size",
to_string(current.workgroup_size),
"grid_size",
to_string(current.grid_size),
"stream_id",
current.stream_id,
[&](::perfetto::EventContext ctx) {
for(auto& [counter_id, counter_value] : counter_id_value)
{
rocprofiler::sdk::add_perfetto_annotation(
ctx, counter_id_name.at(counter_id), counter_value);
}
});
TRACE_EVENT_END(sdk::perfetto_category<sdk::category::kernel_dispatch>::name,
*_track,
current.end);
}
tracing_session->FlushBlocking();
}
}
// counter tracks
{
// memory copy counter track
auto mem_cpy_endpoints = std::map<uint64_t, std::map<rocprofiler_timestamp_t, uint64_t>>{};
auto mem_cpy_extremes = std::pair<uint64_t, uint64_t>{std::numeric_limits<uint64_t>::max(),
std::numeric_limits<uint64_t>::min()};
auto constexpr timestamp_buffer = 1000;
for(auto ditr : memory_copy_gen)
{
for(const auto& itr : memory_copy_gen.get(ditr))
{
uint64_t _mean_timestamp = itr.start + (0.5 * (itr.end - itr.start));
mem_cpy_endpoints[itr.dst_agent_abs_index].emplace(itr.start - timestamp_buffer, 0);
mem_cpy_endpoints[itr.dst_agent_abs_index].emplace(itr.start, 0);
mem_cpy_endpoints[itr.dst_agent_abs_index].emplace(_mean_timestamp, 0);
mem_cpy_endpoints[itr.dst_agent_abs_index].emplace(itr.end, 0);
mem_cpy_endpoints[itr.dst_agent_abs_index].emplace(itr.end + timestamp_buffer, 0);
mem_cpy_extremes = std::make_pair(std::min(mem_cpy_extremes.first, itr.start),
std::max(mem_cpy_extremes.second, itr.end));
}
}
for(auto ditr : memory_copy_gen)
{
for(const auto& itr : memory_copy_gen.get(ditr))
{
auto mbeg = mem_cpy_endpoints.at(itr.dst_agent_abs_index).lower_bound(itr.start);
auto mend = mem_cpy_endpoints.at(itr.dst_agent_abs_index).upper_bound(itr.end);
LOG_IF(FATAL, mbeg == mend)
<< "Missing range for timestamp [" << itr.start << ", " << itr.end << "]";
for(auto mitr = mbeg; mitr != mend; ++mitr)
mitr->second += itr.size;
}
}
constexpr auto bytes_multiplier = 1024;
constexpr auto extremes_endpoint_buffer = 5000;
auto mem_cpy_tracks = std::unordered_map<uint64_t, ::perfetto::CounterTrack>{};
auto mem_cpy_cnt_names = std::vector<std::string>{};
mem_cpy_cnt_names.reserve(mem_cpy_endpoints.size());
for(auto& [abs_index, ts_map] : mem_cpy_endpoints)
{
mem_cpy_endpoints[abs_index].emplace(mem_cpy_extremes.first - extremes_endpoint_buffer,
0);
mem_cpy_endpoints[abs_index].emplace(mem_cpy_extremes.second + extremes_endpoint_buffer,
0);
auto _track_name = std::stringstream{};
const auto _agent = agent_data.at(abs_index).first;
auto agent_index_info = agent_data.at(abs_index).second;
_track_name << "COPY BYTES to " << agent_index_info.label << " ["
<< agent_index_info.index << "] (" << agent_index_info.type << ")";
constexpr auto _unit = ::perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES;
auto& _name = mem_cpy_cnt_names.emplace_back(_track_name.str());
mem_cpy_tracks.emplace(abs_index,
::perfetto::CounterTrack{_name.c_str(), this_pid_track}
.set_unit(_unit)
.set_unit_multiplier(bytes_multiplier)
.set_is_incremental(false));
}
for(auto& mitr : mem_cpy_endpoints)
{
for(auto itr : mitr.second)
{
TRACE_COUNTER(sdk::perfetto_category<sdk::category::memory_copy>::name,
mem_cpy_tracks.at(mitr.first),
itr.first,
itr.second / bytes_multiplier);
}
tracing_session->FlushBlocking();
}
// memory allocation counter track
struct free_memory_information
{
rocprofiler_timestamp_t start_timestamp = 0;
rocprofiler_timestamp_t end_timestamp = 0;
rocprofiler_address_t address = {.handle = 0};
};
struct memory_information
{
uint64_t alloc_size = {0};
rocprofiler_address_t address = {.handle = 0};
bool is_alloc_op = {false};
};
struct agent_and_size
{
uint64_t agent_abs_index = {};
uint64_t size = {0};
};
auto mem_alloc_endpoints =
std::unordered_map<uint64_t, std::map<rocprofiler_timestamp_t, memory_information>>{};
auto mem_alloc_extremes = std::pair<uint64_t, uint64_t>{
std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::min()};
auto address_to_agent_and_size =
std::unordered_map<rocprofiler_address_t, agent_and_size>{};
auto free_mem_info = std::vector<free_memory_information>{};
// Load memory allocation endpoints
for(auto ditr : memory_allocation_gen)
{
for(const auto& itr : memory_allocation_gen.get(ditr))
{
if(itr.type == "ALLOC")
{
LOG_IF(FATAL, itr.agent_name.empty())
<< "Missing agent id for memory allocation trace";
mem_alloc_endpoints[itr.agent_abs_index].emplace(
itr.start,
memory_information{
itr.size, rocprofiler_address_t{.handle = itr.address}, true});
mem_alloc_endpoints[itr.agent_abs_index].emplace(
itr.end,
memory_information{
itr.size, rocprofiler_address_t{.handle = itr.address}, true});
address_to_agent_and_size.emplace(
rocprofiler_address_t{.handle = itr.address},
agent_and_size{itr.agent_abs_index, itr.size});
}
else if(itr.type == "FREE")
{
// Store free memory operations in seperate vector to pair with agent
// and allocation size in following loop
free_mem_info.push_back(free_memory_information{
itr.start, itr.end, rocprofiler_address_t{.handle = itr.address}});
}
else
{
ROCP_CI_LOG(WARNING) << "unhandled memory allocation type " << itr.type;
}
}
}
// Add free memory operations to the endpoint map
for(const auto& itr : free_mem_info)
{
if(address_to_agent_and_size.count(itr.address) == 0)
{
if(itr.address.handle == 0)
{
// Freeing null pointers is expected behavior and is occurs in HSA functions
// like hipStreamDestroy
ROCP_INFO << "null pointer freed due to HSA operation";
}
else
{
// Following should not occur
ROCP_INFO << "Unpaired free operation occurred";
}
continue;
}
auto [agent_abs_index, size] = address_to_agent_and_size[itr.address];
mem_alloc_endpoints[agent_abs_index].emplace(
itr.start_timestamp, memory_information{size, itr.address, false});
mem_alloc_endpoints[agent_abs_index].emplace(
itr.end_timestamp, memory_information{size, itr.address, false});
}
// Create running sum of allocated memory
for(auto& [_, endpoint_map] : mem_alloc_endpoints)
{
if(!endpoint_map.empty())
{
auto earliest_agent_timestamp = endpoint_map.begin()->first;
auto latest_agent_timestamp = (--endpoint_map.end())->first;
mem_alloc_extremes =
std::make_pair(std::min(mem_alloc_extremes.first, earliest_agent_timestamp),
std::max(mem_alloc_extremes.second, latest_agent_timestamp));
}
if(endpoint_map.size() <= 1)
{
continue;
}
auto prev = endpoint_map.begin();
auto itr = std::next(prev);
for(; itr != endpoint_map.end(); ++itr, ++prev)
{
// If address or allocation type are different, add or subtract from running sum
if(prev->second.address != itr->second.address ||
prev->second.is_alloc_op != itr->second.is_alloc_op)
{
if(itr->second.is_alloc_op)
{
itr->second.alloc_size += prev->second.alloc_size;
}
else if(prev->second.alloc_size >= itr->second.alloc_size)
{
itr->second.alloc_size = prev->second.alloc_size - itr->second.alloc_size;
}
}
else
{
itr->second.alloc_size = prev->second.alloc_size;
}
}
}
auto mem_alloc_tracks = std::unordered_map<uint64_t, ::perfetto::CounterTrack>{};
auto mem_alloc_cnt_names = std::vector<std::string>{};
mem_alloc_cnt_names.reserve(mem_alloc_endpoints.size());
for(auto& [abs_index, ts_map] : mem_alloc_endpoints)
{
mem_alloc_endpoints[abs_index].emplace(
mem_alloc_extremes.first - extremes_endpoint_buffer,
memory_information{0, {0}, false});
mem_alloc_endpoints[abs_index].emplace(
mem_alloc_extremes.second + extremes_endpoint_buffer,
memory_information{0, {0}, false});
auto _track_name = std::stringstream{};
if(agent_data.find(abs_index) != agent_data.end())
{
const auto _agent = agent_data.at(abs_index).first;
auto agent_index_info = agent_data.at(abs_index).second;
_track_name << "ALLOCATE BYTES on " << agent_index_info.label << " ["
<< agent_index_info.index << "] (" << agent_index_info.type << ")";
}
else
{
_track_name << "FREE BYTES";
}
constexpr auto _unit = ::perfetto::CounterTrack::Unit::UNIT_SIZE_BYTES;
auto& _name = mem_alloc_cnt_names.emplace_back(_track_name.str());
mem_alloc_tracks.emplace(abs_index,
::perfetto::CounterTrack{_name.c_str(), this_pid_track}
.set_unit(_unit)
.set_unit_multiplier(bytes_multiplier)
.set_is_incremental(false));
}
for(auto& alloc_itr : mem_alloc_endpoints)
{
for(auto itr : alloc_itr.second)
{
TRACE_COUNTER(sdk::perfetto_category<sdk::category::memory_allocation>::name,
mem_alloc_tracks.at(alloc_itr.first),
itr.first,
itr.second.alloc_size / bytes_multiplier);
}
}
tracing_session->FlushBlocking();
}
// Create counter tracks per agent
{
auto counters_endpoints =
std::unordered_map<uint64_t,
std::unordered_map<uint32_t, std::map<uint64_t, uint64_t>>>{};
auto counters_extremes = std::pair<uint64_t, uint64_t>{
std::numeric_limits<uint64_t>::max(), std::numeric_limits<uint64_t>::min()};
auto constexpr timestamp_buffer = 1000;
for(auto ditr : counter_collection_gen)
for(const auto& record : counter_collection_gen.get(ditr))
{
// const auto& info = record.;
const auto& start_timestamp = record.start;
const auto& end_timestamp = record.end;
uint64_t _mean_timestamp =
start_timestamp + (0.5 * (end_timestamp - start_timestamp));
for(auto& [counter_id, counter_value] : counter_id_value)
{
counters_endpoints[record.agent_abs_index][counter_id].emplace(
start_timestamp - timestamp_buffer, 0);
counters_endpoints[record.agent_abs_index][counter_id].emplace(start_timestamp,
counter_value);
counters_endpoints[record.agent_abs_index][counter_id].emplace(_mean_timestamp,
counter_value);
counters_endpoints[record.agent_abs_index][counter_id].emplace(end_timestamp,
0);
counters_endpoints[record.agent_abs_index][counter_id].emplace(
end_timestamp + timestamp_buffer, 0);
}
counters_extremes = std::make_pair(std::min(counters_extremes.first, record.start),
std::max(counters_extremes.second, record.end));
}
auto counter_tracks =
std::unordered_map<uint64_t, std::map<std::string, ::perfetto::CounterTrack>>{};
constexpr auto extremes_endpoint_buffer = 5000;
for(auto ditr : counter_collection_gen)
{
for(const auto& record : counter_collection_gen.get(ditr))
{
// const auto& info = record.dispatch_data.dispatch_info;
// const auto& sym = tool_metadata.get_kernel_symbol(info.kernel_id);
// CHECK(sym != nullptr);
auto name = record.kernel_name;
for(auto& [counter_id, counter_value] : counter_id_value)
{
counters_endpoints[record.agent_id][counter_id].emplace(
counters_extremes.first - extremes_endpoint_buffer, 0);
counters_endpoints[record.agent_id][counter_id].emplace(
counters_extremes.second + extremes_endpoint_buffer, 0);
const auto _agent = agent_data.at(record.agent_abs_index).first;
auto agent_index_info = agent_data.at(record.agent_abs_index).second;
auto track_name_ss = std::stringstream{};
track_name_ss << agent_index_info.label << " [" << agent_index_info.index
<< "] "
<< "PMC " << record.counter_name;
auto track_name = track_name_ss.str();
counter_tracks[record.agent_abs_index].emplace(
track_name, ::perfetto::CounterTrack{track_name.c_str(), this_pid_track});
auto& endpoints = counters_endpoints[record.agent_id][counter_id];
for(auto& counter_itr : endpoints)
{
TRACE_COUNTER(
sdk::perfetto_category<sdk::category::counter_collection>::name,
counter_tracks[record.agent_abs_index].at(track_name),
counter_itr.first,
counter_itr.second);
}
}
}
tracing_session->FlushBlocking();
}
}
::perfetto::TrackEvent::Flush();
tracing_session->FlushBlocking();
}
} // namespace output
} // namespace rocpd
+69
파일 보기
@@ -0,0 +1,69 @@
// 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/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 <rocprofiler-sdk/cxx/perfetto.hpp>
#include <vector>
namespace rocpd
{
namespace output
{
namespace tool = ::rocprofiler::tool;
struct PerfettoSession
{
PerfettoSession(const tool::output_config&);
~PerfettoSession();
std::unique_ptr<::perfetto::TracingSession> tracing_session = {};
const tool::output_config& config;
};
void
write_perfetto(
const PerfettoSession& perfetto_session,
const types::process& process,
const std::unordered_map<uint64_t, std::pair<rocpd::types::agent, tool::agent_index>>&
agent_data,
const tool::generator<types::thread>& thread_gen,
const tool::generator<types::region>& region_gen,
const tool::generator<types::sample>& sample_gen,
const tool::generator<types::kernel_dispatch>& kernel_dispatch_gen,
const tool::generator<types::memory_copies>& memory_copy_gen,
const tool::generator<types::memory_allocation>& memory_allocation_gen,
const tool::generator<types::counter>& counter_collection_gen);
} // namespace output
} // namespace rocpd
+114
파일 보기
@@ -0,0 +1,114 @@
// MIT License
//
// Copyright (c) 2022 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/common/defines.hpp"
#include <pybind11/cast.h>
#include <pybind11/embed.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <pybind11/pytypes.h>
#include <cstdint>
extern "C" {
struct pysqlite_state;
struct sqlite3;
enum autocommit_mode
{
AUTOCOMMIT_LEGACY = -1,
AUTOCOMMIT_ENABLED = 1,
AUTOCOMMIT_DISABLED = 0,
};
typedef struct _callback_context
{
PyObject* callable;
PyObject* module;
pysqlite_state* state;
} callback_context;
// this is the python sqlite3 wrapper from Python 3.12.2
typedef struct
{
PyObject_HEAD;
sqlite3* db;
pysqlite_state* state;
/* the type detection mode. Only 0, PARSE_DECLTYPES, PARSE_COLNAMES or a
* bitwise combination thereof makes sense */
int detect_types;
/* NULL for autocommit, otherwise a string with the isolation level */
const char* isolation_level;
enum autocommit_mode autocommit;
/* 1 if a check should be performed for each API call if the connection is
* used from the same thread it was created in */
int check_same_thread;
int initialized;
/* thread identification of the thread the connection was created in */
unsigned long thread_ident;
PyObject* statement_cache;
/* Lists of weak references to cursors and blobs used within this connection */
PyObject* cursors;
PyObject* blobs;
/* Counters for how many cursors were created in the connection. May be
* reset to 0 at certain intervals */
int created_cursors;
PyObject* row_factory;
/* Determines how bytestrings from SQLite are converted to Python objects:
* - PyUnicode_Type: Python Unicode objects are constructed from UTF-8 bytestrings
* - PyBytes_Type: The bytestrings are returned as-is.
* - Any custom callable: Any object returned from the callable called with the bytestring
* as single parameter.
*/
PyObject* text_factory;
// Remember contexts used by the trace, progress, and authoriser callbacks
callback_context* trace_ctx;
callback_context* progress_ctx;
callback_context* authorizer_ctx;
/* Exception objects: borrowed refs. */
PyObject* Warning;
PyObject* Error;
PyObject* InterfaceError;
PyObject* DatabaseError;
PyObject* DataError;
PyObject* OperationalError;
PyObject* IntegrityError;
PyObject* InternalError;
PyObject* ProgrammingError;
PyObject* NotSupportedError;
} pysqlite_Connection;
}
@@ -0,0 +1,11 @@
#
# add sql common sources to output library target
#
set(libpyrocpd_source_serialization_sources sql.hpp)
set(libpyrocpd_source_serialization_headers sql.cpp)
foreach(_PYTHON_VERSION ${ROCPROFILER_PYTHON_VERSIONS})
rocprofiler_rocpd_python_bindings_target_sources(
${_PYTHON_VERSION} PRIVATE ${libpyrocpd_source_serialization_sources}
${libpyrocpd_source_serialization_headers})
endforeach()
+166
파일 보기
@@ -0,0 +1,166 @@
// 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/serialization/sql.hpp"
#include "lib/output/sql/common.hpp"
namespace cereal
{
SQLite3InputArchive::SQLite3InputArchive(sqlite3* conn,
std::string_view query,
int64_t len,
int64_t chunk_len)
: InputArchive<SQLite3InputArchive>{this}
, m_conn{conn}
, m_row_count{(len > 0) ? len : getRowCount(conn, query)}
, m_chunk_size{chunk_len}
, m_query{query}
, m_iterator{this}
{
ROCP_CI_LOG_IF(ERROR,
sqlite3_prepare_v2(m_conn, m_query.c_str(), -1, &m_stmt, nullptr) != SQLITE_OK)
<< "Error preparing select statement: " << sqlite3_errmsg(m_conn);
ROCP_CI_LOG_IF(ERROR, m_stmt == nullptr) << "Error preparing statment: " << query;
if(!m_stmt) return;
auto col_count = sqlite3_column_count(m_stmt);
ROCP_TRACE << " sql query: " << query;
ROCP_TRACE << " - col_count: " << col_count;
m_column_count = sqlite3_column_count(m_stmt);
for(int64_t i = 0; i < m_column_count; ++i)
{
auto name = impl::extract_column_name(m_stmt, i);
m_column_names.emplace(name, i);
ROCP_TRACE << " - column " << i << ": " << name;
}
if(m_chunk_size > 0)
{
auto _num_chunks = (m_row_count / m_chunk_size);
auto _chunk_modulo = (m_row_count % m_chunk_size);
m_sizes.resize(_num_chunks, m_chunk_size);
if(_chunk_modulo > 0) m_sizes.emplace_back(_chunk_modulo);
}
}
void
SQLite3InputArchive::set_chunk_index(size_t idx)
{
ROCP_FATAL_IF(idx >= m_sizes.size()) << fmt::format(
"Invalid chunk index {} (>= {}) for query '{}'", idx, m_sizes.size(), m_query);
ROCP_TRACE << fmt::format("Setting chunk index to {}. Current index is {}", idx, m_size_idx);
if(idx != m_size_idx)
{
auto _status = sqlite3_reset(m_stmt);
ROCP_FATAL_IF(_status != SQLITE_OK)
<< fmt::format("sqlite3_reset failed for statement '{}'", m_query);
for(size_t i = 0; i < (idx * m_chunk_size); ++i)
++m_iterator;
m_size_idx = idx;
}
}
void
SQLite3InputArchive::loadBinaryValue(void* data, size_t size, const char* name)
{
m_next_name = name;
std::string encoded;
loadValue(encoded);
auto decoded = base64::decode(encoded);
if(size != decoded.size())
throw Exception("Decoded binary data size does not match specified size");
std::memcpy(data, decoded.data(), decoded.size());
m_next_name = nullptr;
};
void
SQLite3InputArchive::loadSize(size_type& size) const
{
if(m_sizes.empty())
size = m_row_count;
else if(m_size_idx >= m_sizes.size())
size = 0;
else
size = m_sizes.at(m_size_idx++);
// ROCP_WARNING << fmt::format(
// "[SQLite3InputArchive] counted={}, chunk_size={}, size={}", m_counted, m_chunk_size,
// size);
}
int64_t
SQLite3InputArchive::search(std::string_view _col_name)
{
auto itr = m_column_names.find(_col_name.data());
if(itr == m_column_names.end())
{
auto _names = std::vector<std::string_view>{};
for(const auto& citr : m_column_names)
_names.emplace_back(citr.first);
auto _msg = fmt::format("SQL query '{}' does not contain a column named '{}'. Columns: {}",
m_query,
m_next_name,
fmt::join(_names.begin(), _names.end(), ", "));
ROCP_WARNING << _msg;
throw Exception(_msg);
}
return itr->second;
}
int64_t
SQLite3InputArchive::search(std::string_view _col_name, std::nothrow_t)
{
auto itr = m_column_names.find(_col_name.data());
if(itr == m_column_names.end())
{
ROCP_WARNING << fmt::format(
"SQL query '{}' does not contain a column named '{}'", m_query, m_next_name);
return -1;
}
return itr->second;
}
int64_t
SQLite3InputArchive::search()
{
auto idx = search(m_next_name);
m_next_name = nullptr;
return idx;
}
int64_t
SQLite3InputArchive::getRowCount(sqlite3* conn, std::string_view query)
{
return impl::extract_row_count(conn, query);
}
} // namespace cereal
+341
파일 보기
@@ -0,0 +1,341 @@
// 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/common/logging.hpp"
#include "lib/common/mpl.hpp"
#include "lib/output/sql/common.hpp"
#include <fmt/format.h>
#include <sqlite3.h>
#include <cereal/cereal.hpp>
#include <cereal/external/base64.hpp>
#include <limits>
#include <new>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
namespace cereal
{
namespace impl
{
using namespace ::rocprofiler::tool::sql;
} // namespace impl
class SQLite3InputArchive
: public InputArchive<SQLite3InputArchive>
, public traits::TextArchive
{
public:
SQLite3InputArchive(sqlite3* conn,
std::string_view query,
int64_t len = 0,
int64_t chunk_len = 0);
~SQLite3InputArchive() CEREAL_NOEXCEPT override
{
sqlite3_finalize(m_stmt); // Finalize statement
}
public:
//! Retrieves the current node name
/*! @return nullptr if no name exists */
static const char* getNodeName() { return nullptr; }
static int64_t getRowCount(sqlite3* conn, std::string_view query);
void startNode() { ++m_iterator; }
void finishNode() const {}
//! Sets the name for the next node created with startNode
void setNextName(const char* name) { m_next_name = name; }
template <typename Tp>
void loadValue(std::string_view colname, Tp& val);
template <typename Tp>
void loadValue(Tp& val);
void loadBinaryValue(void* data, size_t size, const char* name = nullptr);
void loadSize(size_type& size) const;
int64_t search(std::string_view);
int64_t search(std::string_view, std::nothrow_t);
void set_chunk_index(size_t idx);
private:
int64_t search();
private:
class Iterator
{
public:
Iterator(SQLite3InputArchive* ar)
: m_archive{ar}
{}
//! Advance to the next node
Iterator& operator++()
{
m_step_ret = sqlite3_step(m_archive->m_stmt);
return *this;
}
private:
int m_step_ret = 0;
SQLite3InputArchive* m_archive = nullptr;
};
friend class Iterator;
private:
sqlite3* m_conn = nullptr;
sqlite3_stmt* m_stmt = nullptr;
const char* m_next_name = nullptr;
int64_t m_row_count = 0;
int64_t m_column_count = 0;
int64_t m_chunk_size = 0;
std::string m_query = {};
std::unordered_map<std::string, int64_t> m_column_names = {};
Iterator m_iterator;
std::vector<size_t> m_sizes = {};
mutable size_t m_size_idx = 0;
};
template <typename Tp>
void
SQLite3InputArchive::loadValue(std::string_view colname, Tp& val)
{
auto col = search(colname);
auto ret = impl::extract_column<Tp>(m_stmt, col);
if(ret) val = *ret;
}
template <typename Tp>
void
SQLite3InputArchive::loadValue(Tp& val)
{
auto col = search();
auto ret = impl::extract_column<Tp>(m_stmt, col);
if(ret) val = *ret;
}
// ######################################################################
// SQLite3Archive prologue and epilogue functions
// ######################################################################
// ######################################################################
//! Prologue for NVPs for SQLite3 archives
/*! NVPs do not start or finish nodes - they just set up the names */
template <class T>
inline void
prologue(SQLite3InputArchive&, NameValuePair<T> const&)
{}
// ######################################################################
//! Epilogue for NVPs for SQLite3 archives
/*! NVPs do not start or finish nodes - they just set up the names */
template <class T>
inline void
epilogue(SQLite3InputArchive&, NameValuePair<T> const&)
{}
// ######################################################################
//! Prologue for deferred data for SQLite3 archives
/*! Do nothing for the defer wrapper */
template <class T>
inline void
prologue(SQLite3InputArchive&, DeferredData<T> const&)
{}
// ######################################################################
//! Epilogue for deferred for SQLite3 archives
/*! NVPs do not start or finish nodes - they just set up the names */
/*! Do nothing for the defer wrapper */
template <class T>
inline void
epilogue(SQLite3InputArchive&, DeferredData<T> const&)
{}
// ######################################################################
//! Prologue for SizeTags for SQLite3 archives
/*! SizeTags are strictly ignored for SQLite3, they just indicate
that the current node should be made into an array */
template <class T>
inline void
prologue(SQLite3InputArchive&, SizeTag<T> const&)
{}
// ######################################################################
//! Epilogue for SizeTags for SQLite3 archives
/*! SizeTags are strictly ignored for SQLite3 */
template <class T>
inline void
epilogue(SQLite3InputArchive&, SizeTag<T> const&)
{}
// ######################################################################
//! Prologue for all other types for SQLite3 archives (except minimal types)
/*! Starts a new node, named either automatically or by some NVP,
that may be given data by the type about to be archived
Minimal types do not start or finish nodes */
template <
class T,
traits::EnableIf<
!std::is_arithmetic<T>::value,
!traits::has_minimal_base_class_serialization<T,
traits::has_minimal_input_serialization,
SQLite3InputArchive>::value,
!traits::has_minimal_input_serialization<T, SQLite3InputArchive>::value> = traits::sfinae>
inline void
prologue(SQLite3InputArchive& ar, T const&)
{
ar.startNode();
}
// ######################################################################
//! Epilogue for all other types other for SQLite3 archives (except minimal types)
/*! Finishes the node created in the prologue
Minimal types do not start or finish nodes */
template <
class T,
traits::EnableIf<
!std::is_arithmetic<T>::value,
!traits::has_minimal_base_class_serialization<T,
traits::has_minimal_input_serialization,
SQLite3InputArchive>::value,
!traits::has_minimal_input_serialization<T, SQLite3InputArchive>::value> = traits::sfinae>
inline void
epilogue(SQLite3InputArchive& ar, T const&)
{
ar.finishNode();
}
// ######################################################################
//! Prologue for arithmetic types for SQLite3 archives
inline void
prologue(SQLite3InputArchive&, std::nullptr_t const&)
{}
// ######################################################################
//! Epilogue for arithmetic types for SQLite3 archives
inline void
epilogue(SQLite3InputArchive&, std::nullptr_t const&)
{}
// ######################################################################
//! Prologue for arithmetic types for SQLite3 archives
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline void
prologue(SQLite3InputArchive&, T const&)
{}
// ######################################################################
//! Epilogue for arithmetic types for SQLite3 archives
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline void
epilogue(SQLite3InputArchive&, T const&)
{}
// ######################################################################
//! Prologue for strings for SQLite3 archives
template <class CharT, class Traits, class Alloc>
inline void
prologue(SQLite3InputArchive&, std::basic_string<CharT, Traits, Alloc> const&)
{}
// ######################################################################
//! Epilogue for strings for SQLite3 archives
template <class CharT, class Traits, class Alloc>
inline void
epilogue(SQLite3InputArchive&, std::basic_string<CharT, Traits, Alloc> const&)
{}
// ######################################################################
// Common SQLite3Archive serialization functions
// ######################################################################
//! Serializing NVP types to SQLite3
template <class T>
inline void
CEREAL_LOAD_FUNCTION_NAME(SQLite3InputArchive& ar, NameValuePair<T>& t)
{
ar.setNextName(t.name);
ar(t.value);
}
//! Loading nullptr from SQLite3
inline void
CEREAL_LOAD_FUNCTION_NAME(SQLite3InputArchive& ar, std::nullptr_t& t)
{
ar.loadValue(t);
}
//! Loading byte array from SQLite3
template <size_t N>
inline void
CEREAL_LOAD_FUNCTION_NAME(SQLite3InputArchive& ar, std::array<uint8_t, N>& t)
{
ar.loadValue(t);
}
template <size_t N>
inline void
CEREAL_LOAD_FUNCTION_NAME(SQLite3InputArchive& ar, std::array<int8_t, N>& t)
{
ar.loadValue(t);
}
//! Loading arithmetic from SQLite3
template <class T, traits::EnableIf<std::is_arithmetic<T>::value> = traits::sfinae>
inline void
CEREAL_LOAD_FUNCTION_NAME(SQLite3InputArchive& ar, T& t)
{
ar.loadValue(t);
}
//! loading string from SQLite3
template <class CharT, class Traits, class Alloc>
inline void
CEREAL_LOAD_FUNCTION_NAME(SQLite3InputArchive& ar, std::basic_string<CharT, Traits, Alloc>& str)
{
ar.loadValue(str);
}
// ######################################################################
//! Loading SizeTags from SQLite3
template <class T>
inline void
CEREAL_LOAD_FUNCTION_NAME(SQLite3InputArchive& ar, SizeTag<T>& st)
{
ar.loadSize(st.size);
}
} // namespace cereal
// register archives for polymorphic support
CEREAL_REGISTER_ARCHIVE(cereal::SQLite3InputArchive)
+161
파일 보기
@@ -0,0 +1,161 @@
// 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/serialization/sql.hpp"
#include "lib/common/container/ring_buffer.hpp"
#include "lib/common/mpl.hpp"
#include "lib/common/units.hpp"
#include "lib/output/domain_type.hpp"
#include "lib/output/generator.hpp"
#include "lib/output/sql/common.hpp"
#include <sqlite3.h>
#include <chrono>
#include <cstdint>
#include <string_view>
#include <type_traits>
namespace rocpd
{
namespace tool = ::rocprofiler::tool;
template <typename Tp>
struct sql_generator : public tool::generator<Tp>
{
using base_type = tool::generator<Tp>;
sql_generator(sqlite3* conn,
std::string_view query,
std::string_view order_by = {},
int64_t chunk_size = compute_chunk_size());
sql_generator() = delete;
~sql_generator() override = default;
sql_generator(const sql_generator&) = delete;
sql_generator(sql_generator&&) noexcept = delete;
sql_generator& operator=(const sql_generator&) = delete;
sql_generator& operator=(sql_generator&&) noexcept = delete;
std::vector<Tp> get(size_t itr) const override;
static int64_t compute_chunk_size();
private:
static std::string sanitize_query(std::string_view query);
private:
using archive_t = cereal::SQLite3InputArchive;
sqlite3* m_conn = nullptr;
std::string m_query = {};
std::string m_order = {};
int64_t m_chunk_size = 0;
int64_t m_num_entries = 0;
int64_t m_num_chunks = 0;
std::vector<size_t> m_expected = {};
archive_t m_archive;
};
template <typename Tp>
std::string
sql_generator<Tp>::sanitize_query(std::string_view query)
{
if(auto pos = query.find(';'); pos != std::string_view::npos)
return std::string{query.substr(0, pos)};
return std::string{query};
}
template <typename Tp>
int64_t
sql_generator<Tp>::compute_chunk_size()
{
return (16 * ::rocprofiler::common::units::get_page_size()) / sizeof(Tp);
}
template <typename Tp>
sql_generator<Tp>::sql_generator(sqlite3* conn,
std::string_view query,
std::string_view order_by,
int64_t chunk_size)
: base_type{tool::defer_size{}}
, m_conn{conn}
, m_query{sanitize_query(query)}
, m_order{(order_by.empty()) ? std::string{}
: fmt::format(" ORDER BY {}", sanitize_query(order_by))}
, m_chunk_size{chunk_size}
, m_num_entries{tool::sql::extract_row_count(m_conn, sanitize_query(query))}
, m_num_chunks{(m_num_entries / m_chunk_size) + ((m_num_entries % m_chunk_size) > 0 ? 1 : 0)}
, m_archive{m_conn, fmt::format("{}{}", m_query, m_order), m_num_entries, m_chunk_size}
{
base_type::resize(m_num_chunks);
m_expected.resize(m_num_chunks, m_chunk_size);
if(!m_expected.empty() && (m_num_entries % m_chunk_size) > 0)
m_expected.back() = (m_num_entries % m_chunk_size);
ROCP_TRACE << fmt::format("- Query : {}", query);
ROCP_TRACE << fmt::format(" Expected: {}",
fmt::join(m_expected.begin(), m_expected.end(), ", "));
}
template <typename Tp>
std::vector<Tp>
sql_generator<Tp>::get(size_t idx) const
{
auto _data = std::vector<Tp>{};
if(idx < static_cast<size_t>(m_num_chunks))
{
// auto _offset = idx * m_chunk_size;
// auto _limit = m_chunk_size;
// auto _query = fmt::format("{}{} LIMIT {} OFFSET {};", m_query, m_order, _limit,
// _offset);
// auto* conn = const_cast<sqlite3*>(m_conn);
// auto ar = cereal::SQLite3InputArchive{conn, _query};
auto& ar = const_cast<archive_t&>(m_archive);
ar.set_chunk_index(idx);
cereal::load(ar, _data);
ROCP_FATAL_IF(_data.size() != m_expected.at(idx))
<< fmt::format("Unexpected SQL query result for group {}. Found {} rows. Expected {} "
"rows.\nQuery:\n\t{}\n# of entries: {}, chunk size: {}, # chunks: {}",
idx,
_data.size(),
m_expected.at(idx),
m_query,
m_num_entries,
m_chunk_size,
m_num_chunks);
}
return _data;
}
} // namespace rocpd
+89
파일 보기
@@ -0,0 +1,89 @@
// MIT License
//
// Copyright (c) 2022 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/types.hpp"
#include "lib/python/rocpd/source/common.hpp"
#include "lib/output/node_info.hpp"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/cxx/serialization.hpp>
#include <cereal/cereal.hpp>
#include <cstdint>
namespace rocpd
{
namespace types
{
// std::string
// blob::hexdigest() const
// {
// auto _oss = std::ostringstream{};
// for(auto itr : (*this))
// _oss << std::hex << std::setw(2) << std::setfill('0') <<
// static_cast<int>(itr);
// return _oss.str();
// }
// std::string
// blob::hexliteral() const
// {
// auto _oss = std::ostringstream{};
// _oss << "X'";
// for(auto itr : (*this))
// _oss << std::hex << std::setw(2) << std::setfill('0') <<
// static_cast<int>(itr);
// _oss << "'";
// return _oss.str();
// }
void
agent::load_extdata()
{
if(has_extdata())
common::read_json_string(
extdata, [](auto& _ar, base_type& _base) { cereal::load(_ar, _base); }, base());
}
region::decoded_extdata
region::get_extdata() const
{
auto _msg = decoded_extdata{};
if(has_extdata())
common::read_json_string(
extdata, [](auto& ar, auto& msg) { cereal::load(ar, msg); }, _msg);
return _msg;
}
sample::decoded_extdata
sample::get_extdata() const
{
auto _msg = decoded_extdata{};
if(has_extdata())
common::read_json_string(
extdata, [](auto& ar, auto& msg) { cereal::load(ar, msg); }, _msg);
return _msg;
}
} // namespace types
} // namespace rocpd
+942
파일 보기
@@ -0,0 +1,942 @@
// MIT License
//
// Copyright (c) 2022 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/output/agent_info.hpp"
#include "lib/output/node_info.hpp"
#include <rocprofiler-sdk/agent.h>
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/cxx/serialization.hpp>
#include <fmt/format.h>
#include <cereal/archives/json.hpp>
#include <cereal/cereal.hpp>
#include <cstdint>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
namespace rocpd
{
namespace types
{
namespace tool = ::rocprofiler::tool;
template <typename BaseT>
struct base_class : public BaseT
{
using base_type = BaseT;
auto& base() { return static_cast<base_type&>(*this); }
const auto& base() const { return static_cast<const base_type&>(*this); }
};
using guid_t = std::string;
// struct blob : private std::array<uint8_t, 16>
// {
// using base_type = std::array<uint8_t, 16>;
// auto& base() { return static_cast<base_type&>(*this); }
// const auto& base() const { return static_cast<const base_type&>(*this); }
// using base_type::at;
// using base_type::operator[];
// using base_type::data;
// friend bool operator==(const blob& lhs, const blob& rhs)
// {
// for(size_t i = 0; i < lhs.size(); ++i)
// if(lhs.at(i) != rhs.at(i)) return false;
// return true;
// }
// friend bool operator!=(const blob& lhs, const blob& rhs)
// {
// for(size_t i = 0; i < lhs.size(); ++i)
// if(lhs.at(i) != rhs.at(i)) return true;
// return false;
// }
// // decltype(auto) at(size_t idx) { return base_type::at(idx); }
// // decltype(auto) at(size_t idx) const { return base_type::at(idx); }
// // decltype(auto) operator[](size_t idx) { return
// base_type::operator[](idx); }
// // decltype(auto) operator[](size_t idx) const { return
// base_type::operator[](idx); }
// // decltype(auto) data() { return base_type::data(); }
// // decltype(auto) data() const { return base_type::data(); }
// std::string hexdigest() const;
// std::string hexliteral() const;
// };
struct node : public base_class<tool::node_info>
{
guid_t guid = {};
};
// common base class for node info
struct common_node_info
{
guid_t guid = {};
uint64_t nid = 0;
std::string machine_id = {};
std::string hostname = {};
std::string system_name = {};
std::string system_release = {};
std::string system_version = {};
};
struct process : public base_class<common_node_info>
{
pid_t ppid = 0;
pid_t pid = 0;
uint64_t init = 0;
uint64_t start = 0;
uint64_t end = 0;
uint64_t fini = 0;
std::string command = {};
};
struct thread : public base_class<common_node_info>
{
pid_t ppid = 0;
pid_t pid = 0;
pid_t tid = 0;
uint64_t start = 0;
uint64_t end = 0;
std::string name = {};
bool is_main_thread() const { return (tid == pid); }
};
struct agent : public base_class<tool::agent_info>
{
guid_t guid = {};
uint64_t nid = 0;
uint64_t absolute_index = 0;
std::string type = {};
std::string user_name = {};
std::string extdata = {};
bool has_extdata() const { return (extdata.length() > 2); }
void load_extdata();
};
struct code_object
{
uint64_t id = 0;
guid_t guid = {};
uint64_t nid = 0;
uint64_t pid = 0;
uint64_t agent_abs_index = 0;
std::string uri = {};
uint64_t load_base = 0;
uint64_t load_size = 0;
uint64_t load_delta = 0;
std::string storage_type_str = {};
uint64_t storage_type = 0;
uint64_t memory_base = 0;
uint64_t memory_size = 0;
uint16_t code_object_size = 0;
};
struct kernel_symbol
{
uint64_t id = 0;
guid_t guid = {};
uint64_t nid = 0;
pid_t pid = 0;
uint64_t code_object_id = 0;
std::string kernel_name = {};
std::string display_name = {};
uint64_t kernel_id = 0;
uint64_t kernel_object = 0;
uint64_t kernarg_segment_size = 0;
uint64_t kernarg_segment_alignment = 0;
uint64_t group_segment_size = 0;
uint64_t private_segment_size = 0;
uint32_t sgpr_count = 0;
uint32_t arch_vgpr_count = 0;
uint32_t accum_vgpr_count = 0;
uint64_t kernel_symbol_size = 0;
uint64_t kernel_code_entry_byte_offset = 0;
std::string formatted_kernel_name = {};
std::string demangled_kernel_name = {};
std::string truncated_kernel_name = {};
uint64_t kernel_address = 0;
};
struct region
{
struct decoded_extdata
{
std::string message = {};
};
uint64_t id = 0;
guid_t guid = {};
std::string category = {};
std::string name = {};
pid_t nid = 0;
pid_t pid = 0;
pid_t tid = 0;
rocprofiler_timestamp_t start = 0;
rocprofiler_timestamp_t end = 0;
uint64_t event_id = 0;
uint64_t stack_id = 0;
uint64_t parent_stack_id = 0;
uint64_t corr_id = 0;
std::string extdata = {};
bool has_extdata() const { return (extdata.length() > 2); }
decoded_extdata get_extdata() const;
};
struct sample
{
struct decoded_extdata
{
std::string message = {};
};
uint64_t id = 0;
guid_t guid = {};
std::string category = {};
std::string name = {};
pid_t nid = 0;
pid_t pid = 0;
pid_t tid = 0;
rocprofiler_timestamp_t timestamp = 0;
uint64_t event_id = 0;
uint64_t stack_id = 0;
uint64_t parent_stack_id = 0;
uint64_t corr_id = 0;
std::string extdata = {};
bool has_extdata() const { return (extdata.length() > 2); }
decoded_extdata get_extdata() const;
};
struct region_arg
{
uint64_t id = 0;
guid_t guid = {};
pid_t nid = 0;
pid_t pid = 0;
std::string type = {};
std::string name = {};
std::string value = {};
};
struct kernel_dispatch
{
uint64_t id = 0;
guid_t guid = {};
std::string category = {};
std::string region = {};
std::string name = {};
pid_t nid = 0;
pid_t pid = 0;
pid_t tid = 0;
uint64_t agent_abs_index = 0;
uint64_t agent_log_index = 0;
uint64_t agent_type_index = 0;
std::string agent_type = {};
uint64_t code_object_id = 0;
uint64_t kernel_id = 0;
uint64_t dispatch_id = 0;
uint64_t stream_id = 0;
uint64_t queue_id = 0;
std::string queue = {};
std::string stream = {};
rocprofiler_timestamp_t start = 0;
rocprofiler_timestamp_t end = 0;
rocprofiler_dim3_t grid_size = {};
rocprofiler_dim3_t workgroup_size = {};
uint64_t lds_size = 0;
uint64_t scratch_size = 0;
uint64_t static_lds_size = 0;
uint64_t static_scratch_size = 0;
uint64_t stack_id = 0;
uint64_t parent_stack_id = 0;
uint64_t corr_id = 0;
};
struct memory_allocation
{
uint64_t id = 0;
guid_t guid = {};
pid_t pid = 0;
pid_t tid = 0;
rocprofiler_timestamp_t start = 0;
rocprofiler_timestamp_t end = 0;
std::string type = {};
std::string level = {};
std::string agent_name = {};
std::string category = {};
uint64_t agent_abs_index = 0;
uint64_t agent_log_index = 0;
uint64_t agent_type_index = 0;
std::string agent_type = {};
uint64_t address = 0;
uint64_t size = 0;
uint64_t queue_id = 0;
std::string queue_name = {};
uint64_t stream_id = 0;
std::string stream_name = {};
uint64_t stack_id = 0;
uint64_t parent_stack_id = 0;
uint64_t corr_id = 0;
};
struct memory_copies
{
uint64_t id = 0;
guid_t guid = {};
pid_t pid = 0;
pid_t tid = 0;
rocprofiler_timestamp_t start = 0;
rocprofiler_timestamp_t end = 0;
std::string name = {};
std::string region_name = {};
std::string category = {};
uint64_t stream_id = 0;
uint64_t queue_id = 0;
std::string stream_name = {};
std::string queue_name = {};
uint64_t size = 0;
std::string dst_device = {};
uint64_t dst_agent_abs_index = 0;
uint64_t dst_agent_log_index = 0;
uint64_t dst_agent_type_index = 0;
std::string dst_agent_type = {};
uint64_t dst_address = 0;
std::string src_device = {};
uint64_t src_agent_abs_index = 0;
uint64_t src_agent_log_index = 0;
uint64_t src_agent_type_index = 0;
std::string src_agent_type = {};
uint64_t src_address = 0;
uint64_t stack_id = 0;
uint64_t parent_stack_id = 0;
uint64_t corr_id = 0;
};
struct scratch_memory
{
guid_t guid = {};
std::string operation = {};
std::string category = {};
uint64_t agent_abs_index = 0;
uint64_t agent_log_index = 0;
uint64_t agent_type_index = 0;
std::string agent_type = {};
uint64_t queue_id = 0;
pid_t pid = 0;
pid_t tid = 0;
std::string alloc_flags = {};
rocprofiler_timestamp_t start = 0;
rocprofiler_timestamp_t end = 0;
uint64_t size = 0;
uint64_t stack_id = 0;
uint64_t parent_stack_id = 0;
uint64_t corr_id = 0;
};
struct stats
{
std::string name = {};
uint64_t calls = 0;
uint64_t total_duration = 0;
double sqr = 0.0;
double average = 0.0;
double percentage = 0.0;
uint64_t min_ns = 0;
uint64_t max_ns = 0;
double variance = 0.0;
double std_dev = 0.0;
};
struct stats_node
{
guid_t guid = {};
pid_t pid = 0;
// uint64_t nid = 0; // nid is not used in stats_node
std::string name = {};
uint64_t calls = 0;
uint64_t total_duration = 0;
double sqr = 0.0;
double average = 0.0;
double percentage = 0.0;
uint64_t min_ns = 0;
uint64_t max_ns = 0;
double variance = 0.0;
double std_dev = 0.0;
};
// Add this struct after the existing type definitions
struct pmc_event
{
uint64_t id = 0;
guid_t guid = {};
pid_t pid = 0;
uint64_t event_id = 0;
uint64_t pmc_id = 0;
double counter_value = 0;
};
struct counter
{
uint64_t id = 0;
guid_t guid = {};
uint64_t dispatch_id = 0;
uint64_t kernel_id = 0;
uint32_t stack_id = 0;
uint64_t correlation_id = 0;
uint64_t event_id = 0;
pid_t pid = 0;
pid_t tid = 0;
uint32_t agent_id = 0;
uint64_t agent_abs_index = 0;
uint64_t agent_log_index = 0;
uint64_t agent_type_index = 0;
std::string agent_type = {};
uint64_t queue_id = 0;
uint32_t grid_size_x = 0;
uint32_t grid_size_y = 0;
uint32_t grid_size_z = 0;
uint64_t grid_size = 0;
std::string kernel_name = {};
std::string kernel_region = {};
uint32_t workgroup_size_x = 0;
uint32_t workgroup_size_y = 0;
uint32_t workgroup_size_z = 0;
uint32_t workgroup_size = 0;
uint32_t lds_block_size = 0;
uint32_t scratch_size = 0;
uint32_t vgpr_count = 0;
uint32_t accum_vgpr_count = 0;
uint32_t sgpr_count = 0;
std::string counter_name = {};
std::string counter_symbol = {};
std::string component = {};
std::string description = {};
std::string block = {};
std::string expression = {};
std::string value_type = {};
uint32_t counter_id = 0;
double value = 0;
rocprofiler_timestamp_t start = 0;
rocprofiler_timestamp_t end = 0;
bool is_constant = false;
bool is_derived = false;
};
struct pmc_info
{
uint64_t id = 0;
guid_t guid = {};
uint64_t nid = 0;
uint64_t agent_abs_index = 0;
bool is_constant = false;
bool is_derived = false;
std::string name = {};
std::string description = {};
std::string block = {};
std::string expression = {};
};
} // namespace types
} // namespace rocpd
namespace cereal
{
#define LOAD_DATA_FIELD(FIELD) ar(make_nvp(#FIELD, data.FIELD))
#define LOAD_DATA_NAMED(NAME, FIELD) ar(make_nvp(NAME, data.FIELD))
#define LOAD_DATA_VALUE(NAME, ARG) ar(make_nvp(NAME, ARG))
// template <typename ArchiveT>
// void
// load(ArchiveT& ar, rocpd::types::blob& data)
// {
// ::cereal::load(ar, data.base());
// }
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::node& data)
{
::cereal::load(ar, data.base());
LOAD_DATA_FIELD(guid);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::common_node_info& data)
{
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(machine_id);
LOAD_DATA_FIELD(hostname);
LOAD_DATA_FIELD(system_name);
LOAD_DATA_FIELD(system_release);
LOAD_DATA_FIELD(system_version);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::process& data)
{
::cereal::load(ar, data.base());
LOAD_DATA_FIELD(ppid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(init);
LOAD_DATA_FIELD(start);
LOAD_DATA_FIELD(end);
LOAD_DATA_FIELD(fini);
LOAD_DATA_FIELD(command);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::thread& data)
{
::cereal::load(ar, data.base());
LOAD_DATA_FIELD(ppid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(tid);
LOAD_DATA_FIELD(start);
LOAD_DATA_FIELD(end);
LOAD_DATA_FIELD(name);
if(data.name.empty()) data.name = fmt::format("Thread {}", data.tid);
if(data.tid == data.pid && data.name.find("[main]") == std::string::npos)
data.name += std::string(" [main]");
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::agent& data)
{
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(absolute_index);
LOAD_DATA_FIELD(type);
LOAD_DATA_FIELD(user_name);
LOAD_DATA_FIELD(extdata);
data.load_extdata();
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::code_object& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(agent_abs_index);
LOAD_DATA_FIELD(uri);
LOAD_DATA_FIELD(load_base);
LOAD_DATA_FIELD(load_size);
LOAD_DATA_FIELD(load_delta);
LOAD_DATA_FIELD(storage_type_str);
LOAD_DATA_FIELD(storage_type);
LOAD_DATA_FIELD(memory_base);
LOAD_DATA_FIELD(memory_size);
LOAD_DATA_FIELD(code_object_size);
}
// Add after the pmc_info serialization (around line 1005)
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::kernel_symbol& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(code_object_id);
LOAD_DATA_FIELD(kernel_name);
LOAD_DATA_FIELD(display_name);
LOAD_DATA_FIELD(kernel_id);
LOAD_DATA_FIELD(kernel_object);
LOAD_DATA_FIELD(kernarg_segment_size);
LOAD_DATA_FIELD(kernarg_segment_alignment);
LOAD_DATA_FIELD(group_segment_size);
LOAD_DATA_FIELD(private_segment_size);
LOAD_DATA_FIELD(sgpr_count);
LOAD_DATA_FIELD(arch_vgpr_count);
LOAD_DATA_FIELD(accum_vgpr_count);
LOAD_DATA_FIELD(kernel_symbol_size);
LOAD_DATA_FIELD(kernel_code_entry_byte_offset);
LOAD_DATA_FIELD(formatted_kernel_name);
LOAD_DATA_FIELD(demangled_kernel_name);
LOAD_DATA_FIELD(truncated_kernel_name);
LOAD_DATA_FIELD(kernel_address);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::region& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(category);
LOAD_DATA_FIELD(name);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(tid);
LOAD_DATA_FIELD(start);
LOAD_DATA_FIELD(end);
LOAD_DATA_FIELD(event_id);
LOAD_DATA_FIELD(stack_id);
LOAD_DATA_FIELD(parent_stack_id);
LOAD_DATA_FIELD(corr_id);
LOAD_DATA_FIELD(extdata);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::region::decoded_extdata& data)
{
LOAD_DATA_FIELD(message);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::sample& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(category);
LOAD_DATA_FIELD(name);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(tid);
LOAD_DATA_FIELD(timestamp);
LOAD_DATA_FIELD(event_id);
LOAD_DATA_FIELD(stack_id);
LOAD_DATA_FIELD(parent_stack_id);
LOAD_DATA_FIELD(corr_id);
LOAD_DATA_FIELD(extdata);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::sample::decoded_extdata& data)
{
LOAD_DATA_FIELD(message);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::region_arg& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(type);
LOAD_DATA_FIELD(name);
LOAD_DATA_FIELD(value);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::kernel_dispatch& data)
{
auto load_dim3 = [&ar](std::string_view view, auto& _v) {
ar(make_nvp(fmt::format("{}_x", view), _v.x));
ar(make_nvp(fmt::format("{}_y", view), _v.y));
ar(make_nvp(fmt::format("{}_z", view), _v.z));
};
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(category);
LOAD_DATA_FIELD(region);
LOAD_DATA_FIELD(name);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(tid);
LOAD_DATA_FIELD(agent_abs_index);
LOAD_DATA_FIELD(agent_log_index);
LOAD_DATA_FIELD(agent_type_index);
LOAD_DATA_FIELD(agent_type);
LOAD_DATA_FIELD(code_object_id);
LOAD_DATA_FIELD(kernel_id);
LOAD_DATA_FIELD(dispatch_id);
LOAD_DATA_FIELD(queue_id);
LOAD_DATA_FIELD(stream_id);
LOAD_DATA_FIELD(queue);
LOAD_DATA_FIELD(stream);
LOAD_DATA_FIELD(start);
LOAD_DATA_FIELD(end);
load_dim3("workgroup", data.workgroup_size);
load_dim3("grid", data.grid_size);
LOAD_DATA_FIELD(lds_size);
LOAD_DATA_FIELD(scratch_size);
LOAD_DATA_FIELD(static_lds_size);
LOAD_DATA_FIELD(static_scratch_size);
LOAD_DATA_FIELD(stack_id);
LOAD_DATA_FIELD(parent_stack_id);
LOAD_DATA_FIELD(corr_id);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::memory_allocation& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(tid);
LOAD_DATA_FIELD(start);
LOAD_DATA_FIELD(end);
LOAD_DATA_FIELD(type);
LOAD_DATA_FIELD(level);
LOAD_DATA_FIELD(agent_name);
LOAD_DATA_FIELD(agent_abs_index);
LOAD_DATA_FIELD(agent_log_index);
LOAD_DATA_FIELD(agent_type_index);
LOAD_DATA_FIELD(agent_type);
LOAD_DATA_FIELD(address);
LOAD_DATA_FIELD(size);
LOAD_DATA_FIELD(queue_id);
LOAD_DATA_FIELD(queue_name);
LOAD_DATA_FIELD(stream_id);
LOAD_DATA_FIELD(stream_name);
LOAD_DATA_FIELD(category);
LOAD_DATA_FIELD(stack_id);
LOAD_DATA_FIELD(parent_stack_id);
LOAD_DATA_FIELD(corr_id);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::memory_copies& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(tid);
LOAD_DATA_FIELD(start);
LOAD_DATA_FIELD(end);
LOAD_DATA_FIELD(name);
LOAD_DATA_FIELD(region_name);
LOAD_DATA_FIELD(stream_id);
LOAD_DATA_FIELD(queue_id);
LOAD_DATA_FIELD(stream_name);
LOAD_DATA_FIELD(queue_name);
LOAD_DATA_FIELD(size);
LOAD_DATA_FIELD(dst_device);
LOAD_DATA_FIELD(dst_agent_abs_index);
LOAD_DATA_FIELD(dst_agent_log_index);
LOAD_DATA_FIELD(dst_agent_type_index);
LOAD_DATA_FIELD(dst_agent_type);
LOAD_DATA_FIELD(dst_address);
LOAD_DATA_FIELD(src_device);
LOAD_DATA_FIELD(src_agent_abs_index);
LOAD_DATA_FIELD(src_agent_log_index);
LOAD_DATA_FIELD(src_agent_type_index);
LOAD_DATA_FIELD(src_agent_type);
LOAD_DATA_FIELD(src_address);
LOAD_DATA_FIELD(category);
LOAD_DATA_FIELD(stack_id);
LOAD_DATA_FIELD(parent_stack_id);
LOAD_DATA_FIELD(corr_id);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::scratch_memory& data)
{
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(operation);
LOAD_DATA_FIELD(agent_abs_index);
LOAD_DATA_FIELD(agent_log_index);
LOAD_DATA_FIELD(agent_type_index);
LOAD_DATA_FIELD(agent_type);
LOAD_DATA_FIELD(queue_id);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(tid);
LOAD_DATA_FIELD(alloc_flags);
LOAD_DATA_FIELD(start);
LOAD_DATA_FIELD(end);
LOAD_DATA_FIELD(size);
LOAD_DATA_FIELD(category);
LOAD_DATA_FIELD(stack_id);
LOAD_DATA_FIELD(parent_stack_id);
LOAD_DATA_FIELD(corr_id);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::stats& data)
{
LOAD_DATA_FIELD(name);
LOAD_DATA_FIELD(calls);
LOAD_DATA_NAMED("DURATION (nsec)", total_duration);
LOAD_DATA_NAMED("SQR (nsec)", sqr);
LOAD_DATA_NAMED("AVERAGE (nsec)", average);
LOAD_DATA_NAMED("PERCENT (INC)", percentage);
LOAD_DATA_NAMED("MIN (nsec)", min_ns);
LOAD_DATA_NAMED("MAX (nsec)", max_ns);
LOAD_DATA_NAMED("VARIANCE", variance);
LOAD_DATA_NAMED("STD_DEV", std_dev);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::stats_node& data)
{
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(pid);
// LOAD_DATA_FIELD(nid); // nid is not used in stats_node
LOAD_DATA_FIELD(name);
LOAD_DATA_FIELD(calls);
LOAD_DATA_NAMED("DURATION (nsec)", total_duration);
LOAD_DATA_NAMED("SQR (nsec)", sqr);
LOAD_DATA_NAMED("AVERAGE (nsec)", average);
LOAD_DATA_NAMED("PERCENT (INC)", percentage);
LOAD_DATA_NAMED("MIN (nsec)", min_ns);
LOAD_DATA_NAMED("MAX (nsec)", max_ns);
LOAD_DATA_NAMED("VARIANCE", variance);
LOAD_DATA_NAMED("STD_DEV", std_dev);
}
// Add this inside the cereal namespace, after the existing load functions
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::pmc_event& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(event_id);
LOAD_DATA_FIELD(pmc_id);
// LOAD_DATA_FIELD(counter_value);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::counter& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(dispatch_id);
LOAD_DATA_FIELD(kernel_id);
LOAD_DATA_FIELD(stack_id);
LOAD_DATA_FIELD(correlation_id);
LOAD_DATA_FIELD(event_id);
LOAD_DATA_FIELD(pid);
LOAD_DATA_FIELD(tid);
LOAD_DATA_FIELD(agent_id);
LOAD_DATA_FIELD(agent_abs_index);
LOAD_DATA_FIELD(agent_log_index);
LOAD_DATA_FIELD(agent_type_index);
LOAD_DATA_FIELD(agent_type);
LOAD_DATA_FIELD(queue_id);
LOAD_DATA_FIELD(grid_size_x);
LOAD_DATA_FIELD(grid_size_y);
LOAD_DATA_FIELD(grid_size_z);
LOAD_DATA_FIELD(grid_size);
LOAD_DATA_FIELD(kernel_name);
LOAD_DATA_FIELD(kernel_region);
LOAD_DATA_FIELD(workgroup_size_x);
LOAD_DATA_FIELD(workgroup_size_y);
LOAD_DATA_FIELD(workgroup_size_z);
LOAD_DATA_FIELD(workgroup_size);
LOAD_DATA_FIELD(lds_block_size);
LOAD_DATA_FIELD(scratch_size);
LOAD_DATA_FIELD(vgpr_count);
LOAD_DATA_FIELD(accum_vgpr_count);
LOAD_DATA_FIELD(sgpr_count);
LOAD_DATA_FIELD(counter_name);
LOAD_DATA_FIELD(counter_symbol);
LOAD_DATA_FIELD(component);
LOAD_DATA_FIELD(description);
LOAD_DATA_FIELD(block);
LOAD_DATA_FIELD(expression);
LOAD_DATA_FIELD(value_type);
LOAD_DATA_FIELD(counter_id);
LOAD_DATA_FIELD(value);
LOAD_DATA_FIELD(start);
LOAD_DATA_FIELD(end);
LOAD_DATA_FIELD(is_constant);
LOAD_DATA_FIELD(is_derived);
}
template <typename ArchiveT>
void
load(ArchiveT& ar, rocpd::types::pmc_info& data)
{
LOAD_DATA_FIELD(id);
LOAD_DATA_FIELD(guid);
LOAD_DATA_FIELD(nid);
LOAD_DATA_FIELD(agent_abs_index);
LOAD_DATA_FIELD(is_constant);
LOAD_DATA_FIELD(is_derived);
LOAD_DATA_FIELD(name);
LOAD_DATA_FIELD(description);
LOAD_DATA_FIELD(block);
LOAD_DATA_FIELD(expression);
}
} // namespace cereal
#undef LOAD_DATA_FIELD
#undef LOAD_DATA_NAMED
#undef LOAD_DATA_VALUE
// namespace fmt
// {
// template <>
// struct formatter<rocpd::types::blob>
// {
// template <typename ParseContext>
// constexpr auto parse(ParseContext& ctx)
// {
// return ctx.begin();
// }
// template <typename Ctx>
// auto format(const rocpd::types::blob& val, Ctx& ctx) const
// {
// return fmt::format_to(ctx.out(), "{}", val.hexliteral());
// }
// };
// } // namespace fmt