ROCpd support [Part 2] (#109)

* Rocpd part 2, caching

* Fix shadowed variables

* backward compatibility

* Fixed designated initializers

* Fix timemory include

* Remove benchmark & Fix build issues for rhel

* Add missing bracket

* Fix shadowing and pedantic

* Fix pedantic pt2

* Fix duplicated SDK calls

* Add decay in get_size_impl

* Rename sample cache to trace cache

* Add cache storage supported types

* Resolving track naming in sampling module

* fix sampling of flushing thread

* fix sampling of flushing thread 2

* throw exception upon store while buffer storage is not running

* Prevent fork crashing

* Fix rebase issue

* Applied suggestions from code review

* Change flushing thread to use PTL

* Fix agent creation order

* Fix stream id ci throw

* Remove force setup of rocprofiler-sdk

* Code cleanup

* Change initialization for agent

* Add missing namespace

* Fix the mismatch within the tool_agent->device_id

* Switch from using handle to use agent type index

* Fix pmc info comparator in metadata registry

---------

Co-authored-by: Aleksandar <aleksandar.djordjevic@amd.com>
Co-authored-by: Milan Radosavljevic <milan.radosavljevic@amd.com>
Co-authored-by: Marjan Antic <marantic@amd.com>
This commit is contained in:
systems-assistant[bot]
2025-08-19 22:01:04 -04:00
committed by GitHub
parent 351d598869
commit 1f86010ca2
40 changed files with 3432 additions and 1184 deletions
@@ -1,3 +1,25 @@
# 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.
#
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/defines.hpp.in
@@ -25,6 +47,8 @@ set(core_sources
${CMAKE_CURRENT_LIST_DIR}/state.cpp
${CMAKE_CURRENT_LIST_DIR}/timemory.cpp
${CMAKE_CURRENT_LIST_DIR}/utility.cpp
${CMAKE_CURRENT_LIST_DIR}/agent_manager.cpp
${CMAKE_CURRENT_LIST_DIR}/node_info.cpp
)
set(core_headers
@@ -53,6 +77,9 @@ set(core_headers
${CMAKE_CURRENT_LIST_DIR}/state.hpp
${CMAKE_CURRENT_LIST_DIR}/timemory.hpp
${CMAKE_CURRENT_LIST_DIR}/utility.hpp
${CMAKE_CURRENT_LIST_DIR}/agent.hpp
${CMAKE_CURRENT_LIST_DIR}/agent_manager.hpp
${CMAKE_CURRENT_LIST_DIR}/node_info.hpp
)
add_library(rocprofiler-systems-core-library STATIC)
@@ -69,6 +96,7 @@ add_subdirectory(binary)
add_subdirectory(components)
add_subdirectory(containers)
add_subdirectory(rocpd)
add_subdirectory(trace_cache)
target_include_directories(
rocprofiler-systems-core-library
@@ -94,6 +122,7 @@ target_link_libraries(
$<BUILD_INTERFACE:rocprofiler-systems::rocprofiler-systems-compile-definitions>
$<BUILD_INTERFACE:rocprofiler-systems::rocprofiler-systems-compile-options>
$<BUILD_INTERFACE:rocprofiler-systems::rocprofiler-systems-perfetto>
$<BUILD_INTERFACE:rocprofiler-systems::rocprofiler-systems-sqlite3>
$<BUILD_INTERFACE:rocprofiler-systems::rocprofiler-systems-timemory>
$<BUILD_INTERFACE:rocprofiler-systems::rocprofiler-systems-mpi>
$<BUILD_INTERFACE:rocprofiler-systems::rocprofiler-systems-rocm>
@@ -43,7 +43,8 @@ enum class agent_type : uint8_t
struct agent
{
agent_type type;
uint64_t id;
uint64_t handle;
uint64_t device_id;
uint32_t node_id;
int32_t logical_node_id;
int32_t logical_node_type_id;
@@ -52,7 +53,7 @@ struct agent
std::string vendor_name;
std::string product_name;
size_t device_id{ 0 };
size_t device_type_index{ 0 };
size_t base_id{ 0 };
#if ROCPROFSYS_USE_ROCM > 0
amdsmi_processor_handle smi_handle = nullptr;
@@ -44,11 +44,30 @@ agent_manager::insert_agent(agent& _agent)
(_agent.type == agent_type::GPU ? _gpu_agents_cnt : _cpu_agents_cnt),
(_agent.type == agent_type::GPU ? "GPU" : "CPU"));
_agent.device_id =
_agent.device_type_index =
(_agent.type == agent_type::GPU ? _gpu_agents_cnt++ : _cpu_agents_cnt++);
_agents.emplace_back(std::make_shared<agent>(_agent));
}
const agent&
agent_manager::get_agent_by_type_index(size_t type_index, agent_type type) const
{
ROCPROFSYS_VERBOSE(3, "Getting agent for type: %s, with type index: %ld\n",
(type == agent_type::GPU) ? "GPU" : "CPU", type_index);
auto _agent =
std::find_if(_agents.begin(), _agents.end(), [&](const auto& agent_ptr) {
return agent_ptr->type == type && agent_ptr->device_type_index == type_index;
});
if(_agent == _agents.end())
{
std::ostringstream oss;
oss << "Agent not found for type index: " << type_index
<< ", type: " << (type == agent_type::GPU ? "GPU" : "CPU");
throw std::out_of_range(oss.str());
}
return **_agent;
}
const agent&
agent_manager::get_agent_by_id(size_t device_id, agent_type type) const
{
@@ -75,7 +94,7 @@ agent_manager::get_agent_by_handle(uint64_t device_handle, agent_type type) cons
device_handle, (type == agent_type::GPU ? "GPU" : "CPU"));
auto _agent =
std::find_if(_agents.begin(), _agents.end(), [&](const auto& agent_ptr) {
return agent_ptr->type == type && agent_ptr->id == device_handle;
return agent_ptr->type == type && agent_ptr->handle == device_handle;
});
if(_agent == _agents.end())
{
@@ -93,7 +112,7 @@ agent_manager::get_agent_by_handle(size_t device_handle) const
ROCPROFSYS_VERBOSE(3, "Getting agent for device handle: %ld\n", device_handle);
auto _agent =
std::find_if(_agents.begin(), _agents.end(), [&](const auto& agent_ptr) {
return agent_ptr->id == device_handle;
return agent_ptr->handle == device_handle;
});
if(_agent == _agents.end())
{
@@ -41,8 +41,9 @@ struct agent_manager
~agent_manager() = default;
void insert_agent(agent& agent);
const agent& get_agent_by_type_index(size_t type_index, agent_type type) const;
const agent& get_agent_by_id(size_t device_id, agent_type type) const;
const agent& get_agent_by_handle(size_t device_id, agent_type type) const;
const agent& get_agent_by_handle(size_t device_handle, agent_type type) const;
const agent& get_agent_by_handle(size_t device_handle) const;
std::vector<std::shared_ptr<agent>> get_agents_by_type(agent_type type) const;
@@ -1,353 +0,0 @@
// MIT License
//
// Copyright (c) 2022-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 <algorithm>
#include <array>
#include <bitset>
#include <chrono>
#include <cstdlib>
#include <iomanip>
#include <iostream>
#include <limits>
#include <mutex>
#include <sstream>
#include <string>
#include <type_traits>
#include <unistd.h>
#include <unordered_map>
#include <vector>
#include "core/benchmark/category.hpp"
#include "core/debug.hpp"
namespace rocprofsys
{
namespace benchmark
{
namespace
{
template <bool enabled, typename category_enum, category_enum... enabled_categories>
struct benchmark_impl
{
template <category_enum... categories>
struct scope
{
scope(const scope&) = delete;
scope& operator=(const scope&) = delete;
~scope() = default;
protected:
scope() = default;
scope(scope&&) = default;
scope& operator=(scope&&) = default;
};
template <category_enum... categories>
static void start()
{}
template <category_enum... categories>
static void end()
{}
template <category_enum... categories>
[[nodiscard]] static scope<categories...> scoped_trace()
{
return scope<categories...>{};
}
static void init_from_env(const char* = nullptr) {}
static void show_results() {}
};
using tid_t = __pid_t;
struct indexed_category
{
size_t category;
tid_t thread_id;
friend bool operator==(const indexed_category& lhs, const indexed_category& rhs)
{
return lhs.category == rhs.category && lhs.thread_id == rhs.thread_id;
}
};
struct indexed_category_hash
{
size_t operator()(const indexed_category& p) const noexcept
{
std::size_t hash1 = std::hash<size_t>{}(p.category);
std::size_t hash2 = std::hash<size_t>{}(p.thread_id);
return hash1 ^ (hash2 << 1);
}
};
template <typename category_enum, category_enum... enabled_categories>
struct benchmark_impl<true, category_enum, enabled_categories...>
{
static_assert(std::is_enum_v<category_enum>, "category_enum must be an enum");
public:
using clock = std::chrono::high_resolution_clock;
using time_point = clock::time_point;
static constexpr size_t _max_categories = static_cast<size_t>(category_enum::count);
template <category_enum... categories>
struct scope
{
friend benchmark_impl;
public:
scope(const scope&) = delete;
scope& operator=(const scope&) = delete;
~scope() { end<categories...>(); }
protected:
scope() { start<categories...>(); }
scope(scope&&) = default;
scope& operator=(scope&&) = default;
};
template <category_enum... categories>
static void start()
{
static const thread_local auto _thread_id = gettid();
const auto now = clock::now();
std::lock_guard lock(m_mutex);
(..., (is_category_defined<categories>([&] {
if(m_enabled.test(to_index(categories)))
m_started[{ to_index(categories), _thread_id }] = now;
})));
}
template <category_enum... categories>
static void end()
{
static const thread_local auto _thread_id = getpid();
const auto _end_time = clock::now();
std::lock_guard lock(m_mutex);
(..., (is_category_defined<categories>([&] {
if(m_enabled.test(to_index(categories)))
end_category(_end_time, categories, _thread_id);
})));
}
template <category_enum... categories>
[[nodiscard]] static scope<categories...> scoped_trace()
{
return scope<categories...>{};
}
static void init_from_env(const char* envVar = "ROCPROFSYS_BENCHMARK_CATEGORIES")
{
std::lock_guard lock(m_mutex);
const auto* env = std::getenv(envVar);
if(env == nullptr || std::string(env).empty())
{
ROCPROFSYS_WARNING(1, "No BENCHMARK categories specified in environment "
"variable ROCPROFSYS_BENCHMARK_CATEGORIES.\n");
return;
}
std::string _str(env);
std::istringstream ss(_str);
std::string token;
while(std::getline(ss, token, ','))
{
token.erase(0, token.find_first_not_of(" \t"));
token.erase(token.find_last_not_of(" \t") + 1);
for(category_enum cat : compiledCategories)
{
if(to_string(cat) == token)
{
m_enabled.set(to_index(cat));
}
}
}
}
static void show_results()
{
std::lock_guard lock(m_mutex);
std::vector<std::pair<category_enum, result_data>> sorted;
for(category_enum cat : compiledCategories)
{
const auto& data = m_results[to_index(cat)];
if(data.count > 0)
{
sorted.emplace_back(cat, data);
}
}
std::sort(sorted.begin(), sorted.end(), [](const auto& a, const auto& b) {
return a.second.total_time > b.second.total_time;
});
constexpr uint32_t _category = 30;
constexpr uint32_t _calls = 8;
constexpr uint32_t _total = 12;
constexpr uint32_t _avg = 10;
constexpr uint32_t _min = 10;
constexpr uint32_t _max = 10;
std::cout << "\033[32m"
<< std::string(_category + _calls + _total + _avg + _min + _max, '=')
<< "\n";
std::cout << "Benchmark Results (Sorted by Total Time):\n";
std::cout << std::string(_category + _calls + _total + _avg + _min + _max, '-')
<< "\n";
std::cout << std::left << std::setw(_category) << "Category" << std::right
<< std::setw(_calls) << "Calls" << std::setw(_total) << "Total(ms)"
<< std::setw(_avg) << "Avg(us)" << std::setw(_min) << "Min(us)"
<< std::setw(_max) << "Max(us)" << "\n";
std::cout << std::string(_category + _calls + _total + _avg + _min + _max, '-')
<< "\n";
for(const auto& [cat, data] : sorted)
{
double totalMs = static_cast<double>(data.total_time) / 1000.0;
double avgUs = static_cast<double>(data.total_time) / data.count;
std::cout << std::left << std::setw(_category) << to_string(cat) << std::right
<< std::setw(_calls) << data.count << std::setw(_total)
<< std::fixed << std::setprecision(3) << totalMs << std::setw(_avg)
<< std::fixed << std::setprecision(1) << avgUs << std::setw(_min)
<< data.min_time << std::setw(_max) << data.max_time << "\n";
}
std::cout << std::string(_category + _calls + _total + _avg + _min + _max, '=')
<< "\033[0m" << "\n\n";
}
private:
struct result_data
{
uint64_t total_time = 0;
size_t count = 0;
uint64_t min_time = std::numeric_limits<uint64_t>::max();
uint64_t max_time = std::numeric_limits<uint64_t>::min();
void update(uint64_t duration)
{
total_time += duration;
count += 1;
if(duration < min_time) min_time = duration;
if(duration > max_time) max_time = duration;
}
};
static constexpr size_t to_index(category_enum cat)
{
return static_cast<size_t>(cat);
}
static void end_category(const time_point& end_time, category_enum cat,
const tid_t thread_id)
{
const size_t _idx = to_index(cat);
auto _it = m_started.find({ _idx, thread_id });
if(_it == m_started.end())
{
ROCPROFSYS_WARNING(1, "Benchmark error: missing start time for category!\n");
return;
}
auto duration =
std::chrono::duration_cast<std::chrono::microseconds>(end_time - _it->second)
.count();
m_started.erase(_it);
m_results[_idx].update(duration);
}
template <category_enum Cat, typename Func>
static constexpr void is_category_defined(Func&& f)
{
if constexpr(((Cat == enabled_categories) || ...))
{
f();
}
}
static constexpr std::array<category_enum, sizeof...(enabled_categories)>
compiledCategories = { enabled_categories... };
static inline std::unordered_map<indexed_category, time_point, indexed_category_hash>
m_started;
static inline std::array<result_data, _max_categories> m_results{};
static inline std::bitset<_max_categories> m_enabled;
static inline std::mutex m_mutex;
};
#ifdef ROCPROFSYS_ENABLE_BENCHMARK
using _benchmark_impl = benchmark::benchmark_impl<
static_cast<bool>(ROCPROFSYS_ENABLE_BENCHMARK), benchmark::category,
benchmark::category::kernel_dispatch, benchmark::category::memory_copy,
benchmark::category::memory_allocate, benchmark::category::db_entry_kernel_dispatch,
benchmark::category::db_entry_memory_copy,
benchmark::category::db_entry_memory_allocate,
benchmark::category::perfetto_kernel_dispatch,
benchmark::category::sdk_tool_buffered_tracing>;
#else
using _benchmark_impl = benchmark::benchmark_impl<false, benchmark::category>;
#endif
} // namespace
template <category... categories>
void
start()
{
_benchmark_impl::template start<categories...>();
}
template <category... categories>
void
end()
{
_benchmark_impl::template end<categories...>();
}
template <category... categories>
[[nodiscard]] auto
scoped_trace()
{
return _benchmark_impl::template scoped_trace<categories...>();
}
inline void
init_from_env(const char* envVar = "BENCHMARK_CATEGORIES")
{
_benchmark_impl::init_from_env(envVar);
}
inline void
show_results()
{
_benchmark_impl::show_results();
}
} // namespace benchmark
} // namespace rocprofsys
@@ -1,68 +0,0 @@
// Copyright (c) 2018-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
// with 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:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
//
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimers in the
// documentation and/or other materials provided with the distribution.
//
// * Neither the names of Advanced Micro Devices, Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this Software without specific prior written permission.
//
// 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
// CONTRIBUTORS 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 WITH
// THE SOFTWARE.
#pragma once
#include <string_view>
namespace rocprofsys
{
namespace benchmark
{
enum class category
{
kernel_dispatch,
db_entry_kernel_dispatch,
memory_copy,
db_entry_memory_copy,
memory_allocate,
db_entry_memory_allocate,
perfetto_kernel_dispatch,
sdk_tool_buffered_tracing,
count
};
constexpr std::string_view
to_string(category cat)
{
switch(cat)
{
case category::kernel_dispatch: return "kernel_dispatch";
case category::db_entry_kernel_dispatch: return "db_entry_kernel_dispatch";
case category::memory_copy: return "memory_copy";
case category::memory_allocate: return "memory_allocate";
case category::db_entry_memory_copy: return "db_entry_memory_copy";
case category::db_entry_memory_allocate: return "db_entry_memory_allocate";
case category::perfetto_kernel_dispatch: return "perfetto_kernel_dispatch";
case category::sdk_tool_buffered_tracing: return "sdk_tool_buffered_tracing";
default: return "unknown";
}
}
} // namespace benchmark
} // namespace rocprofsys
@@ -96,6 +96,7 @@ ROCPROFSYS_DEFINE_CATEGORY(category, rocm_hip_api, ROCPROFSYS_CATEGORY_ROCM_HIP_
ROCPROFSYS_DEFINE_CATEGORY(category, rocm_hsa_api, ROCPROFSYS_CATEGORY_ROCM_HSA_API, "rocm_hsa_api", "ROCm HSA functions")
ROCPROFSYS_DEFINE_CATEGORY(category, rocm_kernel_dispatch, ROCPROFSYS_CATEGORY_ROCM_KERNEL_DISPATCH, "rocm_kernel_dispatch", "ROCm Kernel dispatch")
ROCPROFSYS_DEFINE_CATEGORY(category, rocm_memory_copy, ROCPROFSYS_CATEGORY_ROCM_MEMORY_COPY, "rocm_memory_copy", "ROCm Async Memory Copy")
ROCPROFSYS_DEFINE_CATEGORY(category, rocm_memory_allocate, ROCPROFSYS_CATEGORY_ROCM_MEMORY_ALLOCATE, "rocm_memory_allocate", "ROCm Memory Allocations")
ROCPROFSYS_DEFINE_CATEGORY(category, rocm_hip_stream, ROCPROFSYS_CATEGORY_ROCM_HIP_STREAM, "rocm_hip_stream", "ROCm HIP Stream")
ROCPROFSYS_DEFINE_CATEGORY(category, rocm_scratch_memory, ROCPROFSYS_CATEGORY_ROCM_SCRATCH_MEMORY, "rocm_scratch_memory", "ROCm kernel scratch memory reallocations")
ROCPROFSYS_DEFINE_CATEGORY(category, rocm_page_migration, ROCPROFSYS_CATEGORY_ROCM_PAGE_MIGRATION, "rocm_page_migration", "ROCm memory page migration")
@@ -167,6 +168,7 @@ using name = perfetto_category<Tp...>;
ROCPROFSYS_PERFETTO_CATEGORY(category::rocm_hsa_api), \
ROCPROFSYS_PERFETTO_CATEGORY(category::rocm_kernel_dispatch), \
ROCPROFSYS_PERFETTO_CATEGORY(category::rocm_memory_copy), \
ROCPROFSYS_PERFETTO_CATEGORY(category::rocm_memory_allocate), \
ROCPROFSYS_PERFETTO_CATEGORY(category::rocm_hip_stream), \
ROCPROFSYS_PERFETTO_CATEGORY(category::rocm_scratch_memory), \
ROCPROFSYS_PERFETTO_CATEGORY(category::rocm_page_migration), \
@@ -1,3 +1,25 @@
// MIT License
//
// Copyright (c) 2022-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 "cpu.hpp"
#include "agent_manager.hpp"
@@ -153,6 +175,7 @@ query_cpu_agents()
auto logical_id = id_count++;
auto id = cpu_count++;
auto cur_agent = agent{ agent_type::CPU,
0,
id,
node_id,
logical_id,
@@ -124,19 +124,20 @@ query_rocm_agents()
auto& _agent_manager = agent_manager::get_instance();
for(size_t i = 0; i < num_agents; ++i)
{
const auto* _agent = static_cast<const rocprofiler_agent_v0_t*>(agents[i]);
auto cur_agent = agent{
const auto* _agent = static_cast<const rocprofiler_agent_v0_t*>(agents[i]);
agent cur_agent;
cur_agent.type =
(_agent->type == ROCPROFILER_AGENT_TYPE_GPU ? agent_type::GPU
: agent_type::CPU),
_agent->device_id,
_agent->node_id,
_agent->logical_node_id,
_agent->logical_node_type_id,
std::string(_agent->name),
std::string(_agent->vendor_name),
std::string(_agent->product_name),
std::string(_agent->model_name),
};
: agent_type::CPU);
cur_agent.handle = _agent->id.handle;
cur_agent.device_id = _agent->device_id;
cur_agent.node_id = _agent->node_id;
cur_agent.logical_node_id = _agent->logical_node_id;
cur_agent.logical_node_type_id = _agent->logical_node_type_id;
cur_agent.name = std::string(_agent->name);
cur_agent.model_name = std::string(_agent->model_name);
cur_agent.vendor_name = std::string(_agent->vendor_name);
cur_agent.product_name = std::string(_agent->product_name);
_agent_manager.insert_agent(cur_agent);
}
return ROCPROFILER_STATUS_SUCCESS;
@@ -1,3 +1,25 @@
# 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.
set(rocpd_sources
${CMAKE_CURRENT_LIST_DIR}/data_processor.cpp
${CMAKE_CURRENT_LIST_DIR}/json.cpp
@@ -69,8 +69,7 @@ data_processor::initialize_metadata()
size_t
data_processor::insert_string(const char* str)
{
std::lock_guard<std::mutex> lock(_data_mutex);
auto it = _string_map.find(str);
auto it = _string_map.find(str);
if(it != _string_map.end()) return _string_map.at(str);
data_storage::queries::table_insert_query query;
@@ -242,24 +241,12 @@ data_processor::insert_sample(const char* track, uint64_t timestamp, size_t even
}
size_t
data_processor::insert_event(size_t category_id, size_t stack_id, size_t parent_stack_id,
size_t correlation_id, const char* call_stack,
const char* line_info, const char* extdata)
data_processor::insert_event(size_t string_primary_key, size_t stack_id,
size_t parent_stack_id, size_t correlation_id,
const char* call_stack, const char* line_info,
const char* extdata)
{
std::lock_guard<std::mutex> lock(_data_mutex);
auto it = _category_map.find(category_id);
if(it == _category_map.end())
{
std::ostringstream oss;
oss << "Insert event failed! Error: Unknown category id: " << category_id
<< " for UPID: " << _upid;
throw std::runtime_error(oss.str());
}
ROCPROFSYS_VERBOSE(3, "Insert event category id: %ld, string id: %ld\n", category_id,
it->second);
_insert_event_statement(_upid.c_str(), it->second, stack_id, parent_stack_id,
_insert_event_statement(_upid.c_str(), string_primary_key, stack_id, parent_stack_id,
correlation_id, call_stack, line_info, extdata);
return data_storage::database::get_instance().get_last_insert_id();
}
@@ -456,7 +443,6 @@ void
data_processor::insert_args(size_t event_id, size_t position, const char* type,
const char* name, const char* value, const char* extdata)
{
std::lock_guard<std::mutex> lock(_data_mutex);
_insert_args_statement(_upid.c_str(), event_id, position, type, name, value, extdata);
}
@@ -464,40 +450,24 @@ void
data_processor::insert_stream_info(size_t stream_id, size_t node_id, size_t process_id,
const char* name, const char* extdata)
{
if(_stream_ids.count(stream_id) > 0)
{
// ROCPROFSYS_WARNING(
// 1, "Insert stream info failed! Error: Stream ID %ld already exists!\n",
// stream_id);
return;
}
data_storage::queries::table_insert_query query;
data_storage::database::get_instance().execute_query(
query.set_table_name("rocpd_info_stream_" + _upid)
.set_columns("id", "guid", "nid", "pid", "name", "extdata")
.set_values(stream_id, _upid, node_id, process_id, name, extdata)
.get_query_string());
_stream_ids.insert(stream_id);
}
void
data_processor::insert_queue_info(size_t queue_id, size_t node_id, size_t process_id,
const char* name, const char* extdata)
{
if(_queue_ids.count(queue_id) > 0)
{
// ROCPROFSYS_WARNING(
// 1, "Insert queue info failed! Error: Queue ID %ld already exists!\n",
// queue_id);
return;
}
data_storage::queries::table_insert_query query;
data_storage::database::get_instance().execute_query(
query.set_table_name("rocpd_info_queue_" + _upid)
.set_columns("id", "guid", "nid", "pid", "name", "extdata")
.set_values(queue_id, _upid, node_id, process_id, name, extdata)
.get_query_string());
_queue_ids.insert(queue_id);
}
void
@@ -506,20 +476,9 @@ data_processor::insert_code_object(size_t id, size_t node_id, size_t process_id,
uint64_t ld_size, uint64_t ld_delta,
const char* storage_type, const char* extdata)
{
if(_code_object_ids.count(id) > 0)
{
// ROCPROFSYS_WARNING(
// 1,
// "Insert code object info failed! Error: Code object ID %ld already
// exists!\n", id);
return;
}
ROCPROFSYS_VERBOSE(2, "Insert code object with ID: %ld\n", id);
std::lock_guard<std::mutex> lock(_data_mutex);
_insert_code_object_statement(id, _upid.c_str(), node_id, process_id, agent_id, uri,
ld_base, ld_size, ld_delta, storage_type, extdata);
_code_object_ids.insert(id);
}
void
@@ -530,40 +489,11 @@ data_processor::insert_kernel_symbol(
uint32_t private_segment_size, uint32_t sgrp_count, uint32_t arch_vgrp_count,
uint32_t accum_vgrp_count, const char* extdata)
{
if(_kernel_sym_ids.count(id) > 0)
{
// ROCPROFSYS_WARNING(
// 1,
// "Insert kernel symbol failed! Error: Kernel symbol ID %ld already
// exists!\n", id);
return;
}
ROCPROFSYS_VERBOSE(2, "Insert kernel symbol: %s with ID: %ld\n", name, id);
std::lock_guard<std::mutex> lock(_data_mutex);
_insert_kernel_symbol_statement(
id, _upid.c_str(), node_id, process_id, code_obj_id, name, display_name,
kernel_obj, kernarg_segmnt_size, kernarg_segment_alignment, group_segment_size,
private_segment_size, sgrp_count, arch_vgrp_count, accum_vgrp_count, extdata);
_kernel_sym_ids.insert(id);
}
void
data_processor::insert_category(size_t category_id, const char* name)
{
auto it = _category_map.find(category_id);
if(it != _category_map.end())
{
// ROCPROFSYS_WARNING(
// 1, "Insert category failed! Error: Category %s already exist!\n", name);
return;
}
auto name_id = insert_string(name);
std::lock_guard<std::mutex> lock(_data_mutex);
ROCPROFSYS_VERBOSE(2, "Insert category: name: %s, id: %ld, name id: %ld\n", name,
category_id, name_id);
_category_map.emplace(category_id, name_id);
}
void
@@ -571,7 +501,6 @@ data_processor::insert_region(size_t node_id, size_t process_id, size_t thread_i
uint64_t start, uint64_t end, size_t name_id,
size_t event_id, const char* extdata)
{
std::lock_guard<std::mutex> lock(_data_mutex);
ROCPROFSYS_VERBOSE(2, "Insert region for event id: %ld\n", event_id);
_insert_region_statement(_upid.c_str(), node_id, process_id, thread_id, start, end,
@@ -587,8 +516,6 @@ data_processor::insert_kernel_dispatch(
size_t grid_size_x, size_t grid_size_y, size_t grid_size_z, size_t region_name_id,
size_t event_id, const char* extdata)
{
std::lock_guard<std::mutex> lock(_data_mutex);
ROCPROFSYS_VERBOSE(2, "Insert kernel dispatch for event id: %ld\n", event_id);
_insert_kernel_dispatch_statement(
@@ -607,8 +534,6 @@ data_processor::insert_memory_copy(size_t node_id, size_t process_id, size_t thr
size_t region_name_id, size_t event_id,
const char* extdata)
{
std::lock_guard<std::mutex> lock(_data_mutex);
_insert_memory_copy_statement(_upid.c_str(), node_id, process_id, thread_id, start,
end, name_id, dst_agent_id, dst_addr, src_agent_id,
src_addr, size, queue_id, stream_id, region_name_id,
@@ -663,6 +588,18 @@ data_processor::insert_thread_info(size_t node_id, size_t parent_process_id,
return thread_idx;
}
size_t
data_processor::map_thread_id_to_primary_key(size_t thread_id)
{
auto it = _thread_id_map.find(thread_id);
if(it == _thread_id_map.end())
{
throw std::invalid_argument("Given thread id don't exist");
}
return _thread_id_map.at(thread_id);
}
void
data_processor::flush()
{
@@ -39,7 +39,7 @@ struct data_processor
using insert_event_stmt =
std::function<void(const char*, size_t, size_t, size_t, size_t, const char*,
const char*, const char*)>;
using insert_pmc_event_stms =
using insert_pmc_event_stmt =
std::function<void(const char*, size_t, size_t, double, const char*)>;
using insert_sample_stmt =
std::function<void(const char*, size_t, uint64_t, size_t, const char*)>;
@@ -124,9 +124,10 @@ public:
void insert_track(const char* track_name, size_t node_id, size_t process_id,
std::optional<size_t> thread_id, const char* extdata = "{}");
size_t insert_event(size_t category_id, size_t stack_id, size_t parent_stack_id,
size_t correlation_id, const char* call_stack = "{}",
const char* line_info = "{}", const char* extdata = "{}");
size_t insert_event(size_t string_primary_key, size_t stack_id,
size_t parent_stack_id, size_t correlation_id,
const char* call_stack = "{}", const char* line_info = "{}",
const char* extdata = "{}");
void insert_pmc_event(size_t event_id, size_t agent_id, const char* pmc_descriptor,
double value, const char* extdata = "{}");
@@ -143,8 +144,6 @@ public:
void insert_sample(const char* track, uint64_t timestamp, size_t event_id,
const char* extdata = "{}");
void insert_category(size_t category_id, const char* name);
void insert_region(size_t node_id, size_t process_id, size_t thread_id,
uint64_t start, uint64_t end, size_t name_id, size_t event_id,
const char* extdata = "{}");
@@ -199,6 +198,8 @@ public:
size_t stream_id, size_t event_id,
const char* extdata = "{}");
size_t map_thread_id_to_primary_key(size_t thread_id);
void flush();
private:
@@ -223,16 +224,10 @@ private:
std::unordered_map<pmc_identifier, size_t, pmc_identifier_hash, pmc_identifier_equal>
_pmc_descriptor_map;
std::unordered_map<size_t, size_t> _thread_id_map;
std::unordered_map<size_t, size_t> _category_map;
std::unordered_map<std::string, size_t> _string_map;
std::set<uint64_t> _code_object_ids;
std::set<uint64_t> _kernel_sym_ids;
std::set<uint64_t> _stream_ids;
std::set<uint64_t> _queue_ids;
insert_event_stmt _insert_event_statement;
insert_pmc_event_stms _insert_pmc_event_statement;
insert_pmc_event_stmt _insert_pmc_event_statement;
insert_sample_stmt _insert_sample_statement;
insert_region_stmt _insert_region_statement;
insert_kernel_dispatch_stmt _insert_kernel_dispatch_statement;
@@ -244,8 +239,6 @@ private:
insert_memory_alloc_no_agent_stmt _insert_memory_alloc_no_agent_statement;
std::string _upid{};
std::mutex _data_mutex;
};
} // namespace rocpd
@@ -93,9 +93,8 @@ database::initialize_schema()
return new_file_path;
}
}
return std::string(
"rocprofiler-systems/source/lib/core/rocpd/data_storage/schema/")
.append(filename);
// TODO: Update to look for the system's rocpd schema
return std::string("source/lib/core/rocpd/data_storage/schema/").append(filename);
};
std::vector<std::string_view> schema_files = { "rocpd_tables.sql", "rocpd_views.sql",
@@ -118,9 +117,11 @@ database::initialize_schema()
std::string query = ss_query.str();
std::regex upid_pattern("\\{\\{uuid\\}\\}");
std::regex guid_pattern("\\{\\{guid\\}\\}");
std::regex view_upid_pattern("\\{\\{view_upid\\}\\}");
query = std::regex_replace(query, upid_pattern, "_" + get_upid());
query = std::regex_replace(query, guid_pattern, get_upid());
query = std::regex_replace(query, view_upid_pattern, "");
validate_sqlite3_result(
@@ -495,6 +495,9 @@ get_buffered_domains()
const auto supported = std::unordered_set<rocprofiler_buffer_tracing_kind_t>{
ROCPROFILER_BUFFER_TRACING_KERNEL_DISPATCH,
ROCPROFILER_BUFFER_TRACING_MEMORY_COPY,
# if(ROCPROFILER_VERSION >= 600)
ROCPROFILER_BUFFER_TRACING_MEMORY_ALLOCATION,
# endif
# if(ROCPROFILER_VERSION < 10000)
ROCPROFILER_BUFFER_TRACING_PAGE_MIGRATION,
# endif
@@ -540,6 +543,16 @@ get_buffered_domains()
{
_data.emplace(ROCPROFILER_BUFFER_TRACING_MARKER_CORE_API);
}
# if(ROCPROFILER_VERSION >= 600)
else if(itr == "memory_allocation")
{
_data.emplace(ROCPROFILER_BUFFER_TRACING_MEMORY_ALLOCATION);
}
# endif
else if(itr == "memory_copy")
{
_data.emplace(ROCPROFILER_BUFFER_TRACING_MEMORY_COPY);
}
else
{
for(size_t idx = 0; idx < buffer_tracing_info.size(); ++idx)
@@ -0,0 +1,44 @@
# 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.
set(trace_cache_sources
${CMAKE_CURRENT_LIST_DIR}/cache_manager.cpp
${CMAKE_CURRENT_LIST_DIR}/storage_parser.cpp
${CMAKE_CURRENT_LIST_DIR}/buffer_storage.cpp
${CMAKE_CURRENT_LIST_DIR}/metadata_registry.cpp
${CMAKE_CURRENT_LIST_DIR}/rocpd_post_processing.cpp
)
set(trace_cache_headers
${CMAKE_CURRENT_LIST_DIR}/cache_manager.hpp
${CMAKE_CURRENT_LIST_DIR}/storage_parser.hpp
${CMAKE_CURRENT_LIST_DIR}/buffer_storage.hpp
${CMAKE_CURRENT_LIST_DIR}/cache_utility.hpp
${CMAKE_CURRENT_LIST_DIR}/metadata_registry.hpp
${CMAKE_CURRENT_LIST_DIR}/rocpd_post_processing.hpp
${CMAKE_CURRENT_LIST_DIR}/sample_type.hpp
)
target_sources(
rocprofiler-systems-core-library
PRIVATE ${trace_cache_sources} ${trace_cache_headers}
)
@@ -0,0 +1,178 @@
// 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 "buffer_storage.hpp"
#include "PTL/Task.hh"
#include "PTL/TaskGroup.hh"
#include "PTL/ThreadPool.hh"
#include "debug.hpp"
#include "library/runtime.hpp"
#include <chrono>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <unistd.h>
using namespace std::chrono_literals;
namespace rocprofsys
{
namespace trace_cache
{
namespace
{
constexpr auto CACHE_FILE_FLUSH_TIMEOUT = 10ms;
constexpr auto NUM_OF_THREADS = 1;
} // namespace
buffer_storage::buffer_storage(pid_t _pid)
{
ROCPROFSYS_SCOPED_SAMPLING_ON_CHILD_THREADS(false);
m_thread_pool = std::make_unique<PTL::ThreadPool>(NUM_OF_THREADS);
m_thread_pool->initialize_threadpool(NUM_OF_THREADS);
m_task_group = std::make_unique<PTL::TaskGroup<void>>(m_thread_pool.get());
m_task_group->exec([this, _pid]() {
std::ofstream _ofs(filename, std::ios::binary | std::ios::out);
if(!_ofs)
{
std::stringstream _ss;
_ss << "Error opening file for writing: " << filename;
throw std::runtime_error(_ss.str());
}
auto execute_flush = [&](std::ofstream& ofs, bool force = false) {
size_t _head, _tail;
{
std::lock_guard guard{ m_mutex };
_head = m_head;
_tail = m_tail;
if(_head == _tail)
{
return;
}
auto used_space =
m_head > m_tail ? (m_head - m_tail) : (buffer_size - m_tail + m_head);
if(!force && used_space < flush_threshold)
{
return;
}
m_tail = m_head;
}
if(_head > _tail)
{
ofs.write(reinterpret_cast<const char*>(m_buffer->data() + _tail),
_head - _tail);
}
else
{
ofs.write(reinterpret_cast<const char*>(m_buffer->data() + _tail),
buffer_size - _tail);
ofs.write(reinterpret_cast<const char*>(m_buffer->data()), _head);
}
};
ROCPROFSYS_DEBUG("Starting buffered storage flushing thread for pid %d",
static_cast<int>(_pid));
m_created_process = _pid;
std::mutex _shutdown_condition_mutex;
while(m_running)
{
execute_flush(_ofs);
std::unique_lock _lock{ _shutdown_condition_mutex };
m_shutdown_condition.wait_for(
_lock, std::chrono::milliseconds(CACHE_FILE_FLUSH_TIMEOUT),
[&]() { return !m_running; });
}
execute_flush(_ofs, true);
_ofs.close();
m_exit_finished = true;
m_exit_condition.notify_one();
});
}
void
buffer_storage::shutdown()
{
ROCPROFSYS_DEBUG("Buffer storage shutting down..");
m_running = false;
m_shutdown_condition.notify_all();
if(m_created_process != getpid())
{
ROCPROFSYS_DEBUG(
"Buffer storage is not created in same process as shutting down..");
return;
}
std::mutex _exit_mutex;
std::unique_lock _exit_lock{ _exit_mutex };
m_exit_condition.wait(_exit_lock, [&]() { return m_exit_finished; });
m_thread_pool->destroy_threadpool();
}
void
buffer_storage::fragment_memory()
{
auto* _data = m_buffer->data();
memset(_data + m_head, 0xFFFF, buffer_size - m_head);
*reinterpret_cast<entry_type*>(_data + m_head) = entry_type::fragmented_space;
size_t remaining_bytes = buffer_size - m_head - minimal_fragmented_memory_size;
*reinterpret_cast<size_t*>(_data + m_head + sizeof(entry_type)) = remaining_bytes;
m_head = 0;
}
uint8_t*
buffer_storage::reserve_memory_space(size_t len)
{
size_t _size;
{
std::lock_guard scope{ m_mutex };
if((m_head + len + minimal_fragmented_memory_size) > buffer_size)
{
fragment_memory();
}
_size = m_head;
m_head = m_head + len;
}
auto* _result = m_buffer->data() + _size;
memset(_result, 0, len);
return _result;
}
bool
buffer_storage::is_running() const
{
return m_running;
}
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,162 @@
// 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 "PTL/TaskGroup.hh"
#include "PTL/ThreadPool.hh"
#include "cache_utility.hpp"
#include "sample_type.hpp"
#include <cassert>
#include <condition_variable>
#include <cstdlib>
#include <cstring>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <stdint.h>
#include <string.h>
#include <thread>
#include <type_traits>
#include <PTL/PTL.hh>
#include <unistd.h>
namespace rocprofsys
{
namespace trace_cache
{
class cache_manager;
class buffer_storage
{
public:
static buffer_storage& get_instance();
template <typename... T>
void store(entry_type type, T&&... values)
{
if(!is_running())
{
throw std::runtime_error(
"Trying to use buffered storage while it is not running");
return;
}
constexpr bool is_supported_type = (supported_types::is_supported<T> && ...);
static_assert(is_supported_type, "Supported types are const char*, char*, "
"unsigned long, unsigned int, and int.");
auto arg_size = get_size(values...);
auto total_size = arg_size + sizeof(type) + sizeof(size_t);
auto* reserved_memory = reserve_memory_space(total_size);
size_t position = 0;
auto store_value = [&](const auto& val) {
using Type = decltype(val);
size_t len = 0;
auto* dest = reserved_memory + position;
if constexpr(std::is_same_v<std::decay_t<Type>, const char*>)
{
len = strlen(val) + 1;
std::memcpy(dest, val, len);
}
else
{
using ClearType = std::decay_t<decltype(val)>;
len = sizeof(ClearType);
*reinterpret_cast<ClearType*>(dest) = val;
}
position += len;
};
store_value(type);
store_value(arg_size);
(store_value(values), ...);
}
private:
friend class cache_manager;
buffer_storage(pid_t _pid);
void shutdown();
bool is_running() const;
void fragment_memory();
uint8_t* reserve_memory_space(size_t len);
template <typename... Types>
struct typelist
{
template <typename T>
constexpr static bool is_supported =
(std::is_same_v<std::decay_t<T>, Types> || ...);
};
using supported_types = typelist<const char*, char*, uint64_t, int32_t, uint32_t>;
template <typename T>
static constexpr bool is_string_literal_v =
std::is_same_v<std::decay_t<T>, const char*> ||
std::is_same_v<std::decay_t<T>, char*>;
template <typename T>
constexpr size_t get_size_impl(T&& val)
{
if constexpr(is_string_literal_v<T>)
{
size_t size = 0;
while(val[size] != '\0')
{
size++;
}
return ++size;
}
else
{
return sizeof(T);
}
}
template <typename... T>
constexpr size_t get_size(T&&... val)
{
auto total_size = 0;
((total_size += get_size_impl(val)), ...);
return total_size;
}
private:
std::mutex m_mutex;
std::condition_variable m_exit_condition;
bool m_exit_finished{ false };
bool m_running{ true };
std::condition_variable m_shutdown_condition;
std::unique_ptr<PTL::ThreadPool>m_thread_pool;
std::unique_ptr<PTL::TaskGroup<void>> m_task_group;
size_t m_head{ 0 };
size_t m_tail{ 0 };
std::unique_ptr<buffer_array_t> m_buffer{ std::make_unique<buffer_array_t>() };
pid_t m_created_process;
};
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,79 @@
// 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 "cache_manager.hpp"
#include "core/config.hpp"
#include "core/trace_cache/storage_parser.hpp"
#include "debug.hpp"
#include "trace_cache/rocpd_post_processing.hpp"
namespace rocprofsys
{
namespace trace_cache
{
cache_manager&
cache_manager::get_instance()
{
static cache_manager instance;
return instance;
}
cache_manager::cache_manager()
: m_postprocessing{ m_metadata }
{
m_postprocessing.register_parser_callback(m_parser);
}
void
cache_manager::post_process()
{
if(m_storage.is_running())
{
ROCPROFSYS_WARNING(2, "Postprocessing called without previously shutting down "
"cache storage. Calling shutdown explicitly..\n");
shutdown();
}
if(get_use_rocpd())
{
ROCPROFSYS_PRINT(
"Generating rocpd with collected data. This may take a while..\n");
}
post_process_metadata();
m_parser.consume_storage();
}
void
cache_manager::post_process_metadata()
{
m_postprocessing.post_process_metadata();
}
void
cache_manager::shutdown()
{
m_storage.shutdown();
}
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,67 @@
// 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 "buffer_storage.hpp"
#include "core/trace_cache/rocpd_post_processing.hpp"
#include "metadata_registry.hpp"
#include "storage_parser.hpp"
namespace rocprofsys
{
namespace trace_cache
{
class cache_manager
{
public:
static cache_manager& get_instance();
buffer_storage& get_buffer_storage() { return m_storage; }
metadata_registry& get_metadata_regsitry() { return m_metadata; }
void shutdown();
void post_process();
private:
void post_process_metadata();
cache_manager();
buffer_storage m_storage{ getpid() };
metadata_registry m_metadata;
storage_parser m_parser{ getpid() };
rocpd_post_processing m_postprocessing;
};
inline metadata_registry&
get_metadata_registry()
{
return cache_manager::get_instance().get_metadata_regsitry();
}
inline buffer_storage&
get_buffer_storage()
{
return cache_manager::get_instance().get_buffer_storage();
}
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,45 @@
// 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 "sample_type.hpp"
#include <array>
#include <string>
#include <timemory/units.hpp>
#include <unistd.h>
namespace rocprofsys
{
namespace trace_cache
{
constexpr size_t buffer_size = 100 * tim::units::megabyte;
constexpr size_t flush_threshold = 80 * tim::units::megabyte;
const auto filename = "/tmp/buffered_storage_" + std::to_string(getpid()) + ".bin";
constexpr size_t minimal_fragmented_memory_size = sizeof(entry_type) + sizeof(size_t);
using buffer_array_t = std::array<uint8_t, buffer_size>;
constexpr auto ABSOLUTE = "ABS";
constexpr auto PERCENTAGE = "%";
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,296 @@
// 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 "metadata_registry.hpp"
#include <algorithm>
#include <cstdint>
namespace rocprofsys
{
namespace trace_cache
{
namespace
{
template <typename ReturnType, typename DataType, typename Filter>
std::optional<ReturnType>
get_type_info(const DataType& data, const Filter& filter)
{
std::optional<ReturnType> result = std::nullopt;
data.rlock([&filter, &result](const auto& _data) {
auto it = std::find_if(_data.begin(), _data.end(), filter);
result = it == _data.end() ? std::nullopt : std::optional<ReturnType>(*it);
});
return result;
}
template <typename T>
auto
assign_set_to_vector(T& result)
{
return [&result](const auto& _data) { result.assign(_data.cbegin(), _data.cend()); };
}
} // namespace
void
metadata_registry::set_process(const info::process& process)
{
m_process.wlock([&process](auto& _process) { _process = process; });
}
void
metadata_registry::add_pmc_info(const info::pmc& pmc_info)
{
m_pmc_infos.wlock([&pmc_info](auto& _data) {
if(_data.count(pmc_info) > 0)
{
return;
}
_data.emplace(pmc_info);
});
}
void
metadata_registry::add_thread_info(const info::thread& thread_info)
{
m_threads.wlock([&thread_info](auto& _data) {
if(_data.count(thread_info) > 0)
{
return;
}
_data.emplace(thread_info);
});
}
void
metadata_registry::add_track(const info::track& track_info)
{
m_tracks.wlock([&track_info](auto& _data) {
if(_data.count(track_info) > 0)
{
return;
}
_data.emplace(track_info);
});
}
void
metadata_registry::add_queue(const uint64_t& queue_handle)
{
m_queues.wlock([&queue_handle](auto& _data) {
if(_data.count(queue_handle) > 0)
{
return;
}
_data.emplace(queue_handle);
});
}
void
metadata_registry::add_stream(const uint64_t& stream_handle)
{
m_streams.wlock([&stream_handle](auto& _data) {
if(_data.count(stream_handle) > 0)
{
return;
}
_data.emplace(stream_handle);
});
}
void
metadata_registry::add_string(const std::string_view& string_value)
{
m_strings.wlock([&string_value](auto& _data) {
if(_data.count(string_value) > 0)
{
return;
}
_data.emplace(string_value);
});
}
info::process
metadata_registry::get_process_info() const
{
info::process result;
m_process.rlock([&result](const auto& _process) { result = _process; });
return result;
}
std::optional<info::pmc>
metadata_registry::get_pmc_info(const std::string_view& unique_name) const
{
return get_type_info<info::pmc>(m_pmc_infos, [&unique_name](const info::pmc& val) {
return val.name == unique_name;
});
}
std::optional<info::thread>
metadata_registry::get_thread_info(const uint32_t& thread_id) const
{
return get_type_info<info::thread>(m_threads, [&thread_id](const info::thread& val) {
return val.thread_id == thread_id;
});
}
std::optional<info::track>
metadata_registry::get_track_info(const std::string_view& track_name) const
{
return get_type_info<info::track>(m_tracks, [&track_name](const info::track& val) {
return val.track_name == track_name;
});
}
std::vector<info::pmc>
metadata_registry::get_pmc_info_list() const
{
std::vector<info::pmc> result;
m_pmc_infos.rlock(assign_set_to_vector(result));
return result;
}
std::vector<info::thread>
metadata_registry::get_thread_info_list() const
{
std::vector<info::thread> result;
m_threads.rlock(assign_set_to_vector(result));
return result;
}
std::vector<info::track>
metadata_registry::get_track_info_list() const
{
std::vector<info::track> result;
m_tracks.rlock(assign_set_to_vector(result));
return result;
}
std::vector<uint64_t>
metadata_registry::get_queue_list() const
{
std::vector<uint64_t> result;
m_queues.rlock(assign_set_to_vector(result));
return result;
}
std::vector<uint64_t>
metadata_registry::get_stream_list() const
{
std::vector<uint64_t> result;
m_streams.rlock(assign_set_to_vector(result));
return result;
}
std::vector<std::string_view>
metadata_registry::get_string_list() const
{
std::vector<std::string_view> result;
m_strings.rlock(assign_set_to_vector(result));
return result;
}
#if ROCPROFSYS_USE_ROCM
void
metadata_registry::add_code_object(
const rocprofiler_callback_tracing_code_object_load_data_t& code_object)
{
m_code_objects.wlock([&code_object](auto& _data) {
if(_data.count(code_object) > 0)
{
return;
}
_data.emplace(code_object);
});
}
void
metadata_registry::add_kernel_symbol(
const rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t&
kernel_symbol)
{
m_kernel_symbols.wlock([&kernel_symbol](auto& _data) {
if(_data.count(kernel_symbol) > 0)
{
return;
}
_data.emplace(kernel_symbol);
});
}
std::optional<rocprofiler_callback_tracing_code_object_load_data_t>
metadata_registry::get_code_object(uint64_t code_object_id) const
{
return get_type_info<rocprofiler_callback_tracing_code_object_load_data_t>(
m_code_objects,
[&code_object_id](
const rocprofiler_callback_tracing_code_object_load_data_t& val) {
return val.code_object_id == code_object_id;
});
}
std::optional<rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t>
metadata_registry::get_kernel_symbol(uint64_t kernel_id) const
{
return get_type_info<
rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t>(
m_kernel_symbols,
[&kernel_id](
const rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t&
val) { return val.kernel_id == kernel_id; });
}
std::vector<rocprofiler_callback_tracing_code_object_load_data_t>
metadata_registry::get_code_object_list() const
{
std::vector<rocprofiler_callback_tracing_code_object_load_data_t> result;
m_code_objects.rlock(assign_set_to_vector(result));
return result;
}
std::vector<rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t>
metadata_registry::get_kernel_symbol_list() const
{
std::vector<rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t>
result;
m_kernel_symbols.rlock(assign_set_to_vector(result));
return result;
}
rocprofiler::sdk::buffer_name_info_t<const char*>
metadata_registry::get_buffer_name_info() const
{
return m_buffered_tracing_info;
}
rocprofiler::sdk::callback_name_info_t<const char*>
metadata_registry::get_callback_tracing_info() const
{
return m_callback_tracing_info;
}
#endif
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,218 @@
// 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 "common/synchronized.hpp"
#include "core/agent.hpp"
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <optional>
#if ROCPROFSYS_USE_ROCM > 0
# include <rocprofiler-sdk/callback_tracing.h>
# include <rocprofiler-sdk/cxx/name_info.hpp>
#endif
#include <set>
#include <stdint.h>
#include <string.h>
#include <string>
#include <sys/types.h>
#include <unordered_set>
namespace rocprofsys
{
namespace trace_cache
{
namespace info
{
struct process
{
pid_t pid; // < Unique
pid_t ppid;
std::string command;
};
struct pmc
{
agent_type type;
size_t agent_type_index;
std::string target_arch;
size_t event_code;
size_t instance_id;
std::string name; // < Unique
std::string symbol;
std::string description;
std::string long_description;
std::string component;
std::string units;
std::string value_type;
std::string block;
std::string expression;
uint32_t is_constant;
uint32_t is_derived;
std::string extdata;
};
struct pmc_info_hash
{
std::size_t operator()(const pmc& _pmc) const noexcept
{
std::size_t h1 = std::hash<size_t>{}(static_cast<size_t>(_pmc.type));
std::size_t h2 = std::hash<size_t>{}(_pmc.agent_type_index);
std::size_t h3 = std::hash<std::string>{}(_pmc.name);
return h1 ^ (h2 << 1) ^ (h3 << 1);
}
};
struct pmc_info_equal
{
bool operator()(const pmc& lhs, const pmc& rhs) const noexcept
{
return lhs.type == rhs.type && lhs.agent_type_index == rhs.agent_type_index &&
lhs.name == rhs.name;
}
};
struct thread
{
int32_t parent_process_id;
int32_t process_id;
uint64_t thread_id; // < Unique
uint32_t start;
uint32_t end;
std::string extdata;
friend bool operator<(const thread& lhs, const thread& rhs)
{
return lhs.thread_id < rhs.thread_id;
}
};
struct track
{
std::string track_name; // < Unique
std::optional<size_t> thread_id;
std::string extdata;
friend bool operator<(const track& lhs, const track& rhs)
{
return lhs.track_name.compare(rhs.track_name) < 0;
}
};
#if ROCPROFSYS_USE_ROCM > 0
struct code_object_less
{
bool operator()(const rocprofiler_callback_tracing_code_object_load_data_t& lhs,
const rocprofiler_callback_tracing_code_object_load_data_t& rhs) const
{
return lhs.code_object_id < rhs.code_object_id;
}
};
struct kernel_symbol_less
{
bool operator()(
const rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t& lhs,
const rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t& rhs)
const
{
return lhs.kernel_object < rhs.kernel_object;
}
};
#endif
} // namespace info
class cache_manager;
struct metadata_registry
{
void set_process(const info::process& process);
void add_pmc_info(const info::pmc& pmc_info);
void add_thread_info(const info::thread& thread_info);
void add_track(const info::track& track_info);
void add_queue(const uint64_t& queue_handle);
void add_stream(const uint64_t& stream_handle);
void add_string(const std::string_view& string_value);
info::process get_process_info() const;
std::optional<info::pmc> get_pmc_info(const std::string_view& unique_name) const;
std::optional<info::thread> get_thread_info(const uint32_t& thread_id) const;
std::optional<info::track> get_track_info(const std::string_view& track_name) const;
std::vector<info::pmc> get_pmc_info_list() const;
std::vector<info::thread> get_thread_info_list() const;
std::vector<info::track> get_track_info_list() const;
std::vector<uint64_t> get_queue_list() const;
std::vector<uint64_t> get_stream_list() const;
std::vector<std::string_view> get_string_list() const;
#if ROCPROFSYS_USE_ROCM > 0
void add_code_object(
const rocprofiler_callback_tracing_code_object_load_data_t& code_object);
void add_kernel_symbol(
const rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t&
kernel_symbol);
std::vector<rocprofiler_callback_tracing_code_object_load_data_t>
get_code_object_list() const;
std::vector<rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t>
get_kernel_symbol_list() const;
std::optional<rocprofiler_callback_tracing_code_object_load_data_t> get_code_object(
uint64_t code_object_id) const;
std::optional<rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t>
get_kernel_symbol(uint64_t kernel_id) const;
rocprofiler::sdk::buffer_name_info_t<const char*> get_buffer_name_info() const;
rocprofiler::sdk::callback_name_info_t<const char*> get_callback_tracing_info() const;
#endif
private:
friend class cache_manager;
metadata_registry() = default;
common::synchronized<info::process> m_process;
common::synchronized<
std::unordered_set<info::pmc, info::pmc_info_hash, info::pmc_info_equal>>
m_pmc_infos;
common::synchronized<std::set<info::thread>> m_threads;
common::synchronized<std::set<info::track>> m_tracks;
common::synchronized<std::set<uint64_t>> m_streams;
common::synchronized<std::set<uint64_t>> m_queues;
common::synchronized<std::unordered_set<std::string_view>> m_strings;
#if ROCPROFSYS_USE_ROCM > 0
common::synchronized<std::set<rocprofiler_callback_tracing_code_object_load_data_t,
info::code_object_less>>
m_code_objects;
common::synchronized<
std::set<rocprofiler_callback_tracing_code_object_kernel_symbol_register_data_t,
info::kernel_symbol_less>>
m_kernel_symbols;
rocprofiler::sdk::buffer_name_info_t<const char*> m_buffered_tracing_info{
rocprofiler::sdk::get_buffer_tracing_names<const char*>()
};
rocprofiler::sdk::callback_name_info_t<const char*> m_callback_tracing_info{
rocprofiler::sdk::get_callback_tracing_names<const char*>()
};
#endif
};
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,574 @@
// 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 "trace_cache/rocpd_post_processing.hpp"
#include "agent_manager.hpp"
#include "common.hpp"
#include "config.hpp"
#include "debug.hpp"
#include "library/thread_info.hpp"
#include "node_info.hpp"
#include "rocpd/data_processor.hpp"
#include "trace_cache/metadata_registry.hpp"
#include "trace_cache/sample_type.hpp"
#include "trace_cache/storage_parser.hpp"
#include <cstdint>
#include <limits>
#include <stdexcept>
#include <string>
#include <timemory/utility/demangle.hpp>
#if ROCPROFSYS_USE_ROCM > 0
# include "library/rocprofiler-sdk/fwd.hpp"
# include <rocprofiler-sdk/context.h>
# include <rocprofiler-sdk/version.h>
#endif
namespace rocprofsys
{
namespace trace_cache
{
namespace
{
rocpd::data_processor&
get_data_processor()
{
return rocpd::data_processor::get_instance();
}
#if ROCPROFSYS_USE_ROCM > 0
auto
get_handle_from_code_object(
const rocprofiler_callback_tracing_code_object_load_data_t& code_object)
{
# if(ROCPROFILER_VERSION >= 600)
return code_object.agent_id.handle;
# else
return code_object.rocp_agent.handle;
# endif
}
#endif
} // namespace
postprocessing_callback
rocpd_post_processing::get_kernel_dispatch_callback() const
{
return [&]([[maybe_unused]] const storage_parsed_type_base& parsed) {
#if ROCPROFSYS_USE_ROCM > 0
auto _kds = static_cast<const struct kernel_dispatch_sample&>(parsed);
auto& data_processor = get_data_processor();
auto& agent_manager = agent_manager::get_instance();
auto& n_info = node_info::get_instance();
auto process = m_metadata.get_process_info();
auto agent_primary_key =
agent_manager.get_agent_by_handle(_kds.agent_id_handle).base_id;
auto thread_primary_key =
data_processor.map_thread_id_to_primary_key(_kds.thread_id);
auto category_id = data_processor.insert_string(
trait::name<category::rocm_kernel_dispatch>::value);
auto kernel_symbol = m_metadata.get_kernel_symbol(_kds.kernel_id);
if(!kernel_symbol.has_value())
{
throw std::runtime_error("Kernel symbol is missing for kernel dispatch");
return;
}
auto region_name_primary_key = data_processor.insert_string(
tim::demangle(kernel_symbol->kernel_name).c_str());
auto stack_id = _kds.correlation_id_internal;
auto parent_stack_id = _kds.correlation_id_ancestor;
auto correlation_id = 0;
auto event_id = data_processor.insert_event(category_id, stack_id,
parent_stack_id, correlation_id);
data_processor.insert_kernel_dispatch(
n_info.id, process.pid, thread_primary_key, agent_primary_key, _kds.kernel_id,
_kds.dispatch_id, _kds.queue_id_handle, _kds.stream_handle,
_kds.start_timestamp, _kds.end_timestamp, _kds.private_segment_size,
_kds.group_segment_size, _kds.workgroup_size_x, _kds.workgroup_size_y,
_kds.workgroup_size_z, _kds.grid_size_x, _kds.grid_size_y, _kds.grid_size_z,
region_name_primary_key, event_id);
#endif
};
}
postprocessing_callback
rocpd_post_processing::get_memory_copy_callback() const
{
return [&]([[maybe_unused]] const storage_parsed_type_base& parsed) {
#if ROCPROFSYS_USE_ROCM > 0
auto _mcs = static_cast<const struct memory_copy_sample&>(parsed);
auto& data_processor = get_data_processor();
auto& agent_manager = agent_manager::get_instance();
auto& n_info = node_info::get_instance();
auto process = m_metadata.get_process_info();
auto _name = std::string{ m_metadata.get_buffer_name_info().at(
static_cast<rocprofiler_buffer_tracing_kind_t>(_mcs.kind),
static_cast<rocprofiler_tracing_operation_t>(_mcs.operation)) };
auto name_primary_key = data_processor.insert_string(_name.c_str());
auto category_primary_key =
data_processor.insert_string(trait::name<category::rocm_memory_copy>::value);
auto thread_primary_key =
data_processor.map_thread_id_to_primary_key(_mcs.thread_id);
auto dst_agent_primary_key =
agent_manager.get_agent_by_handle(_mcs.dst_agent_id_handle).base_id;
auto src_agent_primary_key =
agent_manager.get_agent_by_handle(_mcs.src_agent_id_handle).base_id;
auto stack_id = _mcs.correlation_id_internal;
auto parent_stack_id = _mcs.correlation_id_ancestor;
auto correlation_id = 0;
auto queue_id = 0;
auto event_primary_key = data_processor.insert_event(
category_primary_key, stack_id, parent_stack_id, correlation_id);
data_processor.insert_memory_copy(
n_info.id, process.pid, thread_primary_key, _mcs.start_timestamp,
_mcs.end_timestamp, name_primary_key, dst_agent_primary_key,
_mcs.dst_address_value, src_agent_primary_key, _mcs.src_address_value,
_mcs.bytes, queue_id, _mcs.stream_handle, name_primary_key,
event_primary_key);
#endif
};
}
#if(ROCPROFSYS_USE_ROCM > 0 && ROCPROFILER_VERSION >= 600)
postprocessing_callback
rocpd_post_processing::get_memory_allocate_callback() const
{
# if ROCPROFSYS_USE_ROCM > 0
auto memtype_to_db =
[](std::string_view memory_type) -> std::pair<std::string, std::string> {
constexpr auto MEMORY_PREFIX = std::string_view{ "MEMORY_ALLOCATION_" };
constexpr auto SCRATCH_PREFIX = std::string_view{ "SCRATCH_MEMORY_" };
constexpr auto VMEM_PREFIX = std::string_view{ "VMEM_" };
constexpr auto ASYNC_PREFIX = std::string_view{ "ASYNC_" };
std::string _type;
std::string _level;
if(memory_type.find(MEMORY_PREFIX) == 0)
{
_type = memory_type.substr(MEMORY_PREFIX.length());
if(_type.find(VMEM_PREFIX) == 0)
{
_type = _type.substr(VMEM_PREFIX.length());
_level = "VIRTUAL";
}
else
{
_level = "REAL";
}
}
else if(memory_type.find(SCRATCH_PREFIX) == 0)
{
_type = memory_type.substr(SCRATCH_PREFIX.length());
_level = "SCRATCH";
if(memory_type.find(ASYNC_PREFIX) == 0)
{
_type = memory_type.substr(ASYNC_PREFIX.length()); // RECLAIM
}
}
if(_type == "ALLOCATE")
{
_type = "ALLOC";
}
return std::make_pair(_type, _level);
};
# endif
return [&]([[maybe_unused]] const storage_parsed_type_base& parsed) {
# if ROCPROFSYS_USE_ROCM > 0
auto _mas = static_cast<const struct memory_allocate_sample&>(parsed);
auto& data_processor = get_data_processor();
auto& agent_manager = agent_manager::get_instance();
auto& n_info = node_info::get_instance();
auto process = m_metadata.get_process_info();
auto thread_primary_key =
data_processor.map_thread_id_to_primary_key(_mas.thread_id);
auto agent_primary_key = std::optional<uint64_t>{};
const auto invalid_context = ROCPROFILER_CONTEXT_NONE;
if(_mas.agent_id_handle != invalid_context.handle)
{
{
agent_primary_key =
agent_manager.get_agent_by_handle(_mas.agent_id_handle).base_id;
}
const auto* _name = m_metadata.get_buffer_name_info().at(
static_cast<rocprofiler_buffer_tracing_kind_t>(_mas.kind),
static_cast<rocprofiler_tracing_operation_t>(_mas.operation));
auto [type, level] = memtype_to_db(_name);
auto stack_id = _mas.correlation_id_internal;
auto parent_stack_id = _mas.correlation_id_ancestor;
auto correlation_id = 0;
auto queue_id = 0;
auto category_primary_key = data_processor.insert_string(
trait::name<category::rocm_memory_allocate>::value);
auto event_primary_key = data_processor.insert_event(
category_primary_key, stack_id, parent_stack_id, correlation_id);
data_processor.insert_memory_alloc(
n_info.id, process.pid, thread_primary_key, agent_primary_key,
type.c_str(), level.c_str(), _mas.start_timestamp, _mas.end_timestamp,
_mas.address_value, _mas.allocation_size, queue_id, _mas.stream_handle,
event_primary_key);
# endif
};
};
}
#endif
postprocessing_callback
rocpd_post_processing::get_region_callback() const
{
[[maybe_unused]] auto parse_args = []([[maybe_unused]] const std::string& arg_str) {
#if ROCPROFSYS_USE_ROCM > 0
rocprofiler_sdk::function_args_t args;
const std::string delimiter = ";;";
auto split = [](const std::string& str, const std::string& _delimiter) {
std::vector<std::string> tokens;
size_t start = 0;
size_t end = str.find(_delimiter);
while(end != std::string::npos)
{
tokens.push_back(str.substr(start, end - start));
start = end + _delimiter.length();
end = str.find(_delimiter, start);
}
return tokens;
};
auto tokens = split(arg_str, delimiter);
// Ensure the number of tokens is a multiple of 4
if(tokens.size() % 4 != 0)
{
throw std::invalid_argument("Malformed argument string.");
}
for(auto it = tokens.begin(); it != tokens.end(); it += 4)
{
rocprofiler_sdk::argument_info arg = { static_cast<uint32_t>(std::stoi(*it)),
*(it + 1), *(it + 2), *(it + 3) };
args.push_back(arg);
}
return args;
#endif
};
return [&]([[maybe_unused]] const storage_parsed_type_base& parsed) {
#if ROCPROFSYS_USE_ROCM > 0
auto _rs = static_cast<const struct region_sample&>(parsed);
auto& data_processor = get_data_processor();
auto& n_info = node_info::get_instance();
auto process = m_metadata.get_process_info();
auto thread_primary_key =
data_processor.map_thread_id_to_primary_key(_rs.thread_id);
auto callback_tracing_info = m_metadata.get_callback_tracing_info();
auto _name = std::string{ callback_tracing_info.at(
static_cast<rocprofiler_callback_tracing_kind_t>(_rs.kind),
static_cast<rocprofiler_tracing_operation_t>(_rs.operation)) };
auto name_primary_key = data_processor.insert_string(_name.c_str());
auto category_primary_key = data_processor.insert_string(_rs.category.c_str());
size_t stack_id = _rs.correlation_id_internal;
size_t parent_stack_id = _rs.correlation_id_ancestor;
size_t correlation_id = 0;
auto event_primary_key =
data_processor.insert_event(category_primary_key, stack_id, parent_stack_id,
correlation_id, _rs.call_stack.c_str());
auto args = parse_args(_rs.args_str);
for(const auto& arg : args)
{
data_processor.insert_args(event_primary_key, arg.arg_number,
arg.arg_type.c_str(), arg.arg_name.c_str(),
arg.arg_value.c_str());
}
data_processor.insert_region(n_info.id, process.pid, thread_primary_key,
_rs.start_timestamp, _rs.end_timestamp,
name_primary_key, event_primary_key);
#endif
};
}
postprocessing_callback
rocpd_post_processing::get_in_time_sample_callback() const
{
return [&](const storage_parsed_type_base& parsed) {
auto _its = static_cast<const struct in_time_sample&>(parsed);
auto& data_processor = get_data_processor();
auto track_primary_key = data_processor.insert_string(_its.track_name.c_str());
auto event_id = data_processor.insert_event(
track_primary_key, _its.stack_id, _its.parent_stack_id, _its.correlation_id,
_its.call_stack.c_str(), _its.line_info.c_str(), _its.event_metadata.c_str());
data_processor.insert_sample(_its.track_name.c_str(), _its.timestamp_ns, event_id,
"{}");
};
}
postprocessing_callback
rocpd_post_processing::get_pmc_event_with_sample_callback() const
{
return [&](const storage_parsed_type_base& parsed) {
auto _pmc = static_cast<const struct pmc_event_with_sample&>(parsed);
auto& data_processor = get_data_processor();
auto track_primary_key = data_processor.insert_string(_pmc.track_name.c_str());
auto& agent_manager = agent_manager::get_instance();
auto agent_primary_key =
agent_manager.get_agent_by_handle(_pmc.agent_handle).base_id;
auto event_id = data_processor.insert_event(
track_primary_key, _pmc.stack_id, _pmc.parent_stack_id, _pmc.correlation_id,
_pmc.call_stack.c_str(), _pmc.line_info.c_str(), _pmc.event_metadata.c_str());
data_processor.insert_sample(_pmc.track_name.c_str(), _pmc.timestamp_ns, event_id,
"{}");
data_processor.insert_pmc_event(event_id, agent_primary_key,
_pmc.pmc_info_name.c_str(), _pmc.value);
};
}
rocpd_post_processing::rocpd_post_processing(metadata_registry& md)
: m_metadata(md)
{}
void
rocpd_post_processing::register_parser_callback([[maybe_unused]] storage_parser& parser)
{
#if ROCPROFSYS_USE_ROCM > 0
if(!get_use_rocpd())
{
return;
}
parser.register_type_callback(entry_type::region, get_region_callback());
parser.register_type_callback(entry_type::kernel_dispatch,
get_kernel_dispatch_callback());
parser.register_type_callback(entry_type::memory_copy, get_memory_copy_callback());
# if(ROCPROFILER_VERSION >= 600)
parser.register_type_callback(entry_type::memory_alloc,
get_memory_allocate_callback());
# endif
parser.register_type_callback(entry_type::in_time_sample,
get_in_time_sample_callback());
parser.register_type_callback(entry_type::pmc_event_with_sample,
get_pmc_event_with_sample_callback());
ROCPROFSYS_DEBUG("Buffer parser callbacks are registered..");
#endif
}
void
rocpd_post_processing::post_process_metadata()
{
#if ROCPROFSYS_USE_ROCM > 0
if(!get_use_rocpd())
{
return;
}
ROCPROFSYS_DEBUG("Post processing metadata..");
auto& data_processor = get_data_processor();
auto& agent_mngr = agent_manager::get_instance();
auto n_info = node_info::get_instance();
data_processor.insert_node_info(n_info.id, n_info.hash, n_info.machine_id.c_str(),
n_info.system_name.c_str(), n_info.node_name.c_str(),
n_info.release.c_str(), n_info.version.c_str(),
n_info.machine.c_str(), n_info.domain_name.c_str());
auto process_info = m_metadata.get_process_info();
data_processor.insert_process_info(n_info.id, process_info.ppid, process_info.pid, 0,
0, 0, 0, process_info.command.c_str(), "{}");
const auto& agents = agent_mngr.get_agents();
int counter = 0;
for(const auto& rocpd_agent : agents)
{
auto _base_id = rocpd::data_processor::get_instance().insert_agent(
n_info.id, process_info.pid,
((rocpd_agent->type == agent_type::GPU) ? "GPU" : "CPU"), counter++,
rocpd_agent->logical_node_id, rocpd_agent->logical_node_type_id,
rocpd_agent->device_id, rocpd_agent->name.c_str(),
rocpd_agent->model_name.c_str(), rocpd_agent->vendor_name.c_str(),
rocpd_agent->product_name.c_str(), "");
rocpd_agent->base_id = _base_id;
}
auto _string_list = m_metadata.get_string_list();
for(auto& _string : _string_list)
{
data_processor.insert_string(std::string(_string).c_str());
}
auto _thread_info_list = m_metadata.get_thread_info_list();
for(auto& t_info : _thread_info_list)
{
rocpd_insert_thread_id(t_info, n_info, process_info);
}
auto _track_info_list = m_metadata.get_track_info_list();
for(auto& track : _track_info_list)
{
auto thread_id =
track.thread_id.has_value()
? std::make_optional<size_t>(data_processor.map_thread_id_to_primary_key(
track.thread_id.value()))
: std::nullopt;
data_processor.insert_track(track.track_name.c_str(), n_info.id, process_info.pid,
thread_id);
}
auto _code_object_list = m_metadata.get_code_object_list();
for(const auto& code_object : _code_object_list)
{
auto dev_id =
agent_mngr.get_agent_by_handle(get_handle_from_code_object(code_object))
.base_id;
const char* strg_type = "UNKNOWN";
switch(code_object.storage_type)
{
case ROCPROFILER_CODE_OBJECT_STORAGE_TYPE_FILE: strg_type = "FILE"; break;
case ROCPROFILER_CODE_OBJECT_STORAGE_TYPE_MEMORY: strg_type = "MEMORY"; break;
default: break;
}
data_processor.insert_code_object(code_object.code_object_id, n_info.id,
process_info.pid, dev_id, code_object.uri,
code_object.load_base, code_object.load_size,
code_object.load_delta, strg_type);
}
auto _kernel_symbols_list = m_metadata.get_kernel_symbol_list();
for(const auto& kernel_symbol : _kernel_symbols_list)
{
auto kernel_name = tim::demangle(kernel_symbol.kernel_name);
data_processor.insert_kernel_symbol(
kernel_symbol.kernel_id, n_info.id, process_info.pid,
kernel_symbol.code_object_id, kernel_symbol.kernel_name, kernel_name.c_str(),
kernel_symbol.kernel_object, kernel_symbol.kernarg_segment_size,
kernel_symbol.kernarg_segment_alignment, kernel_symbol.group_segment_size,
kernel_symbol.private_segment_size, kernel_symbol.sgpr_count,
kernel_symbol.arch_vgpr_count, kernel_symbol.accum_vgpr_count);
data_processor.insert_string(kernel_name.c_str());
}
auto _queue_list = m_metadata.get_queue_list();
for(const auto& queue_handle : _queue_list)
{
std::stringstream ss;
ss << "Queue " << queue_handle;
data_processor.insert_queue_info(queue_handle, n_info.id, process_info.pid,
ss.str().c_str());
}
auto _stream_list = m_metadata.get_stream_list();
for(const auto& stream_handle : _stream_list)
{
std::stringstream ss;
ss << "Stream " << stream_handle;
data_processor.insert_stream_info(stream_handle, n_info.id, process_info.pid,
ss.str().c_str());
}
auto buffer_info_list = m_metadata.get_buffer_name_info();
for(const auto& buffer_info : buffer_info_list)
{
for(const auto& item : buffer_info.items())
{
data_processor.insert_string(*item.second);
}
}
auto callback_info_list = m_metadata.get_callback_tracing_info();
for(const auto& cb_info : callback_info_list)
{
for(const auto& item : cb_info.items())
{
data_processor.insert_string(*item.second);
}
}
auto pmc_info_list = m_metadata.get_pmc_info_list();
for(const auto& pmc_info : pmc_info_list)
{
const auto agent_primary_key =
agent_mngr.get_agent_by_type_index(pmc_info.agent_type_index, pmc_info.type)
.base_id;
data_processor.insert_pmc_description(
n_info.id, process_info.pid, agent_primary_key, pmc_info.target_arch.c_str(),
pmc_info.event_code, pmc_info.instance_id, pmc_info.name.c_str(),
pmc_info.symbol.c_str(), pmc_info.description.c_str(),
pmc_info.long_description.c_str(), pmc_info.component.c_str(),
pmc_info.units.c_str(), pmc_info.value_type.c_str(), pmc_info.block.c_str(),
pmc_info.expression.c_str(), pmc_info.is_constant, pmc_info.is_derived);
}
#endif
}
inline void
rocpd_post_processing::rocpd_insert_thread_id(info::thread& t_info,
const node_info& n_info,
const info::process& process_info) const
{
const auto& extended_info = thread_info::get(t_info.thread_id, SequentTID);
if(extended_info.has_value())
{
t_info.start = extended_info->get_start();
t_info.end = extended_info->get_stop();
}
std::stringstream ss;
ss << "Thread " << t_info.thread_id;
get_data_processor().insert_thread_info(n_info.id, process_info.ppid,
process_info.pid, t_info.thread_id,
ss.str().c_str(), t_info.start, t_info.end);
}
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,60 @@
// 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 "core/node_info.hpp"
#include "core/trace_cache/metadata_registry.hpp"
#include "core/trace_cache/storage_parser.hpp"
namespace rocprofsys
{
namespace trace_cache
{
class rocpd_post_processing
{
public:
rocpd_post_processing(metadata_registry& metadata);
void register_parser_callback(storage_parser& parser);
void post_process_metadata();
private:
using primary_key = size_t;
inline void rocpd_insert_thread_id(info::thread& t_info, const node_info& n_info,
const info::process& process_info) const;
postprocessing_callback get_kernel_dispatch_callback() const;
postprocessing_callback get_memory_copy_callback() const;
#if(ROCPROFILER_VERSION >= 600)
postprocessing_callback get_memory_allocate_callback() const;
#endif
postprocessing_callback get_region_callback() const;
postprocessing_callback get_in_time_sample_callback() const;
postprocessing_callback get_pmc_event_with_sample_callback() const;
metadata_registry& m_metadata;
};
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,198 @@
// 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 <stdint.h>
#include <string>
#include <unistd.h>
#include <utility>
#if ROCPROFSYS_USE_ROCM > 0
# include <rocprofiler-sdk/version.h>
#endif
namespace rocprofsys
{
namespace trace_cache
{
struct storage_parsed_type_base
{};
struct kernel_dispatch_sample : storage_parsed_type_base
{
// Timing fields
uint64_t start_timestamp;
uint64_t end_timestamp;
// Identification fields
uint64_t thread_id;
uint64_t agent_id_handle;
uint64_t kernel_id;
uint64_t dispatch_id;
uint64_t queue_id_handle;
// Correlation fields
uint64_t correlation_id_internal;
uint64_t correlation_id_ancestor;
// Dispatch configuration
uint32_t private_segment_size;
uint32_t group_segment_size;
uint32_t workgroup_size_x;
uint32_t workgroup_size_y;
uint32_t workgroup_size_z;
uint32_t grid_size_x;
uint32_t grid_size_y;
uint32_t grid_size_z;
// Stream handle
size_t stream_handle;
};
struct memory_copy_sample : storage_parsed_type_base
{
// Timing fields
uint64_t start_timestamp;
uint64_t end_timestamp;
// Identification fields
uint64_t thread_id;
uint64_t dst_agent_id_handle;
uint64_t src_agent_id_handle;
// Operation details
int32_t kind;
int32_t operation;
uint64_t bytes;
// Correlation fields
uint64_t correlation_id_internal;
uint64_t correlation_id_ancestor;
// Address fields (version dependent)
uint64_t dst_address_value;
uint64_t src_address_value;
// Stream handle
size_t stream_handle;
};
#if(ROCPROFILER_VERSION >= 600)
struct memory_allocate_sample : storage_parsed_type_base
{
// Timing fields
uint64_t start_timestamp;
uint64_t end_timestamp;
// Identification fields
uint64_t thread_id;
uint64_t agent_id_handle;
// Operation details
int32_t kind;
int32_t operation;
uint64_t allocation_size;
// Correlation fields
uint64_t correlation_id_internal;
uint64_t correlation_id_ancestor;
// Address fields (version dependent)
uint64_t address_value;
// Stream handle
size_t stream_handle;
};
#endif
struct region_sample : storage_parsed_type_base
{
region_sample() = default;
region_sample(uint64_t _thread_id, int32_t _kind, int32_t _operation,
uint64_t _correlation_id_internal, uint64_t _correlation_id_ancestor,
uint64_t _start_timestamp, uint64_t _end_timestamp,
std::string _call_stack, std::string _args_str, std::string _category)
: thread_id(_thread_id)
, kind(_kind)
, operation(_operation)
, correlation_id_internal(_correlation_id_internal)
, correlation_id_ancestor(_correlation_id_ancestor)
, start_timestamp(_start_timestamp)
, end_timestamp(_end_timestamp)
, call_stack(std::move(_call_stack))
, args_str(std::move(_args_str))
, category(std::move(_category))
{}
// Identification fields
uint64_t thread_id;
int32_t kind;
int32_t operation;
// Correlation fields
uint64_t correlation_id_internal;
uint64_t correlation_id_ancestor;
// Timing fields
uint64_t start_timestamp;
uint64_t end_timestamp;
// Additional fields
std::string call_stack;
std::string args_str;
std::string category;
};
struct in_time_sample : storage_parsed_type_base
{
std::string track_name;
size_t timestamp_ns;
std::string event_metadata;
size_t stack_id;
size_t parent_stack_id;
size_t correlation_id;
std::string call_stack;
std::string line_info;
};
struct pmc_event_with_sample : in_time_sample
{
size_t agent_handle;
std::string pmc_info_name;
size_t value;
};
enum class entry_type : uint32_t
{
in_time_sample = 0x0000,
pmc_event_with_sample = 0x0001,
region = 0x0002,
kernel_dispatch = 0x0003,
memory_copy = 0x0004,
#if(ROCPROFSYS_USE_ROCM && ROCPROFILER_VERSION >= 600)
memory_alloc = 0x0005,
#endif
fragmented_space = 0xFFFF
};
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,230 @@
// 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 "storage_parser.hpp"
#include "debug.hpp"
#include "trace_cache/sample_type.hpp"
#include <cstdio>
#include <fstream>
#include <sstream>
#include <string>
namespace rocprofsys
{
namespace trace_cache
{
storage_parser::storage_parser(pid_t _pid)
: m_pid(_pid)
{}
void
storage_parser::register_type_callback(
const entry_type& type,
const std::function<void(const storage_parsed_type_base&)>& callback)
{
m_callbacks[type].push_back(callback);
}
void
storage_parser::consume_storage()
{
ROCPROFSYS_DEBUG("Consuming buffered storage with filename: %s", filename.c_str());
if(m_pid != getpid())
{
ROCPROFSYS_DEBUG(
"Storage parser is not created in same process as shutting down..");
return;
}
std::ifstream ifs(filename, std::ios::binary);
if(!ifs)
{
std::stringstream ss;
ss << "Error opening file for reading: " << filename << "\n";
throw std::runtime_error(ss.str());
}
bool _parsing_needed = !m_callbacks.empty();
struct __attribute__((packed)) sample_header
{
entry_type type;
size_t sample_size;
};
sample_header header;
while(!ifs.eof() && _parsing_needed)
{
ifs.read(reinterpret_cast<char*>(&header), sizeof(header));
if(header.sample_size == 0 || ifs.eof())
{
continue;
}
std::vector<uint8_t> sample;
sample.reserve(header.sample_size);
ifs.read(reinterpret_cast<char*>(sample.data()), header.sample_size);
if(ifs.bad())
{
ROCPROFSYS_WARNING(
1,
"Bad read while consuming buffered storage. Filename: %s. Bytes read: %d",
filename.c_str(), static_cast<int>(ifs.tellg()));
continue;
}
switch(header.type)
{
case entry_type::kernel_dispatch:
{
kernel_dispatch_sample _kernel_dispatch_sample;
parse_data(sample.data(), _kernel_dispatch_sample.start_timestamp,
_kernel_dispatch_sample.end_timestamp,
_kernel_dispatch_sample.thread_id,
_kernel_dispatch_sample.agent_id_handle,
_kernel_dispatch_sample.kernel_id,
_kernel_dispatch_sample.dispatch_id,
_kernel_dispatch_sample.queue_id_handle,
_kernel_dispatch_sample.correlation_id_internal,
_kernel_dispatch_sample.correlation_id_ancestor,
_kernel_dispatch_sample.private_segment_size,
_kernel_dispatch_sample.group_segment_size,
_kernel_dispatch_sample.workgroup_size_x,
_kernel_dispatch_sample.workgroup_size_y,
_kernel_dispatch_sample.workgroup_size_z,
_kernel_dispatch_sample.grid_size_x,
_kernel_dispatch_sample.grid_size_y,
_kernel_dispatch_sample.grid_size_z,
_kernel_dispatch_sample.stream_handle);
invoke_callbacks(header.type, _kernel_dispatch_sample);
break;
}
case entry_type::memory_copy:
{
memory_copy_sample _memory_copy_sample;
parse_data(
sample.data(), _memory_copy_sample.start_timestamp,
_memory_copy_sample.end_timestamp, _memory_copy_sample.thread_id,
_memory_copy_sample.dst_agent_id_handle,
_memory_copy_sample.src_agent_id_handle, _memory_copy_sample.kind,
_memory_copy_sample.operation, _memory_copy_sample.bytes,
_memory_copy_sample.correlation_id_internal,
_memory_copy_sample.correlation_id_ancestor,
_memory_copy_sample.dst_address_value,
_memory_copy_sample.src_address_value,
_memory_copy_sample.stream_handle);
invoke_callbacks(header.type, _memory_copy_sample);
break;
}
#if(ROCPROFILER_VERSION >= 600)
case entry_type::memory_alloc:
{
memory_allocate_sample _memory_allocate_sample;
parse_data(sample.data(), _memory_allocate_sample.start_timestamp,
_memory_allocate_sample.end_timestamp,
_memory_allocate_sample.thread_id,
_memory_allocate_sample.agent_id_handle,
_memory_allocate_sample.kind,
_memory_allocate_sample.operation,
_memory_allocate_sample.allocation_size,
_memory_allocate_sample.correlation_id_internal,
_memory_allocate_sample.correlation_id_ancestor,
_memory_allocate_sample.address_value,
_memory_allocate_sample.stream_handle);
invoke_callbacks(header.type, _memory_allocate_sample);
break;
}
#endif
case entry_type::region:
{
region_sample _region_sample;
parse_data(sample.data(), _region_sample.thread_id, _region_sample.kind,
_region_sample.operation,
_region_sample.correlation_id_internal,
_region_sample.correlation_id_ancestor,
_region_sample.start_timestamp, _region_sample.end_timestamp,
_region_sample.call_stack, _region_sample.args_str,
_region_sample.category);
invoke_callbacks(header.type, _region_sample);
break;
}
case entry_type::in_time_sample:
{
in_time_sample _in_time_sample;
parse_data(sample.data(), _in_time_sample.track_name,
_in_time_sample.timestamp_ns, _in_time_sample.event_metadata,
_in_time_sample.stack_id, _in_time_sample.parent_stack_id,
_in_time_sample.correlation_id, _in_time_sample.call_stack,
_in_time_sample.line_info);
invoke_callbacks(header.type, _in_time_sample);
break;
}
case entry_type::pmc_event_with_sample:
{
pmc_event_with_sample _pmc_event_with_sample;
parse_data(
sample.data(), _pmc_event_with_sample.track_name,
_pmc_event_with_sample.timestamp_ns,
_pmc_event_with_sample.event_metadata,
_pmc_event_with_sample.stack_id,
_pmc_event_with_sample.parent_stack_id,
_pmc_event_with_sample.correlation_id,
_pmc_event_with_sample.call_stack, _pmc_event_with_sample.line_info,
_pmc_event_with_sample.agent_handle,
_pmc_event_with_sample.pmc_info_name, _pmc_event_with_sample.value);
invoke_callbacks(header.type, _pmc_event_with_sample);
break;
}
default: break;
}
}
ifs.close();
ROCPROFSYS_DEBUG("File parsing finished. Removing %s from file system",
filename.c_str());
std::remove(filename.c_str());
}
void
storage_parser::invoke_callbacks(entry_type type, const storage_parsed_type_base& parsed)
{
auto _callback_list = m_callbacks.find(type);
if(_callback_list == m_callbacks.end())
{
ROCPROFSYS_VERBOSE(1, "Callback not found for cache postprocessing");
return;
}
for(auto& cb : _callback_list->second)
{
cb(parsed);
}
}
} // namespace trace_cache
} // namespace rocprofsys
@@ -0,0 +1,83 @@
// 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 "buffer_storage.hpp"
#include "sample_type.hpp"
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <map>
#include <rocprofiler-systems/categories.h>
#include <stdint.h>
#include <string>
#include <type_traits>
#include <vector>
namespace rocprofsys
{
namespace trace_cache
{
using postprocessing_callback = std::function<void(const storage_parsed_type_base&)>;
class cache_manager;
class storage_parser
{
public:
void register_type_callback(const entry_type& type,
const postprocessing_callback& callback);
void consume_storage();
private:
friend class cache_manager;
storage_parser(pid_t _pid);
template <typename T>
static void process_arg(const uint8_t*& data_pos, T& arg)
{
if constexpr(std::is_same_v<T, std::string>)
{
arg = std::string((const char*) data_pos);
data_pos += arg.size() + 1;
}
else
{
arg = *reinterpret_cast<const T*>(data_pos);
data_pos += sizeof(T);
}
}
template <typename... Args>
static void parse_data(const uint8_t* data_pos, Args&... args)
{
(process_arg(data_pos, args), ...);
}
private:
pid_t m_pid;
void invoke_callbacks(entry_type type, const storage_parsed_type_base& parsed);
std::map<entry_type, std::vector<postprocessing_callback>> m_callbacks;
};
} // namespace trace_cache
} // namespace rocprofsys