Profiler - Migrate from rocprofv1 to rocprofv3

Signed-off-by: Galantsev, Dmitrii <dmitrii.galantsev@amd.com>

Fixed RDC for Rocprofv3

Updates

Signed-off-by: adapryor <Adam.pryor@amd.com>
Change-Id: Ic9162bacf1322b265e6bbcdd9fbb9b1fdef414fd

last updates

Change-Id: I12e168501327c5e4cff8a9273b0512fb0e098fe7

comment

Change-Id: I61da61e66dcc017ec46f98ff4c90fb064c9679e8


[ROCm/rdc commit: 7c91a07a43]
This commit is contained in:
Galantsev, Dmitrii
2024-12-13 16:50:32 -06:00
committato da Pryor, Adam
parent a3bc9b7a72
commit 755ae0ee5d
11 ha cambiato i file con 542 aggiunte e 261 eliminazioni
+6
Vedi File
@@ -72,6 +72,12 @@ jobs:
rocm-smi-lib \
rocm-validation-suite \
rocprofiler-dev \
rocprofiler-plugins \
rocprofiler-register \
rocprofiler-sdk \
hip-dev \
hip-runtime-amd \
hipcc \
build-essential \
ccache \
cmake \
@@ -22,7 +22,7 @@ THE SOFTWARE.
#ifndef RDC_MODULES_RDC_ROCP_RDCROCPBASE_H_
#define RDC_MODULES_RDC_ROCP_RDCROCPBASE_H_
#include <rocprofiler/rocprofiler.h>
#include <rocprofiler-sdk/agent.h>
#include <cstdint>
#include <map>
@@ -32,16 +32,11 @@ THE SOFTWARE.
#include "rdc/rdc.h"
#include "rdc_lib/RdcTelemetryLibInterface.h"
#include "rdc_modules/rdc_rocp/RdcRocpCounterSampler.h"
namespace amd {
namespace rdc {
typedef struct {
hsa_agent_t* agents;
unsigned count;
unsigned capacity;
} hsa_agent_arr_t;
/// Common interface for RocP tests and samples
class RdcRocpBase {
public:
@@ -68,18 +63,16 @@ class RdcRocpBase {
protected:
private:
typedef std::pair<uint32_t, rdc_field_t> rdc_field_pair_t;
static const size_t buffer_length_k = 5;
/**
* @brief Tweak this to change for how long each metric is collected
*/
static const uint32_t collection_duration_us_k = 10000;
double read_feature(rocprofiler_t* context, uint32_t gpu_index);
double read_feature(rocprofiler_record_counter_t* record, uint32_t gpu_index);
double run_profiler(uint32_t gpu_index, rdc_field_t field);
hsa_agent_arr_t agent_arr = {};
std::vector<hsa_queue_t*> queues;
std::map<uint32_t, rocprofiler_feature_t> gpuid_to_feature;
std::vector<rocprofiler_agent_v0_t> agents = {};
std::vector<std::shared_ptr<CounterSampler>> samplers = {};
std::map<rdc_field_t, const char*> field_to_metric = {};
// these fields must be divided by time passed
@@ -89,9 +82,9 @@ class RdcRocpBase {
};
/**
* @brief Convert from rocmtools status into RDC status
* @brief Convert from profiler status into RDC status
*/
rdc_status_t Rocp2RdcError(hsa_status_t status);
rdc_status_t Rocp2RdcError(rocprofiler_status_t status);
};
} // namespace rdc
@@ -0,0 +1,92 @@
// 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.
#ifndef RDC_MODULES_RDC_ROCP_RDCROCPCOUNTERSAMPLER_H_
#define RDC_MODULES_RDC_ROCP_RDCROCPCOUNTERSAMPLER_H_
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/registration.h>
#include <rocprofiler-sdk/rocprofiler.h>
#include <map>
#include <memory>
#include <unordered_map>
#include <vector>
namespace amd {
namespace rdc {
class CounterSampler {
public:
// Setup system profiling for an agent
explicit CounterSampler(rocprofiler_agent_id_t agent);
~CounterSampler();
// Decode the counter name of a record
const std::string& decode_record_name(const rocprofiler_record_counter_t& rec) const;
// Get the dimensions of a record (what CU/SE/etc the counter is for). High cost operation
// should be cached if possible.
std::unordered_map<std::string, size_t> get_record_dimensions(
const rocprofiler_record_counter_t& rec);
// Sample the counter values for a set of counters, returns the records in the out parameter.
void sample_counter_values(const std::vector<std::string>& counters,
std::vector<rocprofiler_record_counter_t>& out, uint64_t duration);
rocprofiler_agent_id_t get_agent() const { return agent_; }
// Get the supported counters for an agent
static std::unordered_map<std::string, rocprofiler_counter_id_t> get_supported_counters(
rocprofiler_agent_id_t agent);
// Get the available agents on the system
static std::vector<rocprofiler_agent_v0_t> get_available_agents();
static std::vector<std::shared_ptr<CounterSampler>>& get_samplers();
private:
rocprofiler_agent_id_t agent_ = {};
rocprofiler_context_id_t ctx_ = {};
rocprofiler_buffer_id_t buf_ = {};
rocprofiler_profile_config_id_t profile_ = {.handle = 0};
std::map<std::vector<std::string>, rocprofiler_profile_config_id_t> cached_profiles_;
std::map<uint64_t, uint64_t> profile_sizes_;
// Internal function used to set the profile for the agent when start_context is called
void set_profile(rocprofiler_context_id_t ctx, rocprofiler_agent_set_profile_callback_t cb) const;
// Get the size of a counter in number of records
size_t get_counter_size(rocprofiler_counter_id_t counter);
// Get the dimensions of a counter
std::vector<rocprofiler_record_dimension_info_t> get_counter_dimensions(
rocprofiler_counter_id_t counter);
static std::vector<std::shared_ptr<CounterSampler>> samplers_;
};
} // namespace rdc
} // namespace amd
#endif // RDC_MODULES_RDC_ROCP_RDCROCPCOUNTERSAMPLER_H_
+2 -1
Vedi File
@@ -12,7 +12,8 @@ default_field_ids = [
rdc_field_t.RDC_FI_POWER_USAGE,
rdc_field_t.RDC_FI_GPU_CLOCK,
rdc_field_t.RDC_FI_GPU_UTIL,
rdc_field_t.RDC_FI_GPU_TEMP
rdc_field_t.RDC_FI_GPU_TEMP,
rdc_field_t.RDC_FI_GPU_MEMORY_USAGE
]
default_unit_coverter = {
@@ -8,7 +8,8 @@ default_field_ids = [
rdc_field_t.RDC_FI_POWER_USAGE,
rdc_field_t.RDC_FI_GPU_CLOCK,
rdc_field_t.RDC_FI_GPU_UTIL,
rdc_field_t.RDC_FI_GPU_TEMP
rdc_field_t.RDC_FI_GPU_TEMP,
rdc_field_t.RDC_FI_GPU_MEMORY_USAGE,
]
@@ -13,6 +13,7 @@ default_field_ids = [
rdc_field_t.RDC_FI_GPU_TEMP,
rdc_field_t.RDC_FI_PROF_ACTIVE_CYCLES,
rdc_field_t.RDC_FI_PROF_ACTIVE_WAVES,
rdc_field_t.RDC_FI_PROF_OCCUPANCY_PERCENT,
]
class PrometheusReader(RdcReader):
@@ -168,7 +168,7 @@ std::string RdcRocpLib::get_rocm_path() {
std::string line;
while (getline(file, line)) {
size_t index_end = line.find("librocprofiler64.so");
size_t index_end = line.find("librocprofiler-register.so");
size_t index_start = index_end;
if (index_end == std::string::npos) {
// no library on this line
@@ -189,28 +189,27 @@ std::string RdcRocpLib::get_rocm_path() {
}
rdc_status_t RdcRocpLib::set_rocprofiler_path() {
// rocprofiler requires ROCP_METRICS to be set
std::string rocprofiler_metrics_path =
get_rocm_path() + "/libexec/rocprofiler/counters/derived_counters.xml";
// rocprofiler requires ROCPROFILER_METRICS_PATH to be set
std::string rocprofiler_metrics_path = get_rocm_path() + "/share/rocprofiler-sdk/";
// set rocm prefix
int result = setenv("ROCP_METRICS", rocprofiler_metrics_path.c_str(), 0);
int result = setenv("ROCPROFILER_METRICS_PATH", rocprofiler_metrics_path.c_str(), 0);
if (result != 0) {
RDC_LOG(RDC_ERROR, "setenv ROCP_METRICS failed! " << result);
RDC_LOG(RDC_ERROR, "setenv ROCPROFILER_METRICS_PATH failed! " << result);
return RDC_ST_PERM_ERROR;
}
// check that env exists
const char* rocprofiler_metrics_env = getenv("ROCP_METRICS");
const char* rocprofiler_metrics_env = getenv("ROCPROFILER_METRICS_PATH");
if (rocprofiler_metrics_env == nullptr) {
RDC_LOG(RDC_ERROR, "ROCP_METRICS is not set!");
RDC_LOG(RDC_ERROR, "ROCPROFILER_METRICS_PATH is not set!");
return RDC_ST_NO_DATA;
}
// check that file can be accessed
std::ifstream test_file(rocprofiler_metrics_env);
if (!test_file.good()) {
RDC_LOG(RDC_ERROR, "failed to open ROCP_METRICS: " << rocprofiler_metrics_env);
RDC_LOG(RDC_ERROR, "failed to open ROCPROFILER_METRICS_PATH: " << rocprofiler_metrics_env);
return RDC_ST_FILE_ERROR;
}
@@ -9,30 +9,31 @@ set(RDC_ROCP_LIB_COMPONENT "lib${RDC_ROCP_LIB}")
set(RDC_ROCP_LIB_SRC_LIST
"${BOOTSTRAP_LIB_SRC_DIR}/RdcLogger.cc"
"${SRC_DIR}/RdcTelemetryLib.cc"
"${SRC_DIR}/RdcRocpCounterSampler.cc"
"${SRC_DIR}/RdcRocpBase.cc")
set(RDC_ROCP_LIB_INC_LIST
"${PROJECT_SOURCE_DIR}/include/rdc/rdc.h"
"${RDC_LIB_INC_DIR}/RdcDiagnosticLibInterface.h"
"${RDC_LIB_INC_DIR}/rdc_common.h"
"${RDC_LIB_INC_DIR}/RdcLogger.h"
"${INC_DIR}/RdcRocpBase.h")
"${INC_DIR}/RdcRocpBase.h"
"${INC_DIR}/RdcRocpCounterSampler.h")
if(BUILD_PROFILER)
message("Build librdc_rocp.so is enabled, make sure ROCmTools is installed.")
message("RDC_ROCP_LIB_INC_LIST=${RDC_ROCP_LIB_INC_LIST}")
set(ROCPROFILER_LIB rocprofiler::rocprofiler)
# below provides rocprofiler::rocprofiler package
include(Findrocprofiler)
find_package(rocprofiler-sdk
HINTS ${ROCM_DIR}/lib/cmake
CONFIGURE REQUIRED)
find_package(hsa-runtime64
NAMES hsa-runtime64
HINTS ${ROCM_DIR}/lib/cmake
CONFIGURE REQUIRED)
set(RDC_LIB_MODULES ${RDC_LIB_MODULES} ${RDC_ROCP_LIB} PARENT_SCOPE)
add_library(${RDC_ROCP_LIB} SHARED ${RDC_ROCP_LIB_SRC_LIST} ${RDC_ROCP_LIB_INC_LIST})
target_link_libraries(${RDC_ROCP_LIB} PRIVATE ${RDC_LIB} ${BOOTSTRAP_LIB} hsa-runtime64::hsa-runtime64 rocprofiler::rocprofiler pthread dl)
target_link_libraries(${RDC_ROCP_LIB} PRIVATE ${RDC_LIB} ${BOOTSTRAP_LIB} hsa-runtime64::hsa-runtime64 rocprofiler-sdk::rocprofiler-sdk pthread dl)
target_include_directories(${RDC_ROCP_LIB} PRIVATE
"${PROJECT_SOURCE_DIR}"
"${PROJECT_SOURCE_DIR}/include"
@@ -22,7 +22,9 @@ THE SOFTWARE.
#include "rdc_modules/rdc_rocp/RdcRocpBase.h"
#include <rocprofiler/rocprofiler.h>
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/registration.h>
#include <rocprofiler-sdk/rocprofiler.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -33,199 +35,63 @@ THE SOFTWARE.
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <numeric>
#include <utility>
#include <stdexcept>
#include <vector>
// #include "hsa.h"
#include "rdc/rdc.h"
#include "rdc_lib/RdcLogger.h"
#include "rdc_lib/RdcTelemetryLibInterface.h"
#include "rdc_modules/rdc_rocp/RdcRocpCounterSampler.h"
namespace amd {
namespace rdc {
static hsa_status_t get_agent_handle_cb(hsa_agent_t agent, void* agent_arr) {
hsa_device_type_t type;
assert(agent_arr != nullptr);
hsa_agent_arr_t* agent_arr_ = (hsa_agent_arr_t*)agent_arr;
hsa_status_t status = hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &type);
if (status != HSA_STATUS_SUCCESS) {
return status;
}
if (type == HSA_DEVICE_TYPE_GPU) {
if (agent_arr_->count >= agent_arr_->capacity) {
agent_arr_->capacity *= 2;
agent_arr_->agents =
(hsa_agent_t*)realloc(agent_arr_->agents, agent_arr_->capacity * sizeof(hsa_agent_t));
// realloc might set agents to nullptr upon failure
assert(agent_arr_->agents != nullptr);
}
agent_arr_->agents[agent_arr_->count] = agent;
++agent_arr_->count;
}
return HSA_STATUS_SUCCESS;
}
double RdcRocpBase::read_feature(rocprofiler_t* context, uint32_t gpu_index) {
hsa_status_t status = rocprofiler_read(context, 0);
assert(status == HSA_STATUS_SUCCESS);
status = rocprofiler_get_data(context, 0);
assert(status == HSA_STATUS_SUCCESS);
status = rocprofiler_get_metrics(context);
assert(status == HSA_STATUS_SUCCESS);
switch (gpuid_to_feature[gpu_index].data.kind) {
case ROCPROFILER_DATA_KIND_DOUBLE:
return gpuid_to_feature[gpu_index].data.result_double;
break;
case ROCPROFILER_DATA_KIND_INT32:
return static_cast<double>(gpuid_to_feature[gpu_index].data.result_int32);
break;
case ROCPROFILER_DATA_KIND_INT64:
return static_cast<double>(gpuid_to_feature[gpu_index].data.result_int64);
break;
case ROCPROFILER_DATA_KIND_FLOAT:
return static_cast<double>(gpuid_to_feature[gpu_index].data.result_float);
break;
default:
RDC_LOG(RDC_ERROR,
"ERROR: Unexpected feature kind: " << gpuid_to_feature[gpu_index].data.kind);
}
return 0.0;
}
static int get_agents(hsa_agent_arr_t* agent_arr) {
int errcode = 0;
hsa_status_t status = HSA_STATUS_SUCCESS;
agent_arr->capacity = 1;
agent_arr->count = 0;
agent_arr->agents = (hsa_agent_t*)calloc(agent_arr->capacity, sizeof(hsa_agent_t));
assert(agent_arr->agents);
status = hsa_iterate_agents(get_agent_handle_cb, agent_arr);
if (status != HSA_STATUS_SUCCESS) {
errcode = -1;
agent_arr->capacity = 0;
agent_arr->count = 0;
free(agent_arr->agents);
}
return errcode;
}
bool createHsaQueue(hsa_queue_t** queue, hsa_agent_t gpu_agent) {
// create a single-producer queue
hsa_status_t status = hsa_queue_create(gpu_agent, 64, HSA_QUEUE_TYPE_SINGLE, NULL, NULL,
UINT32_MAX, UINT32_MAX, queue);
if (status != HSA_STATUS_SUCCESS) {
RDC_LOG(RDC_ERROR, "Queue creation failed");
}
status = hsa_amd_queue_set_priority(*queue, HSA_AMD_QUEUE_PRIORITY_HIGH);
if (status != HSA_STATUS_SUCCESS) {
RDC_LOG(RDC_ERROR, "HSA Queue Priority Set Failed");
}
return (status == HSA_STATUS_SUCCESS);
}
double RdcRocpBase::run_profiler(uint32_t gpu_index, rdc_field_t field) {
thread_local std::vector<rocprofiler_record_counter_t> records;
// initialize hsa. hsa_init() will also load the profiler libs under the hood
hsa_status_t status = HSA_STATUS_SUCCESS;
gpuid_to_feature[gpu_index].kind = (rocprofiler_feature_kind_t)ROCPROFILER_FEATURE_KIND_METRIC;
gpuid_to_feature[gpu_index].name = field_to_metric[field];
// rocprofiler_t* contexts[agent_arr.count] = {0};
std::vector<rocprofiler_t*> contexts;
contexts.reserve(agent_arr.count);
rocprofiler_properties_t properties = {
queues[gpu_index],
64,
NULL,
NULL,
};
int mode = (ROCPROFILER_MODE_STANDALONE | ROCPROFILER_MODE_SINGLEGROUP);
status = rocprofiler_open(agent_arr.agents[gpu_index], &gpuid_to_feature[gpu_index], 1,
&contexts[gpu_index], mode, &properties);
const char* error_string = nullptr;
rocprofiler_error_string(&error_string);
if (error_string != nullptr) {
if (error_string[0] != '\0') {
RDC_LOG(RDC_ERROR, error_string);
}
auto counter_sampler = CounterSampler::get_samplers()[gpu_index];
if (!counter_sampler) {
RDC_LOG(RDC_ERROR, "Error: Counter sampler not found for GPU index " << gpu_index << std::endl);
throw std::runtime_error("Counter sampler not found");
}
assert(status == HSA_STATUS_SUCCESS);
status = rocprofiler_start(contexts[gpu_index], 0);
assert(status == HSA_STATUS_SUCCESS);
auto field_it = field_to_metric.find(field);
if (field_it == field_to_metric.end()) {
RDC_LOG(RDC_ERROR,
"Error: Field " << field << " not found in field_to_metric map." << std::endl);
throw std::out_of_range("Field not found in field_to_metric map");
}
const std::string& metric_id = field_it->second;
// this is the duration for which the counter increments from zero.
// TODO: Return error if sampling interval is lower than this value
usleep(collection_duration_us_k);
try {
counter_sampler->sample_counter_values({metric_id}, records, collection_duration_us_k);
} catch (const std::exception& e) {
RDC_LOG(RDC_ERROR, "Error while sampling counter values: " << e.what() << std::endl);
throw;
}
status = rocprofiler_stop(contexts[gpu_index], 0);
assert(status == HSA_STATUS_SUCCESS);
double value = read_feature(contexts[gpu_index], gpu_index);
usleep(100);
status = rocprofiler_close(contexts[gpu_index]);
assert(status == HSA_STATUS_SUCCESS);
// Aggregate counter values. Rocprof v1/v2 summed values across dimensions.
double value = 0.0;
for (auto& record : records) {
value += record.counter_value; // Summing up values from all dimensions.
}
return value;
}
const char* RdcRocpBase::get_field_id_from_name(rdc_field_t field) {
return field_to_metric.at(field);
}
// TODO - map RDC gpu_index to node_id
// use rocprofiler to check which metrics are supported
void check_metrics_supported(uint32_t node_id, std::vector<std::string>& metrics_all,
std::vector<std::string>& metrics_good) {
typedef struct {
std::vector<std::string>* metrics_all_;
std::vector<std::string>* metrics_good_;
uint32_t driver_node_id;
} payload_t;
// callback for rocprofiler to check which metrics are supported
auto info_callback = [](const rocprofiler_info_data_t info, void* data) {
payload_t* payload = reinterpret_cast<payload_t*>(data);
if (info.agent_index == payload->driver_node_id) {
auto it =
std::find(payload->metrics_all_->begin(), payload->metrics_all_->end(), info.metric.name);
if (it != payload->metrics_all_->end()) {
payload->metrics_good_->push_back(info.metric.name);
RDC_LOG(RDC_DEBUG, " gpu-agent" << info.agent_index << " : " << info.metric.name << " : "
<< info.metric.description);
if (info.metric.expr != NULL) // if it's a derived metric, print it's formula
RDC_LOG(RDC_DEBUG, " " << info.metric.name << " = " << info.metric.expr);
}
}
return HSA_STATUS_SUCCESS;
};
payload_t payload = {&metrics_all, &metrics_good, node_id};
hsa_status_t status =
rocprofiler_iterate_info(NULL, ROCPROFILER_INFO_KIND_METRIC, info_callback, &payload);
if (status != HSA_STATUS_SUCCESS) {
const char* errstr = nullptr;
hsa_status_string(status, &errstr);
RDC_LOG(RDC_ERROR, "hsa error: " << std::to_string(status) << " " << errstr);
} else {
for (auto& iter : *(payload.metrics_good_)) {
RDC_LOG(RDC_DEBUG, iter << " : exists");
}
auto it = field_to_metric.find(field);
if (it == field_to_metric.end()) {
RDC_LOG(RDC_ERROR,
"Error: Field ID " << field << " not found in field_to_metric map." << std::endl);
throw std::out_of_range("Field ID not found in field_to_metric map");
}
return field_to_metric.at(field);
}
const std::vector<rdc_field_t> RdcRocpBase::get_field_ids() {
@@ -237,20 +103,14 @@ const std::vector<rdc_field_t> RdcRocpBase::get_field_ids() {
}
RdcRocpBase::RdcRocpBase() {
hsa_status_t status = hsa_init();
if (status != HSA_STATUS_SUCCESS) {
const char* errstr = nullptr;
hsa_status_string(status, &errstr);
throw std::runtime_error("hsa error code: " + std::to_string(status) + " " + errstr);
}
// all fields
static const std::map<rdc_field_t, const char*> temp_field_map_k = {
{RDC_FI_PROF_OCCUPANCY_PERCENT, "OccupancyPercent"},
{RDC_FI_PROF_ACTIVE_CYCLES, "GRBM_GUI_ACTIVE"},
{RDC_FI_PROF_ACTIVE_WAVES, "SQ_WAVES"},
{RDC_FI_PROF_ELAPSED_CYCLES, "GRBM_COUNT"},
{RDC_FI_PROF_TENSOR_ACTIVE_PERCENT, "MfmaUtil"}, // same as TENSOR_ACTIVE but available for more GPUs
{RDC_FI_PROF_TENSOR_ACTIVE_PERCENT,
"MfmaUtil"}, // same as TENSOR_ACTIVE but available for more GPUs
{RDC_FI_PROF_GPU_UTIL_PERCENT, "GPU_UTIL"},
// metrics below are divided by time passed
{RDC_FI_PROF_EVAL_MEM_R_BW, "FETCH_SIZE"},
@@ -261,33 +121,41 @@ RdcRocpBase::RdcRocpBase() {
{RDC_FI_PROF_VALU_PIPE_ISSUE_UTIL, "ValuPipeIssueUtil"},
};
hsa_status_t status = hsa_init();
if (status != HSA_STATUS_SUCCESS) {
const char* errstr = nullptr;
hsa_status_string(status, &errstr);
throw std::runtime_error("hsa error code: " + std::to_string(status) + " " + errstr);
}
// check rocprofiler
if (int rocp_status = 0;
rocprofiler_is_initialized(&rocp_status) == ROCPROFILER_STATUS_SUCCESS && rocp_status != 1) {
throw std::runtime_error("Rocprofiler is not initialized. status: " +
std::to_string(rocp_status));
}
std::vector<std::string> all_fields;
std::vector<std::string> checked_fields;
// populate list of agents
agents = CounterSampler::get_available_agents();
RDC_LOG(RDC_DEBUG, "Agent count: " << agents.size());
samplers = CounterSampler::get_samplers();
// populate fields
for (auto& [k, v] : temp_field_map_k) {
all_fields.push_back(v);
}
// populate list of agents
int errcode = get_agents(&agent_arr);
if (errcode != 0) {
return;
}
RDC_LOG(RDC_DEBUG, "Agent count: " << agent_arr.count);
uint32_t driver_node_id = 0;
for (uint32_t gpu_index = 0; gpu_index < agent_arr.count; gpu_index++) {
status = hsa_agent_get_info(agent_arr.agents[gpu_index],
static_cast<hsa_agent_info_t>(HSA_AMD_AGENT_INFO_DRIVER_NODE_ID),
&driver_node_id);
if (status != HSA_STATUS_SUCCESS) {
const char* errstr = nullptr;
hsa_status_string(status, &errstr);
RDC_LOG(RDC_ERROR, "hsa error: " << std::to_string(status) << " " << errstr);
} else {
RDC_LOG(RDC_DEBUG, "gpu_index[" << gpu_index << "] = node_id[" << driver_node_id << "]");
// find intersection of supported and requested fields
for (uint32_t gpu_index = 0; gpu_index < agents.size(); gpu_index++) {
auto& cs = *samplers[gpu_index];
RDC_LOG(RDC_DEBUG,
"gpu_index[" << gpu_index << "] = node_id[" << agents[gpu_index].node_id << "]");
for (auto& [str, id] : cs.get_supported_counters(cs.get_agent())) {
checked_fields.emplace_back(str);
}
check_metrics_supported(driver_node_id, all_fields, checked_fields);
for (auto& [k, v] : temp_field_map_k) {
auto found = std::find(checked_fields.begin(), checked_fields.end(), v);
@@ -298,22 +166,6 @@ RdcRocpBase::RdcRocpBase() {
}
RDC_LOG(RDC_DEBUG, "Rocprofiler supports " << field_to_metric.size() << " fields");
for (uint32_t gpu_index = 0; gpu_index < agent_arr.count; gpu_index++) {
for (const auto& [k, v] : field_to_metric) {
rocprofiler_feature_t temp_feature;
temp_feature.kind = (rocprofiler_feature_kind_t)ROCPROFILER_FEATURE_KIND_METRIC;
temp_feature.name = v;
gpuid_to_feature.insert({gpu_index, temp_feature});
}
}
for (uint32_t gpu_index = 0; gpu_index < agent_arr.count; gpu_index++) {
queues.push_back(nullptr);
if (!createHsaQueue(&queues[gpu_index], agent_arr.agents[gpu_index])) {
RDC_LOG(RDC_ERROR, "can't create queues[" << gpu_index << "]\n");
}
}
}
RdcRocpBase::~RdcRocpBase() {
@@ -332,10 +184,6 @@ rdc_status_t RdcRocpBase::rocp_lookup(rdc_gpu_field_t gpu_field, double* value)
return RDC_ST_BAD_PARAMETER;
}
hsa_status_t status = HSA_STATUS_SUCCESS;
if (status != HSA_STATUS_SUCCESS) {
return Rocp2RdcError(status);
}
const auto start_time = std::chrono::high_resolution_clock::now();
*value = run_profiler(gpu_index, field);
const auto stop_time = std::chrono::high_resolution_clock::now();
@@ -343,8 +191,6 @@ rdc_status_t RdcRocpBase::rocp_lookup(rdc_gpu_field_t gpu_field, double* value)
if (eval_fields.find(field) != eval_fields.end()) {
const auto elapsed =
std::chrono::duration_cast<std::chrono::milliseconds>(stop_time - start_time).count();
// RDC_LOG(RDC_DEBUG, "INDEX: " << gpu_index << " before[" << *value << "] after["
// << (*value / elapsed) << "]");
*value = *value / elapsed;
}
// GPU_UTIL metric is available on more GPUs than ENGINE_ACTIVE.
@@ -352,16 +198,7 @@ rdc_status_t RdcRocpBase::rocp_lookup(rdc_gpu_field_t gpu_field, double* value)
if (field == RDC_FI_PROF_GPU_UTIL_PERCENT) {
*value = *value / 100.0F;
}
return Rocp2RdcError(status);
}
rdc_status_t RdcRocpBase::Rocp2RdcError(hsa_status_t status) {
switch (status) {
case HSA_STATUS_SUCCESS:
return RDC_ST_OK;
default:
return RDC_ST_UNKNOWN_ERROR;
}
return RDC_ST_OK;
}
} // namespace rdc
@@ -0,0 +1,350 @@
// 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 "rdc_modules/rdc_rocp/RdcRocpCounterSampler.h"
#include <rocprofiler-sdk/fwd.h>
#include <rocprofiler-sdk/registration.h>
#include <rocprofiler-sdk/rocprofiler.h>
#include <unistd.h>
#include <atomic>
#include <chrono>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
#include <vector>
#include "rdc_lib/RdcLogger.h"
template <typename Callable>
void RocprofilerCall(Callable&& callable, const std::string& msg, const char* file, int line) {
auto result = callable();
if (result != ROCPROFILER_STATUS_SUCCESS) {
std::string status_msg = rocprofiler_get_status_string(result);
RDC_LOG(RDC_ERROR, "[CALL][" << file << ":" << line << "] " << msg << " failed with error code "
<< result << ": " << status_msg << std::endl);
std::stringstream errmsg{};
errmsg << "[CALL][" << file << ":" << line << "] " << msg << " failure (" << status_msg << ")";
throw std::runtime_error(errmsg.str());
}
}
namespace amd {
namespace rdc {
std::vector<std::shared_ptr<CounterSampler>> CounterSampler::samplers_;
std::vector<std::shared_ptr<CounterSampler>>& CounterSampler::get_samplers() { return samplers_; }
CounterSampler::CounterSampler(rocprofiler_agent_id_t agent) : agent_(agent) {
// Setup context (should only be done once per agent)
auto client_thread = rocprofiler_callback_thread_t{};
RocprofilerCall([&]() { return rocprofiler_create_context(&ctx_); }, "context creation failed",
__FILE__, __LINE__);
RocprofilerCall(
[&]() {
// 4096 is total buffer size
// 2048 is size for callback buffer, not likely used as rdc reads results immediately, not
// from buffer feature will drop in future
return rocprofiler_create_buffer(
ctx_, 4096, 2048, ROCPROFILER_BUFFER_POLICY_LOSSLESS,
[](rocprofiler_context_id_t, rocprofiler_buffer_id_t, rocprofiler_record_header_t**,
size_t, void*, uint64_t) {},
nullptr, &buf_);
},
"buffer creation failed", __FILE__, __LINE__);
RocprofilerCall([&]() { return rocprofiler_create_callback_thread(&client_thread); },
"failure creating callback thread", __FILE__, __LINE__);
RocprofilerCall([&]() { return rocprofiler_assign_callback_thread(buf_, client_thread); },
"failed to assign thread for buffer", __FILE__, __LINE__);
RocprofilerCall(
[&]() {
return rocprofiler_configure_device_counting_service(
ctx_, buf_, agent,
[](rocprofiler_context_id_t context_id, rocprofiler_agent_id_t,
rocprofiler_agent_set_profile_callback_t set_config, void* user_data) {
if (user_data) {
auto* sampler = static_cast<CounterSampler*>(user_data);
sampler->set_profile(context_id, set_config);
}
},
this);
},
"Could not setup buffered service", __FILE__, __LINE__);
}
CounterSampler::~CounterSampler() { rocprofiler_stop_context(ctx_); }
const std::string& CounterSampler::decode_record_name(
const rocprofiler_record_counter_t& rec) const {
static auto roc_counters = [this]() {
auto name_to_id = CounterSampler::get_supported_counters(agent_);
std::map<uint64_t, std::string> id_to_name;
for (const auto& [name, id] : name_to_id) {
id_to_name.emplace(id.handle, name);
}
return id_to_name;
}();
rocprofiler_counter_id_t counter_id = {.handle = 0};
rocprofiler_query_record_counter_id(rec.id, &counter_id);
auto it = roc_counters.find(counter_id.handle);
if (it == roc_counters.end()) {
RDC_LOG(RDC_ERROR, "Error: Counter handle " << counter_id.handle
<< " not found in roc_counters." << std::endl);
throw std::runtime_error("Counter handle not found in roc_counters");
}
return it->second;
}
std::unordered_map<std::string, size_t> CounterSampler::get_record_dimensions(
const rocprofiler_record_counter_t& rec) {
std::unordered_map<std::string, size_t> out;
rocprofiler_counter_id_t counter_id = {.handle = 0};
rocprofiler_query_record_counter_id(rec.id, &counter_id);
auto dims = get_counter_dimensions(counter_id);
for (auto& dim : dims) {
size_t pos = 0;
rocprofiler_query_record_dimension_position(rec.id, dim.id, &pos);
out.emplace(dim.name, pos);
}
return out;
}
void CounterSampler::sample_counter_values(const std::vector<std::string>& counters,
std::vector<rocprofiler_record_counter_t>& out,
uint64_t duration) {
auto profile_cached = cached_profiles_.find(counters);
if (profile_cached == cached_profiles_.end()) {
size_t expected_size = 0;
rocprofiler_profile_config_id_t profile = {};
std::vector<rocprofiler_counter_id_t> gpu_counters;
auto roc_counters = get_supported_counters(agent_);
for (const auto& counter : counters) {
auto it = roc_counters.find(counter);
if (it == roc_counters.end()) {
RDC_LOG(RDC_ERROR, "Counter " << counter << " not found\n");
continue;
}
gpu_counters.push_back(it->second);
expected_size += get_counter_size(it->second);
}
RocprofilerCall(
[&]() {
return rocprofiler_create_profile_config(agent_, gpu_counters.data(), gpu_counters.size(),
&profile);
},
"Could not create profile", __FILE__, __LINE__);
cached_profiles_.emplace(counters, profile);
profile_sizes_.emplace(profile.handle, expected_size);
profile_cached = cached_profiles_.find(counters);
}
if (profile_sizes_.find(profile_cached->second.handle) == profile_sizes_.end()) {
RDC_LOG(RDC_ERROR, "Error: Profile handle " << profile_cached->second.handle
<< " not found in profile_sizes_." << std::endl);
throw std::runtime_error("Profile handle not found in profile_sizes_");
}
out.resize(profile_sizes_.at(profile_cached->second.handle));
profile_ = profile_cached->second;
rocprofiler_start_context(ctx_);
size_t out_size = out.size();
// Wait for sampling window to collect metrics
usleep(duration);
rocprofiler_sample_device_counting_service(ctx_, {}, ROCPROFILER_COUNTER_FLAG_NONE, out.data(),
&out_size);
rocprofiler_stop_context(ctx_);
out.resize(out_size);
}
std::vector<rocprofiler_agent_v0_t> CounterSampler::get_available_agents() {
std::vector<rocprofiler_agent_v0_t> agents;
rocprofiler_query_available_agents_cb_t iterate_cb = [](rocprofiler_agent_version_t agents_ver,
const void** agents_arr,
size_t num_agents, void* udata) {
if (agents_ver != ROCPROFILER_AGENT_INFO_VERSION_0)
throw std::runtime_error{"unexpected rocprofiler agent version"};
auto* agents_v = static_cast<std::vector<rocprofiler_agent_v0_t>*>(udata);
for (size_t i = 0; i < num_agents; ++i) {
const auto* rocp_agent = static_cast<const rocprofiler_agent_v0_t*>(agents_arr[i]);
if (rocp_agent->type == ROCPROFILER_AGENT_TYPE_GPU) agents_v->emplace_back(*rocp_agent);
}
return ROCPROFILER_STATUS_SUCCESS;
};
RocprofilerCall(
[&]() {
return rocprofiler_query_available_agents(
ROCPROFILER_AGENT_INFO_VERSION_0, iterate_cb, sizeof(rocprofiler_agent_t),
const_cast<void*>(static_cast<const void*>(&agents)));
},
"query available agents", __FILE__, __LINE__);
return agents;
}
void CounterSampler::set_profile(rocprofiler_context_id_t ctx,
rocprofiler_agent_set_profile_callback_t cb) const {
if (profile_.handle != 0) {
cb(ctx, profile_);
}
}
size_t CounterSampler::get_counter_size(rocprofiler_counter_id_t counter) {
size_t size = 1;
rocprofiler_iterate_counter_dimensions(
counter,
[](rocprofiler_counter_id_t, const rocprofiler_record_dimension_info_t* dim_info,
size_t num_dims, void* user_data) {
size_t* s = static_cast<size_t*>(user_data);
for (size_t i = 0; i < num_dims; i++) {
*s *= dim_info[i].instance_size;
}
return ROCPROFILER_STATUS_SUCCESS;
},
static_cast<void*>(&size));
return size;
}
std::unordered_map<std::string, rocprofiler_counter_id_t> CounterSampler::get_supported_counters(
rocprofiler_agent_id_t agent) {
std::unordered_map<std::string, rocprofiler_counter_id_t> out;
std::vector<rocprofiler_counter_id_t> gpu_counters;
RocprofilerCall(
[&]() {
return rocprofiler_iterate_agent_supported_counters(
agent,
[](rocprofiler_agent_id_t, rocprofiler_counter_id_t* counters, size_t num_counters,
void* user_data) {
std::vector<rocprofiler_counter_id_t>* vec =
static_cast<std::vector<rocprofiler_counter_id_t>*>(user_data);
for (size_t i = 0; i < num_counters; i++) {
vec->push_back(counters[i]);
}
return ROCPROFILER_STATUS_SUCCESS;
},
static_cast<void*>(&gpu_counters));
},
"Could not fetch supported counters", __FILE__, __LINE__);
for (auto& counter : gpu_counters) {
rocprofiler_counter_info_v0_t version;
RocprofilerCall(
[&]() {
return rocprofiler_query_counter_info(counter, ROCPROFILER_COUNTER_INFO_VERSION_0,
static_cast<void*>(&version));
},
"Could not query info for counter", __FILE__, __LINE__);
out.emplace(version.name, counter);
}
return out;
}
std::vector<rocprofiler_record_dimension_info_t> CounterSampler::get_counter_dimensions(
rocprofiler_counter_id_t counter) {
std::vector<rocprofiler_record_dimension_info_t> dims;
rocprofiler_available_dimensions_cb_t cb = [](rocprofiler_counter_id_t,
const rocprofiler_record_dimension_info_t* dim_info,
size_t num_dims, void* user_data) {
std::vector<rocprofiler_record_dimension_info_t>* vec =
static_cast<std::vector<rocprofiler_record_dimension_info_t>*>(user_data);
for (size_t i = 0; i < num_dims; i++) {
vec->push_back(dim_info[i]);
}
return ROCPROFILER_STATUS_SUCCESS;
};
RocprofilerCall([&]() { return rocprofiler_iterate_counter_dimensions(counter, cb, &dims); },
"Could not iterate counter dimensions", __FILE__, __LINE__);
return dims;
}
int tool_init(rocprofiler_client_finalize_t, void*) {
// Get the agents available on the device
auto agents = CounterSampler::get_available_agents();
if (agents.empty()) {
RDC_LOG(RDC_ERROR, "No agents found\n");
return -1;
}
for (auto agent : agents) {
CounterSampler::get_samplers().push_back(std::make_shared<CounterSampler>(agent.id));
}
// no errors
return 0;
}
void tool_fini(void* user_data) {
auto* output_stream = static_cast<std::ostream*>(user_data);
*output_stream << std::flush;
if (output_stream != &std::cout && output_stream != &std::cerr) delete output_stream;
}
extern "C" rocprofiler_tool_configure_result_t* rocprofiler_configure(uint32_t version,
const char* runtime_version,
uint32_t priority,
rocprofiler_client_id_t* id) {
// set the client name
id->name = "CounterClientSample";
// compute major/minor/patch version info
uint32_t major = version / 10000;
uint32_t minor = (version % 10000) / 100;
uint32_t patch = version % 100;
// generate info string
auto info = std::stringstream{};
info << id->name << " (priority=" << priority << ") is using rocprofiler-sdk v" << major << "."
<< minor << "." << patch << " (" << runtime_version << ")";
std::clog << info.str() << std::endl;
std::ostream* output_stream = nullptr;
std::string filename = "counter_collection.log";
if (auto* outfile = getenv("ROCPROFILER_SAMPLE_OUTPUT_FILE"); outfile) filename = outfile;
if (filename == "stdout")
output_stream = &std::cout;
else if (filename == "stderr")
output_stream = &std::cerr;
else
output_stream = new std::ofstream{filename};
// create configure data
static auto cfg =
rocprofiler_tool_configure_result_t{sizeof(rocprofiler_tool_configure_result_t), &tool_init,
&tool_fini, static_cast<void*>(output_stream)};
// return pointer to configure data
return &cfg;
}
} // namespace rdc
} // namespace amd
@@ -52,7 +52,7 @@ bool is_rocp_disabled() {
[&value_str](const char* val) { return value_str == val; });
}
rdc_status_t rdc_module_init(uint64_t /*flags*/) {
rdc_status_t rdc_module_init(uint64_t /*flags*/) {
if (is_rocp_disabled()) {
// rocprofiler does NOT work in gtest.
// GTest starts up multiple instances of the progam under test,