Adding rocprofilerv2

Change-Id: Ic0cc280ba207d2b8f6ccae1cd4ac3184152fc1ad
This commit is contained in:
Ammar ELWazir
2023-02-03 12:31:39 -06:00
parent e0e5f7336b
commit 8032adb64f
263 changed files with 607729 additions and 307 deletions
+329
View File
@@ -0,0 +1,329 @@
#include "device_profiling.h"
// #include "src/utils/debug.h"
#include <iostream>
#include <sched.h>
#include <atomic>
#include <vector>
#include "src/utils/exception.h"
#include "src/core/hsa/queues/queue.h"
// #include "src/core/counters/rdc/rdc_metrics.h"
#include "src/core/hsa/hsa_common.h"
#include <exception>
#include <typeinfo>
#include <stdexcept>
#define QUEUE_NUM_PACKETS 64
static const size_t CMD_SLOT_SIZE_B = 0x40;
using namespace rocmtools;
typedef std::vector<hsa_ven_amd_aqlprofile_info_data_t> pmc_callback_data_t;
static std::atomic<uint64_t> SESSION_COUNTER{1};
uint64_t GenerateUniqueSessionId() {
return SESSION_COUNTER.fetch_add(1, std::memory_order_release);
}
struct devices_t {
std::vector<hsa_agent_t> cpu_devices;
std::vector<hsa_agent_t> gpu_devices;
std::vector<hsa_agent_t> other_devices;
};
bool createHsaQueue(hsa_queue_t** queue, hsa_agent_t gpu_agent) {
// create a single-producer queue
// TODO: check if API args are correct, especially UINT32_MAX
hsa_status_t status;
status = hsa_queue_create(gpu_agent, QUEUE_NUM_PACKETS, HSA_QUEUE_TYPE_SINGLE, NULL, NULL,
UINT32_MAX, UINT32_MAX, queue);
if (status != HSA_STATUS_SUCCESS) fatal("queue creation failed");
return (status == HSA_STATUS_SUCCESS);
}
uint64_t submitPacket(hsa_queue_t* queue, const void* packet) {
const uint32_t slot_size_b = CMD_SLOT_SIZE_B;
// advance command queue
const uint64_t write_idx = hsa_queue_add_write_index_scacq_screl(queue, 1);
while ((write_idx - hsa_queue_load_read_index_relaxed(queue)) >= queue->size) {
sched_yield(); // TODO: remove
}
const uint32_t slot_idx = (uint32_t)(write_idx % queue->size);
uint32_t* queue_slot =
reinterpret_cast<uint32_t*>((uintptr_t)(queue->base_address) + (slot_idx * slot_size_b));
const uint32_t* slot_data = reinterpret_cast<const uint32_t*>(packet);
// Copy buffered commands into the queue slot.
// Overwrite the AQL invalid header (first dword) last.
// This prevents the slot from being read until it's fully written.
memcpy(&queue_slot[1], &slot_data[1], slot_size_b - sizeof(uint32_t));
std::atomic<uint32_t>* header_atomic_ptr =
reinterpret_cast<std::atomic<uint32_t>*>(&queue_slot[0]);
header_atomic_ptr->store(slot_data[0], std::memory_order_release);
// ringdoor bell
hsa_signal_store_relaxed(queue->doorbell_signal, write_idx);
return write_idx;
}
// Wait signal
hsa_signal_value_t signalWait(const hsa_signal_t& signal, const hsa_signal_value_t& signal_value) {
const hsa_signal_value_t exp_value = signal_value - 1;
hsa_signal_value_t ret_value = signal_value;
while (1) {
// TODO: The 4th argument mentioning timeout is current set to UINT64_MAX.
// Probably a maximum wait time should be set. We don't want application to hang because of
// unlimited wait.
// TODO2 : try 500000 assuming nanosecond granularity -- must be verified.
ret_value = hsa_signal_wait_scacquire(signal, HSA_SIGNAL_CONDITION_LT, signal_value, UINT64_MAX,
HSA_WAIT_STATE_BLOCKED);
if (ret_value == exp_value) break;
if (ret_value != signal_value)
fatal("Error: signalWait: signal_value(%lu), ret_value(%lu)", signal_value, ret_value);
}
return ret_value;
}
bool DeviceProfileSession::generatePackets() {
// char gpu_name[64];
// hsa_agent_get_info(gpu_agent_, HSA_AGENT_INFO_NAME, gpu_name);
// Get the PM4 Packets
// TODO: The below function is wasteful. Doesn't do resource cleanup.
// write a function that is specific to the needs of this class
/*
profiles_ = Packet::initializeAqlPackets(
cpu_agent_, gpu_agent_, gpu_name, profiling_data_,
profiling_data_.size());
if(profiles_->size() > 1)
std::cout<<"Multiple profiles present!\n";
profile_ = (*profiles_)[0].second;
start_packet_ = *(*profiles_)[0].first->start_packet;
stop_packet_ = *(*profiles_)[0].first->stop_packet;
read_packet_ = *(*profiles_)[0].first->read_packet;
counter_map_ = *(*profiles_)[0].first->counter_map; */
std::map<std::pair<uint32_t, uint32_t>, uint64_t> events_max_block_counters;
std::map<std::string, std::set<std::string>> metrics_counters;
metrics::ExtractMetricEvents(profiling_data_, gpu_agent_, metrics_dict_, results_map_,
events_list_, results_list_, events_max_block_counters,
metrics_counters);
profile_ = Packet::InitializeDeviceProfilingAqlPackets(cpu_agent_, gpu_agent_, &events_list_[0],
events_list_.size(), &start_packet_,
&stop_packet_, &read_packet_);
start_packet_.header = HSA_PACKET_TYPE_VENDOR_SPECIFIC << HSA_PACKET_HEADER_TYPE;
start_packet_.completion_signal = {};
read_packet_.header = HSA_PACKET_TYPE_VENDOR_SPECIFIC << HSA_PACKET_HEADER_TYPE;
read_packet_.completion_signal = {};
stop_packet_.header = HSA_PACKET_TYPE_VENDOR_SPECIFIC << HSA_PACKET_HEADER_TYPE;
stop_packet_.completion_signal = {};
return true;
}
bool DeviceProfileSession::createQueue() {
// Ensuring there is only one queue per device
hsa_queue_t* queue = DeviceProfileSession::getQueue(gpu_agent_);
if (queue != nullptr) return true;
if (::createHsaQueue(&queue, gpu_agent_) == false) return false;
std::lock_guard<std::mutex> lock(agent_queue_map_mutex_);
agent_queue_map_.insert(std::make_pair(gpu_agent_.handle, queue));
return true;
}
hsa_queue_t* DeviceProfileSession::getQueue(hsa_agent_t gpu_agent) {
std::lock_guard<std::mutex> lock(agent_queue_map_mutex_);
auto it = agent_queue_map_.find(gpu_agent.handle);
return (it != agent_queue_map_.end()) ? it->second : nullptr;
}
DeviceProfileSession::DeviceProfileSession(std::vector<std::string> profiling_data,
hsa_agent_t cpu_agent, hsa_agent_t gpu_agent,
uint64_t* session_id)
: profiling_data_(profiling_data), cpu_agent_(cpu_agent), gpu_agent_(gpu_agent) {
session_id_ = GenerateUniqueSessionId();
*session_id = session_id_;
// initialize packets struct
start_packet_ = {};
stop_packet_ = {};
read_packet_ = {};
profile_ = NULL;
char gpu_name[64];
if (hsa_agent_get_info(gpu_agent_, HSA_AGENT_INFO_NAME, gpu_name) != HSA_STATUS_SUCCESS)
fatal("Agent name query failed");
Agent::AgentInfo* agentInfo = &(hsa_support::GetAgentInfo(gpu_agent_.handle));
metrics_dict_ = MetricsDict::Create(agentInfo);
for (auto& d : profiling_data_) {
Metric* metric = const_cast<Metric*>(metrics_dict_->Get(d));
if (metric == NULL) std::cout << d << " not found in metrics_dict\n";
metrics_list_.push_back(metric);
}
createQueue();
generatePackets();
// create signals
hsa_status_t status = hsa_signal_create(1, 0, NULL, &start_signal_);
if (status != HSA_STATUS_SUCCESS) fatal("start signal creation failed");
status = hsa_signal_create(1, 0, NULL, &completion_signal_);
if (status != HSA_STATUS_SUCCESS) fatal("completion signal creation failed");
status = hsa_signal_create(1, 0, NULL, &stop_signal_);
if (status != HSA_STATUS_SUCCESS) fatal("stop signal creation failed");
}
DeviceProfileSession::~DeviceProfileSession() {
// TODO:
// delete queue
// delete signals
// free command buffer/output buffer
}
void DeviceProfileSession::StartSession() {
// TODO: check if session was already started. Don't allow start twice
// Set completion signal
start_packet_.completion_signal = start_signal_;
// Place the "Start" packet in the Queue
submitPacket(DeviceProfileSession::getQueue(gpu_agent_), &start_packet_);
// Wait for the completion signal of the packet
::signalWait(start_packet_.completion_signal, 1);
// restore signal to a value of 1
hsa_signal_store_screlease(start_signal_, 1);
// set a variable that this session has started
}
void DeviceProfileSession::PollMetrics(rocprofiler_device_profile_metric_t* data) {
// TODO: check if session was already started
// TODO: can't poll if stopped
// Reset the completion signal value for read packet
// TODO: clear profile output buffer
// Set completion signal
read_packet_.completion_signal = completion_signal_;
// Place the "Read" packet in the Queue
::submitPacket(DeviceProfileSession::getQueue(gpu_agent_), &read_packet_);
// Wait for the completion signal of the packet
::signalWait(read_packet_.completion_signal, 1);
// Collect counter values for events
metrics::GetCounterData(profile_, results_list_);
// evaluate metrics based on collected counter values
metrics::GetMetricsData(results_map_, metrics_list_);
for (size_t i = 0; i < profiling_data_.size(); i++) {
auto it = results_map_.find(profiling_data_[i]);
if (it != results_map_.end()) {
strcpy(data[i].metric_name, it->first.c_str());
data[i].value.value = it->second->val_double;
}
}
// restore signal to a value of 1
hsa_signal_store_screlease(completion_signal_, 1);
}
void DeviceProfileSession::StopSession() {
// TODO: check if session was already started
// Set completion signal
stop_packet_.completion_signal = stop_signal_;
// Place the "Stop" packet in the Queue
submitPacket(DeviceProfileSession::getQueue(gpu_agent_), &stop_packet_);
// Wait for the completion signal of the packet
// What is the correct value to wait for?
signalWait(stop_packet_.completion_signal, 1);
// restore signal to a value of 1
hsa_signal_store_screlease(stop_signal_, 1);
}
hsa_status_t device_cb(hsa_agent_t agent, void* data) {
hsa_device_type_t device_type;
devices_t* devices = reinterpret_cast<devices_t*>(data);
if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &device_type) != HSA_STATUS_SUCCESS)
fatal("hsa_agent_get_info failed");
switch (device_type) {
case HSA_DEVICE_TYPE_CPU:
devices->cpu_devices.push_back(agent);
break;
case HSA_DEVICE_TYPE_GPU:
devices->gpu_devices.push_back(agent);
break;
default:
devices->other_devices.push_back(agent);
break;
}
return HSA_STATUS_SUCCESS;
}
void get_hsa_agents_list(devices_t* device_list) {
// Enumerate the agents.
if (hsa_iterate_agents(device_cb, device_list) != HSA_STATUS_SUCCESS)
fatal("hsa_iterate_agents failed");
}
bool rocmtools::find_hsa_agent_cpu(uint64_t index, hsa_agent_t* agent) {
devices_t device_list;
get_hsa_agents_list(&device_list);
if (index > device_list.cpu_devices.size()) return false;
*agent = device_list.cpu_devices[index];
return true;
}
bool rocmtools::find_hsa_agent_gpu(uint64_t index, hsa_agent_t* agent) {
devices_t device_list;
get_hsa_agents_list(&device_list);
if (index > device_list.gpu_devices.size()) return false;
*agent = device_list.gpu_devices[index];
return true;
}
std::map<uint64_t, hsa_queue_t*> DeviceProfileSession::agent_queue_map_;
std::mutex DeviceProfileSession::agent_queue_map_mutex_;
+87
View File
@@ -0,0 +1,87 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef SRC_CORE_SESSION_DEVICE_PROFILING_H_
#define SRC_CORE_SESSION_DEVICE_PROFILING_H_
#include <rocprofiler.h>
#include "src/core/hsa/packets/packets_generator.h"
#include <mutex>
// #include "src/core/counters/rdc/rdc_metrics.h"
#include "src/core/counters/metrics/metrics.h"
#include "src/core/counters/metrics/eval_metrics.h"
namespace rocmtools {
class DeviceProfileSession {
public:
void StartSession();
void PollMetrics(rocprofiler_device_profile_metric_t* data);
void StopSession();
DeviceProfileSession(std::vector<std::string> counters, hsa_agent_t cpu_agent,
hsa_agent_t gpu_agent, uint64_t* session_id);
~DeviceProfileSession();
private:
bool createQueue();
bool generatePackets();
bool readPmcCounters();
static hsa_queue_t* getQueue(hsa_agent_t);
uint64_t session_id_;
std::vector<std::string> profiling_data_;
hsa_agent_t cpu_agent_;
hsa_agent_t gpu_agent_;
static std::map<uint64_t, hsa_queue_t*> agent_queue_map_;
static std::mutex agent_queue_map_mutex_;
Packet::packet_t start_packet_;
Packet::packet_t stop_packet_;
Packet::packet_t read_packet_;
MetricsDict* metrics_dict_;
std::vector<const Metric*> metrics_list_;
std::map<std::string, results_t*> results_map_;
std::vector<event_t> events_list_;
std::vector<results_t*> results_list_;
hsa_signal_t completion_signal_;
hsa_signal_t start_signal_;
hsa_signal_t stop_signal_;
// TODO: remove this or do actual cleanup
hsa_ven_amd_aqlprofile_profile_t* profile_;
};
bool find_hsa_agent_cpu(uint64_t index, hsa_agent_t* agent);
bool find_hsa_agent_gpu(uint64_t index, hsa_agent_t* agent);
} // namespace rocmtools
#endif // SRC_CORE_SESSION_DEVICE_PROFILING_H_
+217
View File
@@ -0,0 +1,217 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#include "src/core/session/filter.h"
#include <mutex>
#include "src/utils/helper.h"
namespace rocmtools {
Filter::Filter(rocprofiler_filter_id_t id, rocprofiler_filter_kind_t filter_kind,
rocprofiler_filter_data_t filter_data, uint64_t data_count)
: id_(id), kind_(filter_kind) {
switch (filter_kind) {
case ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION: {
break;
}
case ROCPROFILER_COUNTERS_COLLECTION: {
profiler_counter_names_.clear();
for (uint32_t j = 0; j < data_count; j++)
profiler_counter_names_.emplace_back(filter_data.counters_names[j]);
break;
}
case ROCPROFILER_PC_SAMPLING_COLLECTION:
case ROCPROFILER_ATT_TRACE: {
break;
}
case ROCPROFILER_SPM_COLLECTION: {
spm_parameter_ = filter_data.spm_parameters;
break;
}
case ROCPROFILER_API_TRACE: {
tracer_apis_.clear();
for (uint32_t j = 0; j < data_count; j++)
tracer_apis_.emplace_back(filter_data.trace_apis[j]);
break;
}
default: {
warning(
"Error: ROCMtools filter specified is not supported for "
"profiler mode!\n");
}
}
}
Filter::~Filter() {}
rocprofiler_filter_id_t Filter::GetId() { return id_; }
void Filter::SetBufferId(rocprofiler_buffer_id_t buffer_id) { buffer_id_ = buffer_id; }
rocprofiler_buffer_id_t Filter::GetBufferId() { return buffer_id_; }
bool Filter::HasBuffer() { return (buffer_id_.value > 0); }
rocprofiler_filter_kind_t Filter::GetKind() { return kind_; }
std::mutex counter_data_lock;
std::vector<std::string> Filter::GetCounterData() {
if (kind_ == ROCPROFILER_COUNTERS_COLLECTION) {
std::lock_guard<std::mutex> lock(counter_data_lock);
return profiler_counter_names_;
}
fatal(
"Error: ROCMtools filter specified is not supported for "
"Counter Collection Filter!\n");
}
std::vector<rocprofiler_tracer_activity_domain_t> Filter::GetTraceData() {
if (kind_ == ROCPROFILER_API_TRACE) {
return tracer_apis_;
}
fatal(
"Error: ROCMtools filter specified is not supported for "
"profiler mode!\n");
}
rocprofiler_spm_parameter_t* Filter::GetSpmParameterData() {
if (kind_ == ROCPROFILER_SPM_COLLECTION) {
return spm_parameter_;
}
fatal(
"Error: ROCMtools filter specified is not supported for "
"SPM collection mode!\n");
}
void Filter::SetProperty(rocprofiler_filter_property_t property) {
switch (property.kind) {
case ROCPROFILER_FILTER_HSA_TRACER_API_FUNCTIONS: {
if (kind_ == ROCPROFILER_API_TRACE) {
hsa_tracer_api_calls_.clear();
for (uint32_t j = 0; j < property.data_count; j++)
hsa_tracer_api_calls_.emplace_back(property.hsa_functions_names[j]);
} else {
throw(ROCPROFILER_STATUS_ERROR_SESSION_FILTER_DATA_MISMATCH);
}
break;
}
case ROCPROFILER_FILTER_HIP_TRACER_API_FUNCTIONS: {
if (kind_ == ROCPROFILER_API_TRACE) {
hip_tracer_api_calls_.clear();
for (uint32_t j = 0; j < property.data_count; j++)
hip_tracer_api_calls_.emplace_back(property.hip_functions_names[j]);
} else {
throw(ROCPROFILER_STATUS_ERROR_SESSION_FILTER_DATA_MISMATCH);
}
break;
}
case ROCPROFILER_FILTER_GPU_NAME: {
if (kind_ == ROCPROFILER_COUNTERS_COLLECTION ||
kind_ == ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION) {
agent_names_.clear();
for (uint32_t j = 0; j < property.data_count; j++)
agent_names_.emplace_back(property.name_regex[j]);
} else {
throw(ROCPROFILER_STATUS_ERROR_SESSION_FILTER_DATA_MISMATCH);
}
break;
}
case ROCPROFILER_FILTER_RANGE: {
if (kind_ == ROCPROFILER_COUNTERS_COLLECTION ||
kind_ == ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION) {
dispatch_range_[0] = property.range[0];
dispatch_range_[1] = property.range[1];
} else {
throw(ROCPROFILER_STATUS_ERROR_SESSION_FILTER_DATA_MISMATCH);
}
break;
}
case ROCPROFILER_FILTER_KERNEL_NAMES: {
if (kind_ == ROCPROFILER_COUNTERS_COLLECTION ||
kind_ == ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION) {
kernel_names_.clear();
for (uint32_t j = 0; j < property.data_count; j++)
kernel_names_.emplace_back(property.name_regex[j]);
} else {
throw(ROCPROFILER_STATUS_ERROR_SESSION_FILTER_DATA_MISMATCH);
}
break;
}
default:
break;
// TODO(aelwazir): Check for empty property
// warning(
// "Error: ROCMtools filter specified is not supported for "
// "profiler mode!\n");
}
}
std::variant<std::vector<std::string>, uint32_t*> Filter::GetProperty(
rocprofiler_filter_property_kind_t kind) {
std::variant<std::vector<std::string>, uint32_t*> property;
switch (kind) {
case ROCPROFILER_FILTER_GPU_NAME: {
property = agent_names_;
}
case ROCPROFILER_FILTER_RANGE: {
property = static_cast<uint32_t*>(dispatch_range_);
}
case ROCPROFILER_FILTER_KERNEL_NAMES: {
property = kernel_names_;
}
case ROCPROFILER_FILTER_HSA_TRACER_API_FUNCTIONS: {
property = hsa_tracer_api_calls_;
}
case ROCPROFILER_FILTER_HIP_TRACER_API_FUNCTIONS: {
property = hip_tracer_api_calls_;
}
default:
fatal(
"Error: ROCMtools filter specified is not supported for the given "
"kind!");
}
return property;
}
void Filter::SetCallback(rocprofiler_sync_callback_t& callback) { callback_ = callback; }
rocprofiler_sync_callback_t& Filter::GetCallback() { return callback_; }
size_t Filter::GetPropertiesCount(rocprofiler_filter_property_kind_t kind) {
switch (kind) {
case ROCPROFILER_FILTER_GPU_NAME: {
return agent_names_.size();
}
case ROCPROFILER_FILTER_RANGE: {
return 2;
}
case ROCPROFILER_FILTER_KERNEL_NAMES: {
return kernel_names_.size();
}
case ROCPROFILER_FILTER_HSA_TRACER_API_FUNCTIONS: {
return hsa_tracer_api_calls_.size();
}
case ROCPROFILER_FILTER_HIP_TRACER_API_FUNCTIONS: {
return hip_tracer_api_calls_.size();
}
}
fatal(
"Error: ROCMtools filter specified is not supported for the given "
"kind!");
}
} // namespace rocmtools
+80
View File
@@ -0,0 +1,80 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef SRC_CORE_SESSION_FILTER_H_
#define SRC_CORE_SESSION_FILTER_H_
#include <string>
#include <variant>
#include <vector>
#include "inc/rocprofiler.h"
#define ASSERTM(exp, msg) assert(((void)msg, exp))
namespace rocmtools {
class Filter {
public:
Filter(rocprofiler_filter_id_t id, rocprofiler_filter_kind_t filter_kind,
rocprofiler_filter_data_t filter_data, uint64_t data_count);
~Filter();
rocprofiler_filter_id_t GetId();
void SetBufferId(rocprofiler_buffer_id_t buffer_id);
rocprofiler_buffer_id_t GetBufferId();
bool HasBuffer();
rocprofiler_filter_kind_t GetKind();
std::vector<std::string> GetCounterData();
std::vector<rocprofiler_tracer_activity_domain_t> GetTraceData();
void SetCallback(rocprofiler_sync_callback_t& callback);
rocprofiler_sync_callback_t& GetCallback();
void SetProperty(rocprofiler_filter_property_t property);
std::variant<std::vector<std::string>, uint32_t*> GetProperty(
rocprofiler_filter_property_kind_t kind);
size_t GetPropertiesCount(rocprofiler_filter_property_kind_t kind);
rocprofiler_spm_parameter_t* GetSpmParameterData();
private:
rocprofiler_filter_id_t id_;
rocprofiler_filter_kind_t kind_;
rocprofiler_buffer_id_t buffer_id_{0};
std::vector<std::string> agent_names_; // GPU name filter
std::vector<std::string> hsa_tracer_api_calls_; // HSA API Functions
std::vector<std::string> hip_tracer_api_calls_; // HIP API Functions
std::vector<std::string> kernel_names_; // HIP/HSA API Functions
uint32_t dispatch_range_[2]; // Kernel Dispatches OR API Range
std::vector<std::string> profiler_counter_names_; // Counter Names to collect
std::vector<rocprofiler_tracer_activity_domain_t> tracer_apis_; // ROCTX/HIP/HSA API
rocprofiler_spm_parameter_t* spm_parameter_; // spm parameter
rocprofiler_sync_callback_t callback_;
};
} // namespace rocmtools
#endif // SRC_CORE_SESSION_FILTER_H_
+145
View File
@@ -0,0 +1,145 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#include "profiler.h"
#include <atomic>
#include <chrono>
#include <cstdint>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <stack>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "src/core/counters/basic/basic_counter.h"
#include "src/utils/helper.h"
#include "src/utils/logger.h"
#define ASSERTM(exp, msg) assert(((void)msg, exp))
namespace rocmtools {
namespace profiler {
uint64_t GetCounterID(std::string& counter_name) {
static auto counter_hash_fn = std::hash<std::string>{};
return counter_hash_fn(counter_name);
}
Profiler::Profiler(rocprofiler_buffer_id_t buffer_id, rocprofiler_filter_id_t filter_id,
rocprofiler_session_id_t session_id)
: buffer_id_(buffer_id), filter_id_(filter_id), session_id_(session_id) {}
Profiler::~Profiler() {}
void Profiler::AddCounterName(rocprofiler_counter_id_t counter_id, std::string counter_name) {
std::lock_guard<std::mutex> lock(counter_names_lock_);
counter_names_.emplace(counter_id.handle, counter_name);
}
void Profiler::AddCounterName(std::string& counter_name) {
std::lock_guard<std::mutex> lock(counter_names_lock_);
counter_names_.emplace(GetCounterID(counter_name), counter_name);
}
std::string& Profiler::GetCounterName(rocprofiler_counter_id_t counter_id) {
std::lock_guard<std::mutex> lock(counter_names_lock_);
auto it = counter_names_.find(counter_id.handle);
ASSERTM(it != counter_names_.end(), "Error: couldn't find kernel name with given descriptor!");
return it->second;
}
bool Profiler::FindCounter(rocprofiler_counter_id_t counter_id) {
std::lock_guard<std::mutex> lock(counter_names_lock_);
return counter_names_.find(counter_id.handle) != counter_names_.end();
}
size_t Profiler::GetCounterInfoSize(rocprofiler_counter_info_kind_t kind,
rocprofiler_counter_id_t counter_id) {
switch (kind) {
case ROCPROFILER_COUNTER_NAME: {
std::lock_guard<std::mutex> lock(counter_names_lock_);
return counter_names_.at(counter_id.handle).size();
break;
}
default:
warning("Not yet Supported!");
break;
}
return 0;
}
const char* Profiler::GetCounterInfo(rocprofiler_counter_info_kind_t kind,
rocprofiler_counter_id_t counter_id) {
switch (kind) {
case ROCPROFILER_COUNTER_NAME: {
std::lock_guard<std::mutex> lock(counter_names_lock_);
return counter_names_.at(counter_id.handle).c_str();
break;
}
default:
warning("Not yet Supported!");
break;
}
return nullptr;
}
void Profiler::StartReplayPass(rocprofiler_session_id_t session_id) { warning("Not yet supported!"); }
void Profiler::EndReplayPass() { warning("Not yet supported!"); }
bool Profiler::HasActivePass() {
warning("Not yet supported!");
return true;
}
void Profiler::AddPendingSignals(uint32_t writer_id, uint64_t kernel_object,
const hsa_signal_t& completion_signal,
rocprofiler_session_id_t session_id, rocprofiler_buffer_id_t buffer_id,
rocmtools::profiling_context_t* context,
uint64_t session_data_count,
hsa_ven_amd_aqlprofile_profile_t* profile,
rocprofiler_kernel_properties_t kernel_properties,
uint32_t thread_id, uint64_t queue_index) {
std::lock_guard<std::mutex> lock(sessions_pending_signals_lock_);
if (sessions_pending_signals_.find(writer_id) == sessions_pending_signals_.end())
sessions_pending_signals_.emplace(writer_id, std::vector<pending_signal_t>());
sessions_pending_signals_.at(writer_id).emplace_back(
pending_signal_t{kernel_object, completion_signal, session_id_, buffer_id, context,
session_data_count, profile});
}
const std::vector<pending_signal_t>& Profiler::GetPendingSignals(uint32_t writer_id) {
std::lock_guard<std::mutex> lock(sessions_pending_signals_lock_);
assert(sessions_pending_signals_.find(writer_id) != sessions_pending_signals_.end() &&
"writer_id is not found in the pending_signals");
return sessions_pending_signals_.at(writer_id);
}
bool Profiler::CheckPendingSignalsIsEmpty() {
std::lock_guard<std::mutex> lock(sessions_pending_signals_lock_);
return sessions_pending_signals_.empty();
}
} // namespace profiler
} // namespace rocmtools
+106
View File
@@ -0,0 +1,106 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef SRC_TOOLS_PROFILER_PROFILER_H_
#define SRC_TOOLS_PROFILER_PROFILER_H_
#include <hsa/hsa_ven_amd_aqlprofile.h>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <optional>
#include <string>
#include <vector>
#include "inc/rocprofiler.h"
#include "src/core/counters/basic/basic_counter.h"
#include "src/core/counters/metrics/eval_metrics.h"
typedef void (*rocprofiler_add_profiler_record_t)(rocprofiler_record_profiler_t&& record,
rocprofiler_session_id_t session_id);
typedef rocprofiler_timestamp_t (*rocprofiler_get_timestamp_t)();
namespace rocmtools {
typedef struct {
uint64_t kernel_descriptor;
hsa_signal_t signal;
rocprofiler_session_id_t session_id;
rocprofiler_buffer_id_t buffer_id;
rocmtools::profiling_context_t* context;
uint64_t counters_count;
hsa_ven_amd_aqlprofile_profile_t* profile;
rocprofiler_kernel_properties_t kernel_properties;
uint32_t thread_id;
uint64_t queue_index;
} pending_signal_t;
namespace profiler {
uint64_t GetCounterID(std::string& counter_name);
class Profiler {
public:
Profiler(rocprofiler_buffer_id_t buffer_id, rocprofiler_filter_id_t filter_id,
rocprofiler_session_id_t session_id);
~Profiler();
void AddPendingSignals(uint32_t writer_id, uint64_t kernel_object,
const hsa_signal_t& completion_signal, rocprofiler_session_id_t session_id,
rocprofiler_buffer_id_t buffer_id,
rocmtools::profiling_context_t* context, uint64_t session_data_count,
hsa_ven_amd_aqlprofile_profile_t* profile,
rocprofiler_kernel_properties_t kernel_properties, uint32_t thread_id,
uint64_t queue_index);
const std::vector<pending_signal_t>& GetPendingSignals(uint32_t writer_id);
bool CheckPendingSignalsIsEmpty();
void AddCounterName(rocprofiler_counter_id_t handler, std::string counter_name);
void AddCounterName(std::string& counter_name);
std::string& GetCounterName(rocprofiler_counter_id_t handler);
bool FindCounter(rocprofiler_counter_id_t counter_id);
size_t GetCounterInfoSize(rocprofiler_counter_info_kind_t kind, rocprofiler_counter_id_t counter_id);
const char* GetCounterInfo(rocprofiler_counter_info_kind_t kind, rocprofiler_counter_id_t counter_id);
void StartReplayPass(rocprofiler_session_id_t session_id);
void EndReplayPass();
bool HasActivePass();
private:
std::mutex counter_names_lock_;
std::map<uint64_t, std::string> counter_names_;
rocprofiler_get_timestamp_t get_timestamp_fn_;
rocprofiler_buffer_id_t buffer_id_;
rocprofiler_filter_id_t filter_id_;
rocprofiler_session_id_t session_id_;
std::mutex sessions_pending_signals_lock_;
std::map<uint32_t, std::vector<pending_signal_t>> sessions_pending_signals_;
};
} // namespace profiler
} // namespace rocmtools
#endif // SRC_TOOLS_PROFILER_PROFILER_H_
+345
View File
@@ -0,0 +1,345 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#include "session.h"
#include <string.h>
#include <atomic>
#include <cassert>
#include <cstdint>
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include "rocprofiler.h"
#include "src/pcsampler/session/pc_sampler.h"
#include "src/utils/helper.h"
#include "src/core/hsa/queues/queue.h"
namespace rocmtools {
Session::Session(rocprofiler_replay_mode_t replay_mode, rocprofiler_session_id_t session_id)
: session_id_(session_id), is_active_(false), replay_mode_(replay_mode) {}
Session::~Session() {
while (GetCurrentActiveInterruptSignalsCount() > 0) {
}
if (profiler_started_.load(std::memory_order_release)) {
delete profiler_;
profiler_started_.exchange(false, std::memory_order_release);
}
// if (tracer_started_.load(std::memory_order_release)) {
// delete tracer_;
// tracer_started_.exchange(false, std::memory_order_release);
// }
// {
// std::lock_guard<std::mutex> lock(filters_lock_);
// buffers_.clear();
// }
}
void Session::DisableTools(rocprofiler_buffer_id_t buffer_id) {
if ((FindFilterWithKind(ROCPROFILER_COUNTERS_COLLECTION) &&
GetFilter(GetFilterIdWithKind(ROCPROFILER_COUNTERS_COLLECTION))->GetBufferId().value ==
buffer_id.value) ||
(FindFilterWithKind(ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION) &&
GetFilter(GetFilterIdWithKind(ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION))
->GetBufferId()
.value == buffer_id.value)) {
if (profiler_started_.load(std::memory_order_release)) {
// Implement Disable Profiling
}
}
if (FindFilterWithKind(ROCPROFILER_API_TRACE) &&
GetFilter(GetFilterIdWithKind(ROCPROFILER_API_TRACE))->GetBufferId().value == buffer_id.value) {
if (tracer_started_.load(std::memory_order_release)) {
tracer_->DisableRoctracer();
}
}
}
void Session::Start() {
std::lock_guard<std::mutex> lock(session_lock_);
if (!is_active_) {
if (FindFilterWithKind(ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION)) {
if (profiler_started_.load(std::memory_order_release)) delete profiler_;
profiler_ = new profiler::Profiler(
GetFilter(GetFilterIdWithKind(ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION))->GetBufferId(),
GetFilter(GetFilterIdWithKind(ROCPROFILER_DISPATCH_TIMESTAMPS_COLLECTION))->GetId(),
session_id_);
profiler_started_.exchange(true, std::memory_order_release);
}
if (FindFilterWithKind(ROCPROFILER_COUNTERS_COLLECTION)) {
if (profiler_started_.load(std::memory_order_release)) delete profiler_;
profiler_ = new profiler::Profiler(
GetFilter(GetFilterIdWithKind(ROCPROFILER_COUNTERS_COLLECTION))->GetBufferId(),
GetFilter(GetFilterIdWithKind(ROCPROFILER_COUNTERS_COLLECTION))->GetId(), session_id_);
profiler_started_.exchange(true, std::memory_order_release);
}
if (FindFilterWithKind(ROCPROFILER_SPM_COLLECTION)) {
if (spm_started_.load(std::memory_order_release)) delete spmcounter_;
rocprofiler_spm_parameter_t* spmparameter =
GetFilter(GetFilterIdWithKind(ROCPROFILER_SPM_COLLECTION))->GetSpmParameterData();
spmcounter_ = new spm::SpmCounters(
GetFilter(GetFilterIdWithKind(ROCPROFILER_SPM_COLLECTION))->GetBufferId(),
GetFilter(GetFilterIdWithKind(ROCPROFILER_SPM_COLLECTION))->GetId(), spmparameter,
session_id_);
if (profiler_started_.load(std::memory_order_release)) delete profiler_;
profiler_ = new profiler::Profiler(
GetFilter(GetFilterIdWithKind(ROCPROFILER_SPM_COLLECTION))->GetBufferId(),
GetFilter(GetFilterIdWithKind(ROCPROFILER_SPM_COLLECTION))->GetId(), session_id_);
profiler_started_.exchange(true, std::memory_order_release);
}
if (FindFilterWithKind(ROCPROFILER_API_TRACE)) {
std::vector<rocprofiler_tracer_activity_domain_t> domains =
GetFilter(GetFilterIdWithKind(ROCPROFILER_API_TRACE))->GetTraceData();
if (!tracer_started_.load(std::memory_order_release)) {
tracer_ = new tracer::Tracer(
session_id_, GetFilter(GetFilterIdWithKind(ROCPROFILER_API_TRACE))->GetCallback(),
GetFilter(GetFilterIdWithKind(ROCPROFILER_API_TRACE))->GetBufferId(), domains);
tracer_started_.exchange(true, std::memory_order_release);
}
tracer_->StartRoctracer();
}
if (FindFilterWithKind(ROCPROFILER_PC_SAMPLING_COLLECTION)) {
if (!pc_sampler_started_.load(std::memory_order_release)) {
pc_sampler_ = new pc_sampler::PCSampler(
GetFilter(GetFilterIdWithKind(ROCPROFILER_PC_SAMPLING_COLLECTION))->GetBufferId(),
GetFilter(GetFilterIdWithKind(ROCPROFILER_PC_SAMPLING_COLLECTION))->GetId(), session_id_);
pc_sampler_started_.exchange(true, std::memory_order_release);
}
pc_sampler_->Start();
}
is_active_ = true;
if (FindFilterWithKind(ROCPROFILER_SPM_COLLECTION)) startSpm();
}
}
void Session::Terminate() {
if (is_active_) {
std::lock_guard<std::mutex> lock(session_lock_);
if (FindFilterWithKind(ROCPROFILER_SPM_COLLECTION)) {
{
stopSpm();
delete spmcounter_;
}
}
if (FindFilterWithKind(ROCPROFILER_API_TRACE)) {
std::vector<rocprofiler_tracer_activity_domain_t> domains =
GetFilter(GetFilterIdWithKind(ROCPROFILER_API_TRACE))->GetTraceData();
if (tracer_started_.load(std::memory_order_release)) {
tracer_->StopRoctracer();
delete tracer_;
tracer_started_.exchange(false, std::memory_order_release);
}
}
if (FindFilterWithKind(ROCPROFILER_PC_SAMPLING_COLLECTION)) {
if (pc_sampler_started_.load(std::memory_order_release)) {
pc_sampler_->Stop();
delete pc_sampler_;
pc_sampler_started_.exchange(false, std::memory_order_release);
}
}
is_active_ = false;
}
}
rocprofiler_session_id_t Session::GetId() { return session_id_; }
bool Session::IsActive() { return is_active_; }
profiler::Profiler* Session::GetProfiler() { return profiler_; }
tracer::Tracer* Session::GetTracer() { return tracer_; }
spm::SpmCounters* Session::GetSpmCounter() { return spmcounter_; }
pc_sampler::PCSampler* Session::GetPCSampler() { return pc_sampler_; }
rocprofiler_filter_id_t Session::CreateFilter(rocprofiler_filter_kind_t filter_kind,
rocprofiler_filter_data_t filter_data,
uint64_t data_count,
rocprofiler_filter_property_t property) {
rocprofiler_filter_id_t id =
rocprofiler_filter_id_t{filters_counter_.fetch_add(1, std::memory_order_release)};
{
std::lock_guard<std::mutex> lock(filters_lock_);
filters_.emplace_back(new Filter{id, filter_kind, filter_data, data_count});
filters_.back()->SetProperty(property);
}
return id;
}
bool Session::FindFilter(rocprofiler_filter_id_t filter_id) {
{
std::lock_guard<std::mutex> lock(filters_lock_);
for (auto& filter : filters_) {
if (filter->GetId().value == filter_id.value) return true;
}
}
return false;
}
void Session::DestroyFilter(rocprofiler_filter_id_t filter_id) {
{
std::vector<Filter*>::iterator filter;
std::lock_guard<std::mutex> lock(filters_lock_);
for (filter = filters_.begin(); filter != filters_.end(); ++filter) {
if ((*filter) && (*filter)->GetId().value == filter_id.value) filters_.erase(filter);
}
}
}
Filter* Session::GetFilter(rocprofiler_filter_id_t filter_id) {
{
std::lock_guard<std::mutex> lock(filters_lock_);
for (auto& filter : filters_) {
if (filter->GetId().value == filter_id.value) return filter;
}
}
fatal("Filter is not found!");
}
bool Session::CheckFilterBufferSize(rocprofiler_filter_id_t filter_id,
rocprofiler_buffer_id_t buffer_id) {
// TODO(aelwazir): To be implemented
return true;
}
bool Session::HasFilter() { return filters_.size() > 0; }
bool Session::FindFilterWithKind(rocprofiler_filter_kind_t kind) {
{
std::lock_guard<std::mutex> lock(filters_lock_);
for (auto& filter : filters_) {
if (filter->GetKind() == kind) return true;
}
}
return false;
}
rocprofiler_filter_id_t Session::GetFilterIdWithKind(rocprofiler_filter_kind_t kind) {
{
std::lock_guard<std::mutex> lock(filters_lock_);
for (auto& filter : filters_) {
if (filter->GetKind() == kind) return filter->GetId();
}
}
return rocprofiler_filter_id_t{0};
}
bool Session::HasBuffer() { return buffers_.size() > 0; }
rocprofiler_buffer_id_t Session::CreateBuffer(rocprofiler_buffer_callback_t buffer_callback,
size_t buffer_size) {
rocprofiler_buffer_id_t id =
rocprofiler_buffer_id_t{buffers_counter_.fetch_add(1, std::memory_order_release)};
{
std::lock_guard<std::mutex> lock(buffers_lock_);
buffers_.emplace(id.value,
new Memory::GenericBuffer(session_id_, id, buffer_size, buffer_callback));
}
return id;
}
bool Session::FindBuffer(rocprofiler_buffer_id_t buffer_id) {
{
std::lock_guard<std::mutex> lock(buffers_lock_);
return buffers_.find(buffer_id.value) != buffers_.end();
}
}
void Session::DestroyTracer() { /* tracer_.reset(); */
}
void Session::DestroyBuffer(rocprofiler_buffer_id_t buffer_id) {
{
std::lock_guard<std::mutex> lock(filters_lock_);
delete buffers_.at(buffer_id.value);
buffers_.erase(buffer_id.value);
// if (buffers_.find(buffer_id.value) != buffers_.end() &&
// buffers_.at(buffer_id.value)->IsValid())
// buffers_.at(buffer_id.value).reset();
}
}
rocprofiler_status_t Session::startSpm() {
if (spmcounter_) {
spm_started_.exchange(true, std::memory_order_release);
return spmcounter_->startSpm();
} else {
std::cout << "Apply the SPM Filter" << std::endl;
return ROCPROFILER_STATUS_ERROR;
}
}
rocprofiler_status_t Session::stopSpm() {
if (spmcounter_ && spm_started_.load()) {
spm_started_.exchange(false, std::memory_order_release);
return spmcounter_->stopSpm();
} else {
std::cout << "SPM not started" << std::endl;
return ROCPROFILER_STATUS_ERROR;
}
}
Memory::GenericBuffer* Session::GetBuffer(rocprofiler_buffer_id_t buffer_id) {
{
std::lock_guard<std::mutex> lock(buffers_lock_);
return buffers_.at(buffer_id.value);
}
}
void Session::PushRangeLabels(const std::string label) {
{
std::lock_guard<std::mutex> lock(range_labels_lock_);
range_labels_.push(label);
}
current_range_label_ = label;
}
bool Session::PopRangeLabels() {
{
std::lock_guard<std::mutex> lock(range_labels_lock_);
if (range_labels_.empty()) {
return false;
}
range_labels_.pop();
}
current_range_label_ = "";
return true;
}
std::string& Session::GetCurrentRangeLabel() { return current_range_label_; }
std::mutex& Session::GetSessionLock() { return session_lock_; }
static std::atomic<uint64_t> SESSION_COUNTER{1};
// use some util function to generate a unique id
uint64_t GenerateUniqueSessionId() {
return SESSION_COUNTER.fetch_add(1, std::memory_order_release);
}
} // namespace rocmtools
+131
View File
@@ -0,0 +1,131 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef SRC_CORE_SESSION_SESSION_H_
#define SRC_CORE_SESSION_SESSION_H_
#include <hsa/hsa.h>
#include <hsa/hsa_ven_amd_aqlprofile.h>
#include <map>
#include <memory>
#include <mutex>
#include <stack>
#include <string>
#include <variant>
#include <vector>
#include "inc/rocprofiler.h"
#include "src/core/memory/generic_buffer.h"
#include "src/core/session/filter.h"
#include "profiler/profiler.h"
#include "tracer/tracer.h"
#include "spm/spm.h"
#include "src/pcsampler/session/pc_sampler.h"
#define ASSERTM(exp, msg) assert(((void)msg, exp))
namespace rocmtools {
class Session {
public:
Session(rocprofiler_replay_mode_t replay_mode, rocprofiler_session_id_t session_id);
~Session();
void DisableTools(rocprofiler_buffer_id_t buffer_id);
void Start();
void Terminate();
rocprofiler_session_id_t GetId();
bool IsActive();
void DestroyTracer();
profiler::Profiler* GetProfiler();
tracer::Tracer* GetTracer();
spm::SpmCounters* GetSpmCounter();
pc_sampler::PCSampler* GetPCSampler();
// Filter
rocprofiler_filter_id_t CreateFilter(rocprofiler_filter_kind_t filter_kind,
rocprofiler_filter_data_t filter_data, uint64_t data_count,
rocprofiler_filter_property_t property);
bool FindFilter(rocprofiler_filter_id_t filter_id);
void DestroyFilter(rocprofiler_filter_id_t filter_id);
Filter* GetFilter(rocprofiler_filter_id_t filter_id);
bool HasFilter();
bool FindFilterWithKind(rocprofiler_filter_kind_t kind);
rocprofiler_filter_id_t GetFilterIdWithKind(rocprofiler_filter_kind_t kind);
std::mutex& GetSessionLock();
bool CheckFilterBufferSize(rocprofiler_filter_id_t filter_id, rocprofiler_buffer_id_t buffer_id);
// Buffer
rocprofiler_buffer_id_t CreateBuffer(rocprofiler_buffer_callback_t buffer_callback,
size_t buffer_size);
bool FindBuffer(rocprofiler_buffer_id_t buffer_id);
void DestroyBuffer(rocprofiler_buffer_id_t buffer_id);
Memory::GenericBuffer* GetBuffer(rocprofiler_buffer_id_t buffer_id);
bool HasBuffer();
rocprofiler_status_t startSpm();
rocprofiler_status_t stopSpm();
// Range Labels
void PushRangeLabels(const std::string label);
bool PopRangeLabels();
std::string& GetCurrentRangeLabel();
private:
rocprofiler_session_id_t session_id_;
std::atomic<bool> is_active_;
rocprofiler_replay_mode_t replay_mode_;
std::mutex session_lock_;
std::atomic<uint64_t> filters_counter_{1};
std::mutex filters_lock_;
std::vector<Filter*> filters_;
std::atomic<bool> profiler_started_{false};
std::atomic<bool> tracer_started_{false};
std::atomic<bool> spm_started_{false};
profiler::Profiler* profiler_;
tracer::Tracer* tracer_;
spm::SpmCounters* spmcounter_;
std::atomic<bool> pc_sampler_started_{false};
pc_sampler::PCSampler* pc_sampler_;
std::atomic<uint64_t> buffers_counter_{1};
std::mutex buffers_lock_;
std::map<uint64_t, Memory::GenericBuffer*> buffers_;
std::atomic<uint64_t> records_counter_{1};
std::mutex range_labels_lock_;
std::stack<std::string> range_labels_;
std::string current_range_label_;
};
uint64_t GenerateUniqueSessionId();
} // namespace rocmtools
#endif // SRC_CORE_SESSION_SESSION_H_
+425
View File
@@ -0,0 +1,425 @@
#include "spm.h"
#include "src/core/hsa/hsa_support.h"
#include "src/utils/helper.h"
#include "src/api/rocmtool.h"
#include <hsa/hsa.h>
#include <stdlib.h>
#include <bitset>
#define QUEUE_NUM_PACKETS 64
static const size_t CMD_SLOT_SIZE_B = 0x40;
// #define ASSERTM(exp, msg) assert(((void)msg, exp))
#define DEST_BUFFER_MAX 4
namespace {
struct devices_t {
std::vector<hsa_agent_t> cpu_devices;
std::vector<hsa_agent_t> gpu_devices;
std::vector<hsa_agent_t> other_devices;
};
typedef struct {
uint32_t size; // size of buffer in bytes
uint32_t timeout;
uint32_t len; // len of streamed data in spm buffer
void* addr; // address of spm buffer
bool data_loss; // OUT
} spm_buffer_params_t;
typedef struct spm_data_buffer {
char* addr;
uint32_t buffSize;
// spm_data_buffer(char * data, uint32_t len) :
// addr{data}, buffSize{len} {}
} spm_data_buffer_t;
std::queue<spm_data_buffer_t> process_queue;
// spm_buffer_params_t spm_buffer_params[3];
std::atomic<bool> is_started;
std::atomic<bool> buffer_read_flag;
std::atomic<uint32_t> spm_buffer_idx;
std::thread thread_buffer_setup;
std::thread thread_spm_data_parse;
// std::atomic<uint32_t> currIndex;
// std::atomic<uint32_t> preIndex;
std::mutex processQueueLock;
// void spmDataParse();
// void spmBufferSetup(hsa_agent_t preferredGpuNode);
// FILE* fd;
// rocprofiler_status_t setSpmDestBuffer(hsa_agent_t preferred_agent, size_t size_in_bytes,
// uint32_t* timeout, uint32_t* size_copied, void* dest,
// bool* is_data_loss) {
// [[maybe_unused]] hsa_status_t status = HSA_STATUS_SUCCESS;
// #if 0
// status = rocmtools::hsa_support::GetAmdExtTable().hsa_amd_spm_set_dest_buffer_fn(
// preferred_agent, size_in_bytes, timeout, size_copied, dest, is_data_loss);
// ASSERTM(status == HSA_STATUS_SUCCESS, "ERROR: SPM set buffer failed");
// #endif
// return ROCPROFILER_STATUS_SUCCESS;
// }
// rocprofiler_status_t SetDestBuffer(hsa_agent_t GPUNode, uint32_t size, uint32_t timeout) {
// rocprofiler_status_t ret;
// uint32_t idx = currIndex.load(std::memory_order_release);
// if (size) {
// // Check if user buffer in using
// if (spm_buffer_params[idx].addr != NULL) {
// std::cout << "Buffer in use ." << std::endl;
// return ROCPROFILER_STATUS_ERROR;
// }
// spm_buffer_params[idx].addr = malloc(size);
// if (spm_buffer_params[idx].addr == NULL) {
// std::cout << "Malloc(size) Failed." << std::endl;
// return ROCPROFILER_STATUS_ERROR;
// }
// } else {
// spm_buffer_params[idx].addr = NULL;
// }
// spm_buffer_params[idx].timeout = timeout;
// spm_buffer_params[idx].data_loss = 0;
// ret = setSpmDestBuffer(GPUNode, spm_buffer_params[idx].size, &spm_buffer_params[idx].timeout,
// &spm_buffer_params[idx].len, spm_buffer_params[idx].addr,
// &spm_buffer_params[idx].data_loss);
// if (ret != ROCPROFILER_STATUS_SUCCESS) {
// std::cout << "Fail to set Dest Buf "
// << "ret " << ret << std::endl;
// return ROCPROFILER_STATUS_ERROR;
// }
// if (spm_buffer_params[idx].data_loss) std::cout << "Data Loss" << std::endl;
// if (spm_buffer_params[idx].len) {
// uint32_t pidx = preIndex.load(std::memory_order_release);
// if (spm_buffer_params[idx].len == spm_buffer_params[pidx].size) {
// std::cout << "Buffer completely filled with bytes" << spm_buffer_params[idx].len << std::endl;
// fd = fopen("SPM_rocmtool_data.txt", "wb");
// size_t retele = fwrite(spm_buffer_params[pidx].addr, 1, spm_buffer_params[idx].len, fd);
// if (retele <= 0) rocmtools::warning("SPM Data is wrong!");
// fclose(fd);
// } else {
// std::cout << "Buffer partially filled with %d bytes" << spm_buffer_params[idx].len
// << std::endl;
// }
// if (timeout)
// if (spm_buffer_params[idx].timeout == timeout) std::cout << "Timeout occurred" << std::endl;
// ret = ROCPROFILER_STATUS_SUCCESS;
// } else {
// std::cout << "Data collection failed" << std::endl;
// ret = ROCPROFILER_STATUS_SUCCESS;
// }
// spm_buffer_params[idx].addr = NULL;
// return ret;
// }
// void spmBufferSetup(hsa_agent_t GPUNode) {
// rocprofiler_status_t ret;
// if (is_started.load(std::memory_order_release)) {
// uint32_t idx = currIndex.load(std::memory_order_release);
// ret = SetDestBuffer(GPUNode, spm_buffer_params[idx].size, spm_buffer_params[idx].timeout);
// if (ret != ROCPROFILER_STATUS_SUCCESS) {
// std::cout << "Fail to set Dest Buf 2 "
// << "ret " << ret << std::endl;
// return;
// }
// usleep(5 * 1000);
// // Set blocking dest buff
// currIndex.store(1, std::memory_order_release);
// preIndex.store(0, std::memory_order_release);
// spm_buffer_params[idx].timeout = 1000;
// ret = SetDestBuffer(GPUNode, spm_buffer_params[idx].size, spm_buffer_params[idx].timeout);
// if (ret != ROCPROFILER_STATUS_SUCCESS) {
// std::cout << "Fail to set Dest Buf 1"
// << "ret " << ret << std::endl;
// }
// usleep(5 * 1000);
// currIndex.store(0, std::memory_order_release);
// preIndex.store(1, std::memory_order_release);
// spm_buffer_params[idx].timeout = 80;
// }
// }
// void AddSpmRecords(std::vector<uint16_t>& sample) {
// // Getting timestamps
// int index = 0;
// int nSample = 0;
// int se = 0;
// uint64_t count = 0;
// std::vector<uint64_t> timestamp_vec;
// // Get Buffer
// rocmtools::Session* session =
// rocmtools::GetROCMToolObj()->GetSession(rocmtools::GetROCMToolObj()->GetCurrentSessionId());
// rocprofiler_filter_id_t filter_id = session->GetFilterIdWithKind(ROCPROFILER_SPM_COLLECTION);
// rocmtools::Filter* filter = session->GetFilter(filter_id);
// rocprofiler_buffer_id_t buffer_id = filter->GetBufferId();
// Memory::GenericBuffer* buffer = session->GetBuffer(buffer_id);
// // Getting timestamps
// while (static_cast<size_t>(index) < sample.size()) {
// int64_t timestamp = sample[index] >> 16 | sample[index + 1];
// timestamp = timestamp >> 16 | sample[index + 2];
// timestamp = timestamp >> 16 | sample[index + 3];
// timestamp_vec.emplace_back(timestamp);
// index = index + 160;
// }
// index = 32;
// while (static_cast<size_t>(index) < sample.size()) {
// se = 0;
// rocprofiler_record_spm_t record = {};
// record.timestamps = rocprofiler_record_header_timestamp_t{timestamp_vec[nSample]};
// while (se < 4) {
// count = 0;
// while (count < 15) {
// record.shader_engine_data[se].counters_data[count].value = sample[index];
// record.shader_engine_data[se].counters_data[count + 15].value = sample[index + 15];
// count++;
// }
// se++;
// }
// record.header.id = rocprofiler_record_id_t{rocmtools::GetROCMToolObj()->GetUniqueRecordId()};
// buffer->AddRecord(record);
// nSample++;
// index += 160;
// }
// }
// void spmDataParse() {
// std::vector<uint16_t> lines;
// fd = fopen("SPM_rocmtool_data.txt", "rb");
// while (!feof(fd)) {
// char bytes[2];
// size_t size = fread(&bytes, 1, 2, fd);
// if (size) {
// uint16_t value;
// memcpy(&value, bytes, 2);
// lines.push_back(value);
// }
// }
// AddSpmRecords(lines);
// }
hsa_status_t device_cb(hsa_agent_t agent, void* data) {
hsa_device_type_t device_type;
devices_t* devices = reinterpret_cast<devices_t*>(data);
if (hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &device_type) != HSA_STATUS_SUCCESS)
rocmtools::fatal("hsa_agent_get_info failed");
switch (device_type) {
case HSA_DEVICE_TYPE_CPU:
devices->cpu_devices.push_back(agent);
break;
case HSA_DEVICE_TYPE_GPU:
devices->gpu_devices.push_back(agent);
break;
default:
devices->other_devices.push_back(agent);
break;
}
return HSA_STATUS_SUCCESS;
}
void get_hsa_agents_list(devices_t* device_list) {
hsa_status_t status;
// Enumerate the agents.
status = hsa_iterate_agents(device_cb, device_list);
if (status != HSA_STATUS_SUCCESS) rocmtools::fatal("hsa_iterate_agents failed");
}
uint64_t submitPacket(hsa_queue_t* queue, const void* packet) {
const uint32_t slot_size_b = CMD_SLOT_SIZE_B;
// advance command queue
const uint64_t write_idx =
rocmtools::hsa_support::GetCoreApiTable().hsa_queue_add_write_index_scacq_screl_fn(queue, 1);
while ((write_idx -
rocmtools::hsa_support::GetCoreApiTable().hsa_queue_load_read_index_relaxed_fn(queue)) >=
queue->size) {
sched_yield(); // TODO: remove
}
const uint32_t slot_idx = (uint32_t)(write_idx % queue->size);
uint32_t* queue_slot =
reinterpret_cast<uint32_t*>((uintptr_t)(queue->base_address) + (slot_idx * slot_size_b));
const uint32_t* slot_data = reinterpret_cast<const uint32_t*>(packet);
// Copy buffered commands into the queue slot.
// Overwrite the AQL invalid header (first dword) last.
// This prevents the slot from being read until it's fully written.
memcpy(&queue_slot[1], &slot_data[1], slot_size_b - sizeof(uint32_t));
std::atomic<uint32_t>* header_atomic_ptr =
reinterpret_cast<std::atomic<uint32_t>*>(&queue_slot[0]);
header_atomic_ptr->store(slot_data[0], std::memory_order_release);
// ringdoor bell
rocmtools::hsa_support::GetCoreApiTable().hsa_signal_store_relaxed_fn(queue->doorbell_signal,
write_idx);
return write_idx;
}
// bool createHsaQueue(hsa_queue_t** queue, hsa_agent_t gpu_agent) {
// // create a single-producer queue
// // TODO: check if API args are correct, especially UINT32_MAX
// hsa_status_t status;
// status = rocmtools::hsa_support::GetCoreApiTable().hsa_queue_create_fn(
// gpu_agent, QUEUE_NUM_PACKETS, HSA_QUEUE_TYPE_SINGLE, nullptr, nullptr, UINT32_MAX, UINT32_MAX,
// queue);
// if (status != HSA_STATUS_SUCCESS) rocmtools::fatal("queue creation failed");
// return (status == HSA_STATUS_SUCCESS);
// }
hsa_signal_value_t signalWait(const hsa_signal_t& signal, const hsa_signal_value_t& signal_value) {
const hsa_signal_value_t exp_value = signal_value - 1;
hsa_signal_value_t ret_value = signal_value;
while (1) {
// TODO: The 4th argument mentioning timeout is current set to UINT64_MAX.
// Probably a maximum wait time should be set. We don't want application to hang because of
// unlimited wait.
// TODO2 : try 500000 assuming nanosecond granularity -- must be verified.
ret_value = rocmtools::hsa_support::GetCoreApiTable().hsa_signal_wait_scacquire_fn(
signal, HSA_SIGNAL_CONDITION_LT, signal_value, UINT64_MAX, HSA_WAIT_STATE_BLOCKED);
if (ret_value == exp_value) break;
if (ret_value != signal_value)
rocmtools::fatal("Error: signalWait: signal_value(%lu), ret_value(%lu)", signal_value,
ret_value);
}
return ret_value;
}
} // namespace
namespace rocmtools {
spm::SpmCounters::SpmCounters(rocprofiler_buffer_id_t buffer_id, rocprofiler_filter_id_t filter_id,
rocprofiler_spm_parameter_t* spmparameter,
rocprofiler_session_id_t session_id)
: buffer_id_(buffer_id),
filter_id_(filter_id),
spmparameter_(spmparameter),
session_id_(session_id) {
queue_ = nullptr;
devices_t* device_list_ = new devices_t;
get_hsa_agents_list(device_list_);
defaultGpuNode_ = device_list_->gpu_devices[0];
defaultCpuNode_ = device_list_->cpu_devices[0];
// create signals
hsa_status_t status =
hsa_support::GetCoreApiTable().hsa_signal_create_fn(1, 0, NULL, &start_signal_);
if (status != HSA_STATUS_SUCCESS) fatal("start signal creation failed");
status = hsa_support::GetCoreApiTable().hsa_signal_create_fn(1, 0, NULL, &stop_signal_);
if (status != HSA_STATUS_SUCCESS) fatal("start signal creation failed");
is_started.store(false, std::memory_order_relaxed);
buffer_read_flag.store(false, std::memory_order_relaxed);
spm_buffer_idx.store(false, std::memory_order_relaxed);
}
rocprofiler_status_t spm::SpmCounters::startSpm() {
if (spmparameter_->gpu_agent_id != NULL)
preferredGpuNode_.handle = spmparameter_->gpu_agent_id->handle;
else
// else choose the default node to collect SPM
preferredGpuNode_ = defaultGpuNode_;
// hsa_agent_t preferred_cpu_agent = defaultCpuNode_;
// int counter_count = spmparameter_->counters_count;
// Packet::packet_t start_packet;
#if 0
hsa_status_t hsa_status = hsa_support::GetAmdExtTable().hsa_amd_spm_acquire_fn(preferredGpuNode_);
if (hsa_status == HSA_STATUS_SUCCESS) {
if (!createHsaQueue(&queue_, preferredGpuNode_))
std::cout << "Create queue is failed" << std::endl;
agent_queue_map_.insert(std::make_pair(preferredGpuNode_.handle, queue_));
// Generate the start and stop packets
char gpu_name[64];
hsa_agent_get_info(preferredGpuNode_, HSA_AGENT_INFO_NAME, &gpu_name);
std::vector<std::string> counter_names;
for (int i = 0; i < counter_count; i++) {
counter_names.push_back(std::string(spmparameter_->counters_names[i]));
}
profiles_ =
Packet::InitializeAqlPackets(preferred_cpu_agent, preferredGpuNode_, counter_names, true);
// Submit the start packet
start_packet = *(*profiles_)[0].first->start_packet;
start_packet.header = HSA_PACKET_TYPE_VENDOR_SPECIFIC << HSA_PACKET_HEADER_TYPE;
start_packet.completion_signal = {};
start_packet.completion_signal = start_signal_;
submitPacket(queue_, &start_packet);
signalWait(start_packet.completion_signal, 1);
// restore signal to a value of 1
hsa_signal_store_screlease(start_signal_, 1);
is_started.exchange(true, std::memory_order_release);
uint32_t timeout = 10000;
const uint32_t spm_buffer_size = 0x2000000;
spm_buffer_params[0].size = spm_buffer_size;
spm_buffer_params[0].timeout = timeout;
spm_buffer_params[0].len = 0;
spm_buffer_params[0].addr = nullptr;
spm_buffer_params[0].data_loss = false;
spm_buffer_params[1].size = spm_buffer_size;
spm_buffer_params[1].timeout = timeout;
spm_buffer_params[1].len = 0;
spm_buffer_params[1].addr = nullptr;
spm_buffer_params[1].data_loss = false;
spm_buffer_params[2].size = spm_buffer_size;
spm_buffer_params[2].timeout = timeout;
spm_buffer_params[2].len = 0;
spm_buffer_params[2].addr = malloc(spm_buffer_size);
spm_buffer_params[2].data_loss = false;
setSpmDestBuffer(preferredGpuNode_, spm_buffer_params[2].size, &timeout,
&spm_buffer_params[2].len, &spm_buffer_params[2].addr,
&spm_buffer_params[2].data_loss);
currIndex.store(0, std::memory_order_release);
preIndex.store(0, std::memory_order_release);
// thread_buffer_setup = std::thread(spmBufferSetup, preferredGpuNode_);
// thread_spm_data_parse = std::thread(spmDataParse);
spmBufferSetup(preferredGpuNode_);
spmDataParse();
return ROCPROFILER_STATUS_SUCCESS;
} else {
std::cout << "SPM acquire failed\n" << std::endl;
return ROCPROFILER_STATUS_ERROR;
}
#endif
return ROCPROFILER_STATUS_SUCCESS; //delete this line with if 0
}
rocprofiler_status_t spm::SpmCounters::stopSpm() {
Packet::packet_t stop_packet;
// submit the start packet
is_started.exchange(false, std::memory_order_release);
// thread_buffer_setup.join();
// thread_spm_data_parse.join();
stop_packet = *(*profiles_)[0].first->stop_packet;
buffer_read_flag.exchange(false, std::memory_order_release);
stop_packet.header = HSA_PACKET_TYPE_VENDOR_SPECIFIC << HSA_PACKET_HEADER_TYPE;
stop_packet.completion_signal = {};
stop_packet.completion_signal = stop_signal_;
submitPacket(queue_, &stop_packet);
signalWait(stop_packet.completion_signal, 1);
// restore signal to a value of 1
hsa_signal_store_screlease(stop_signal_, 1);
hsa_status_t status = HSA_STATUS_SUCCESS;
if (queue_ != nullptr) {
status = hsa_support::GetCoreApiTable().hsa_queue_destroy_fn(queue_);
queue_ = nullptr;
}
if (status != HSA_STATUS_SUCCESS) rocmtools::warning("Queue destroy failed");
return ROCPROFILER_STATUS_SUCCESS;
}
} // namespace rocmtools
+57
View File
@@ -0,0 +1,57 @@
#ifndef SRC_CORE_SESSION_SPM_H_
#define SRC_CORE_SESSION_SPM_H_
#include <map>
#include <vector>
#include <atomic>
#include <thread>
#include <queue>
#include <mutex>
#include "hsa/hsa_ext_amd.h"
#include "src/core/hsa/packets/packets_generator.h"
#include "src/utils/exception.h"
#include "inc/rocprofiler.h"
namespace rocmtools {
namespace spm {
class SpmCounters {
private:
rocprofiler_buffer_id_t buffer_id_;
rocprofiler_filter_id_t filter_id_;
rocprofiler_spm_parameter_t* spmparameter_;
rocprofiler_session_id_t session_id_;
std::map<uint64_t, hsa_queue_t*> agent_queue_map_;
typedef std::vector<std::pair<profiling_context_t*, hsa_ven_amd_aqlprofile_profile_t*>>
profile_vector_t;
profile_vector_t* profiles_;
hsa_queue_t* queue_;
hsa_agent_t defaultGpuNode_;
hsa_agent_t defaultCpuNode_;
hsa_agent_t preferredGpuNode_;
hsa_signal_t start_signal_;
hsa_signal_t stop_signal_;
public:
SpmCounters(rocprofiler_buffer_id_t buffer_id, rocprofiler_filter_id_t filter_id,
rocprofiler_spm_parameter_t* spmparameter, rocprofiler_session_id_t session_id);
~SpmCounters(){};
rocprofiler_status_t startSpm();
rocprofiler_status_t stopSpm();
}; // class SpmCounters
} // namespace spm
bool find_hsa_agent_cpu(uint64_t index, hsa_agent_t* agent);
bool find_hsa_agent_gpu(uint64_t index, hsa_agent_t* agent);
} // namespace rocmtools
#endif // SRC_CORE_SESSION_SPM_H_
@@ -0,0 +1,99 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#include "correlation_id.h"
#include "roctracer.h"
#include <atomic>
#include <stack>
#include <vector>
namespace {
// A stack that can be used for TLS variables. TLS destructors are invoked before global destructors
// which is a problem if operations invoked by global destructors use TLS variables. If the TLS
// stack is destructed, it still has well defined behavior by always returning a dummy element.
template <typename T> class Stack : std::stack<T, std::vector<T>> {
using parent_type = typename std::stack<T, std::vector<T>>;
public:
Stack() { valid_.store(true, std::memory_order_relaxed); }
~Stack() { valid_.store(false, std::memory_order_relaxed); }
template <class... Args> auto& emplace(Args&&... args) {
return is_valid() ? parent_type::emplace(std::forward<Args>(args)...)
: dummy_element_ = T(std::forward<Args>(args)...);
}
void push(const T& v) {
if (is_valid()) parent_type::push(v);
}
void push(T&& v) {
if (is_valid()) parent_type::push(std::move(v));
}
void pop() {
if (is_valid()) parent_type::pop();
}
const auto& top() const { return is_valid() ? parent_type::top() : dummy_element_; }
auto& top() { return is_valid() ? parent_type::top() : (dummy_element_ = {}); }
bool is_valid() const { return valid_.load(std::memory_order_relaxed); }
size_t size() const { return is_valid() ? parent_type::size() : 0; }
bool empty() const { return size() == 0; }
private:
std::atomic<bool> valid_{false};
T dummy_element_; // Dummy element used when the stack is not valid.
};
thread_local Stack<activity_correlation_id_t> correlation_id_stack{};
thread_local Stack<activity_correlation_id_t> external_id_stack{};
} // namespace
namespace roctracer {
activity_correlation_id_t CorrelationIdPush() {
static std::atomic<uint64_t> counter{1};
return correlation_id_stack.emplace(counter.fetch_add(1, std::memory_order_relaxed));
}
void CorrelationIdPop() { correlation_id_stack.pop(); }
activity_correlation_id_t CorrelationId() {
return correlation_id_stack.empty() ? 0 : correlation_id_stack.top();
}
void ExternalCorrelationIdPush(activity_correlation_id_t external_id) {
external_id_stack.push(external_id);
}
std::optional<activity_correlation_id_t> ExternalCorrelationIdPop() {
if (external_id_stack.empty()) return std::nullopt;
auto external_id = external_id_stack.top();
external_id_stack.pop();
return std::make_optional(external_id);
}
std::optional<activity_correlation_id_t> ExternalCorrelationId() {
return external_id_stack.empty() ? std::nullopt : std::make_optional(external_id_stack.top());
}
} // namespace roctracer
@@ -0,0 +1,50 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#pragma once
#include "roctracer.h"
#include <optional>
namespace roctracer {
// Start a new correlation ID region and push it onto the thread local stack. Correlation ID
// regions are nested and per-thread.
activity_correlation_id_t CorrelationIdPush();
// Stop the current correlation ID region and pop it from the thread local stack.
void CorrelationIdPop();
// Return the ID currently active correlation ID region, or 0 if no regin is active.
activity_correlation_id_t CorrelationId();
// Start a new external correlation ID region for the given \p external_id. As for the internal
// correlation ID regions, external correlation ID regions are nested and per-thread.
void ExternalCorrelationIdPush(activity_correlation_id_t external_id);
// Stop the current external correlation ID region and return the external_id used to start the
// region. Return a nullopt if no region was active.
std::optional<activity_correlation_id_t> ExternalCorrelationIdPop();
// Return the current external correlation ID or nullopt is no region is active.
std::optional<activity_correlation_id_t> ExternalCorrelationId();
} // namespace roctracer
+44
View File
@@ -0,0 +1,44 @@
/* Copyright (c) 2018-2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef EXCEPTION_H_
#define EXCEPTION_H_
#include <sstream>
#include <stdexcept>
#include <string>
#include <sstream>
namespace roctracer {
class ApiError : public std::runtime_error {
public:
explicit ApiError(roctracer_status_t status, const std::string& what_arg)
: std::runtime_error(what_arg), status_(status) {}
roctracer_status_t status() const noexcept { return status_; }
private:
const roctracer_status_t status_;
};
} // namespace roctracer
#endif // EXCEPTION_H_
+194
View File
@@ -0,0 +1,194 @@
/* Copyright (c) 2018-2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef ROCTRACER_LOADER_H_
#define ROCTRACER_LOADER_H_
#include <dlfcn.h>
#include <hip/hip_runtime_api.h>
#include <link.h>
#include <unistd.h>
#include <experimental/filesystem>
#include "src/utils/helper.h"
namespace fs = std::experimental::filesystem;
namespace roctracer {
// Base loader class
template <typename Loader> class BaseLoader {
protected:
BaseLoader(const char* pattern) {
// Iterate through the process' loaded shared objects and try to dlopen the
// first entry with a file name starting with the given 'pattern'. This
// allows the loader to acquire a handle to the target library iff it is
// already loaded. The handle is used to query symbols exported by that
// library.
auto callback = [this, pattern](dl_phdr_info* info) {
if (handle_ == nullptr &&
fs::path(info->dlpi_name).filename().string().rfind(pattern, 0) == 0)
handle_ = ::dlopen(info->dlpi_name, RTLD_LAZY);
};
dl_iterate_phdr(
[](dl_phdr_info* info, size_t size, void* data) {
(*reinterpret_cast<decltype(callback)*>(data))(info);
return 0;
},
&callback);
}
~BaseLoader() {
if (handle_ != nullptr) ::dlclose(handle_);
}
BaseLoader(const BaseLoader&) = delete;
BaseLoader& operator=(const BaseLoader&) = delete;
public:
bool IsEnabled() const { return handle_ != nullptr; }
template <typename FunctionPtr> FunctionPtr GetFun(const char* symbol) const {
assert(IsEnabled());
auto function_ptr = reinterpret_cast<FunctionPtr>(::dlsym(handle_, symbol));
if (function_ptr == nullptr)
rocmtools::fatal("symbol lookup '%s' failed: %s", symbol, ::dlerror());
return function_ptr;
}
static inline Loader& Instance() {
static Loader instance;
return instance;
}
private:
void* handle_;
};
} // namespace roctracer
// HIP runtime library loader class
namespace roctracer {
#if STATIC_BUILD
__attribute__((weak)) const char* hipKernelNameRef(const hipFunction_t f) { return nullptr; }
__attribute__((weak)) const char* hipKernelNameRefByPtr(const void* hostFunction,
hipStream_t stream) {
return nullptr;
}
__attribute__((weak)) int hipGetStreamDeviceId(hipStream_t stream) { return 0; }
__attribute__((weak)) const char* hipGetCmdName(unsigned op) { return nullptr; }
__attribute__((weak)) const char* hipApiName(uint32_t id) { return nullptr; }
__attribute__((weak)) void hipRegisterTracerCallback(int (*function)(activity_domain_t domain,
uint32_t operation_id,
void* data)) {}
class HipLoader {
private:
HipLoader() {}
public:
bool IsEnabled() const { return true; }
int GetStreamDeviceId(hipStream_t stream) const { return hipGetStreamDeviceId(stream); }
const char* KernelNameRef(const hipFunction_t f) const { return hipKernelNameRef(f); }
const char* KernelNameRefByPtr(const void* host_function, hipStream_t stream = nullptr) const {
return hipKernelNameRefByPtr(host_function, stream);
}
const char* GetOpName(unsigned op) const { return hipGetCmdName(op); }
const char* ApiName(uint32_t id) const { return hipApiName(id); }
void RegisterTracerCallback(int (*callback)(activity_domain_t domain, uint32_t operation_id,
void* data)) const {
return hipRegisterTracerCallback(callback);
}
static inline HipLoader& Instance() {
static HipLoader instance;
return instance;
}
};
#else
class HipLoader : public BaseLoader<HipLoader> {
private:
friend HipLoader& BaseLoader::Instance();
HipLoader() : BaseLoader("libamdhip64.so") {}
public:
int GetStreamDeviceId(hipStream_t stream) const {
static auto function = GetFun<int (*)(hipStream_t stream)>("hipGetStreamDeviceId");
return function(stream);
}
const char* KernelNameRef(const hipFunction_t f) const {
static auto function = GetFun<const char* (*)(const hipFunction_t f)>("hipKernelNameRef");
return function(f);
}
const char* KernelNameRefByPtr(const void* host_function, hipStream_t stream = nullptr) const {
static auto function = GetFun<const char* (*)(const void* hostFunction, hipStream_t stream)>(
"hipKernelNameRefByPtr");
return function(host_function, stream);
}
const char* GetOpName(unsigned op) const {
static auto function = GetFun<const char* (*)(unsigned op)>("hipGetCmdName");
return function(op);
}
const char* ApiName(uint32_t id) const {
static auto function = GetFun<const char* (*)(uint32_t id)>("hipApiName");
return function(id);
}
void RegisterTracerCallback(int (*callback)(activity_domain_t domain, uint32_t operation_id,
void* data)) const {
static auto function = GetFun<void (*)(int (*callback)(
activity_domain_t domain, uint32_t operation_id, void* data))>("hipRegisterTracerCallback");
return function(callback);
}
};
#endif
// ROCTX library loader class
class RocTxLoader : public BaseLoader<RocTxLoader> {
private:
friend RocTxLoader& BaseLoader::Instance();
RocTxLoader() : BaseLoader("libroctx64.so") {}
public:
void RegisterTracerCallback(int (*callback)(activity_domain_t domain, uint32_t operation_id,
void* data)) const {
static auto function =
GetFun<void (*)(int (*callback)(activity_domain_t domain, uint32_t operation_id,
void* data))>("roctxRegisterTracerCallback");
return function(callback);
}
};
} // namespace roctracer
#endif // ROCTRACER_LOADER_H_
@@ -0,0 +1,101 @@
/* Copyright (c) 2018-2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef UTIL_CALLBACK_TABLE_H_
#define UTIL_CALLBACK_TABLE_H_
#include <array>
#include <atomic>
#include <cassert>
#include <optional>
#include <shared_mutex>
#include <utility>
#include "roctracer.h"
namespace roctracer::util {
#if __GNUC__ == 11 || __GNUCC__ == 12
// Starting with gcc-11 (verified with gcc-12 as well), an array out-of-bounds
// subscript error is reported for accessing the registration table element at
// the operation ID index. Validating the index in the function calling
// Register/Unregister does not quiet the warning/error in release builds, so,
// for gcc-11 and gcc-12, we disable that warning just for this class.
#define IGNORE_GCC_ARRAY_BOUNDS_ERROR 1
#endif // __GNUC__ == 11 || __GNUCC__ == 12
#if IGNORE_GCC_ARRAY_BOUNDS_ERROR
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warray-bounds"
#endif // IGNORE_GCC_ARRAY_BOUNDS_ERROR
namespace detail {
struct False {
constexpr bool operator()() { return false; }
};
} // namespace detail
// Generic callbacks table
template <typename T, uint32_t N, typename IsStopped = detail::False> class RegistrationTable {
public:
template <typename... Args> void Register(uint32_t operation_id, Args... args) {
assert(operation_id < N && "operation_id is out of range");
auto& entry = table_[operation_id];
std::unique_lock lock(entry.mutex);
if (!entry.enabled.exchange(true, std::memory_order_relaxed))
registered_count_.fetch_add(1, std::memory_order_relaxed);
entry.data = T{std::forward<Args>(args)...};
}
void Unregister(uint32_t operation_id) {
assert(operation_id < N && "id is out of range");
auto& entry = table_[operation_id];
std::unique_lock lock(entry.mutex);
if (entry.enabled.exchange(false, std::memory_order_relaxed))
registered_count_.fetch_sub(1, std::memory_order_relaxed);
}
std::optional<T> Get(uint32_t operation_id) const {
assert(operation_id < N && "id is out of range");
auto& entry = table_[operation_id];
if (!entry.enabled.load(std::memory_order_relaxed) || IsStopped{}()) return std::nullopt;
std::shared_lock lock(entry.mutex);
return entry.enabled.load(std::memory_order_relaxed) ? std::make_optional(entry.data)
: std::nullopt;
}
bool IsEmpty() const { return registered_count_.load(std::memory_order_relaxed) == 0; }
private:
std::atomic<size_t> registered_count_{0};
struct {
std::atomic<bool> enabled{false};
mutable std::shared_mutex mutex;
T data;
} table_[N]{};
};
#if IGNORE_GCC_ARRAY_BOUNDS_ERROR
#pragma GCC diagnostic pop
#endif // IGNORE_GCC_ARRAY_BOUNDS_ERROR
} // namespace roctracer::util
#endif // UTIL_CALLBACK_TABLE_H_
+847
View File
@@ -0,0 +1,847 @@
/* Copyright (c) 2018-2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#include "roctracer.h"
#include <assert.h>
#include <dirent.h>
#include <hsa/hsa_api_trace.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <atomic>
#include <mutex>
#include <stack>
#include <type_traits>
#include <unordered_map>
#include <variant>
#include <vector>
#include "correlation_id.h"
#include "exception.h"
#include "loader.h"
#include "registration_table.h"
#include "src/core/hsa/hsa_support.h"
#include "src/utils/helper.h"
#include "src/api/rocmtool.h"
static inline uint32_t GetPid() {
static auto pid = syscall(__NR_getpid);
return pid;
}
static inline uint32_t GetTid() {
static thread_local auto tid = syscall(__NR_gettid);
return tid;
}
using namespace roctracer;
namespace {
session_buffer_id_t session_buffer_id{};
roctracer_start_cb_t roctracer_start_cb = nullptr;
roctracer_stop_cb_t roctracer_stop_cb = nullptr;
std::mutex registration_mutex;
// Memory pool routines and primitives
std::recursive_mutex memory_pool_mutex;
} // namespace
// Return Op code and kind by given string
void roctracer_op_code(uint32_t domain, const char* str, uint32_t* op, uint32_t* kind) {
switch (domain) {
case ACTIVITY_DOMAIN_HSA_API: {
*op = hsa_support::GetApiCode(str);
if (*op == HSA_API_ID_NUMBER) {
EXC_RAISING(ROCTRACER_STATUS_ERROR_INVALID_ARGUMENT,
"Invalid API name \"" << str << "\", domain ID(" << domain << ")");
}
if (kind != nullptr) *kind = 0;
break;
}
case ACTIVITY_DOMAIN_HIP_API: {
*op = hipApiIdByName(str);
if (*op == HIP_API_ID_NONE) {
EXC_RAISING(ROCTRACER_STATUS_ERROR_INVALID_ARGUMENT,
"Invalid API name \"" << str << "\", domain ID(" << domain << ")");
}
if (kind != nullptr) *kind = 0;
break;
}
default:
EXC_RAISING(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "limited domain ID(" << domain << ")");
}
}
// Return Op string by given domain and activity/API codes
// nullptr returned on the error and the library errno is set
const char* roctracer_op_string(uint32_t domain, uint32_t op) {
switch (domain) {
case ACTIVITY_DOMAIN_HSA_API:
return hsa_support::GetApiName(op);
case ACTIVITY_DOMAIN_HSA_EVT:
return hsa_support::GetEvtName(op);
case ACTIVITY_DOMAIN_HSA_OPS:
return hsa_support::GetOpsName(op);
case ACTIVITY_DOMAIN_HIP_OPS:
return HipLoader::Instance().GetOpName(op);
case ACTIVITY_DOMAIN_HIP_API:
return HipLoader::Instance().ApiName(op);
case ACTIVITY_DOMAIN_EXT_API:
return "EXT_API";
default:
throw roctracer::ApiError(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "invalid domain ID");
}
}
namespace {
template <activity_domain_t> struct DomainTraits;
template <> struct DomainTraits<ACTIVITY_DOMAIN_HIP_API> {
using ApiData = hip_api_data_t;
using OperationId = hip_api_id_t;
static constexpr size_t kOpIdBegin = HIP_API_ID_FIRST;
static constexpr size_t kOpIdEnd = HIP_API_ID_LAST + 1;
};
template <> struct DomainTraits<ACTIVITY_DOMAIN_HSA_API> {
using ApiData = hsa_api_data_t;
using OperationId = hsa_api_id_t;
static constexpr size_t kOpIdBegin = 0;
static constexpr size_t kOpIdEnd = HSA_API_ID_NUMBER;
};
template <> struct DomainTraits<ACTIVITY_DOMAIN_ROCTX> {
using ApiData = roctx_api_data_t;
using OperationId = roctx_api_id_t;
static constexpr size_t kOpIdBegin = 0;
static constexpr size_t kOpIdEnd = ROCTX_API_ID_NUMBER;
};
template <> struct DomainTraits<ACTIVITY_DOMAIN_HIP_OPS> {
using OperationId = hip_op_id_t;
static constexpr size_t kOpIdBegin = 0;
static constexpr size_t kOpIdEnd = HIP_OP_ID_NUMBER;
};
template <> struct DomainTraits<ACTIVITY_DOMAIN_HSA_OPS> {
using OperationId = hsa_op_id_t;
static constexpr size_t kOpIdBegin = 0;
static constexpr size_t kOpIdEnd = HSA_OP_ID_NUMBER;
};
template <> struct DomainTraits<ACTIVITY_DOMAIN_HSA_EVT> {
using ApiData = hsa_evt_data_t;
using OperationId = hsa_evt_id_t;
static constexpr size_t kOpIdBegin = 0;
static constexpr size_t kOpIdEnd = HSA_EVT_ID_NUMBER;
};
constexpr uint32_t get_op_begin(activity_domain_t domain) {
switch (domain) {
case ACTIVITY_DOMAIN_HSA_OPS:
return DomainTraits<ACTIVITY_DOMAIN_HSA_OPS>::kOpIdBegin;
case ACTIVITY_DOMAIN_HSA_API:
return DomainTraits<ACTIVITY_DOMAIN_HSA_API>::kOpIdBegin;
case ACTIVITY_DOMAIN_HSA_EVT:
return DomainTraits<ACTIVITY_DOMAIN_HSA_EVT>::kOpIdBegin;
case ACTIVITY_DOMAIN_HIP_OPS:
return DomainTraits<ACTIVITY_DOMAIN_HIP_OPS>::kOpIdBegin;
case ACTIVITY_DOMAIN_HIP_API:
return DomainTraits<ACTIVITY_DOMAIN_HIP_API>::kOpIdBegin;
case ACTIVITY_DOMAIN_ROCTX:
return DomainTraits<ACTIVITY_DOMAIN_ROCTX>::kOpIdBegin;
case ACTIVITY_DOMAIN_EXT_API:
return 0;
default:
throw roctracer::ApiError(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "invalid domain ID");
}
}
constexpr uint32_t get_op_end(activity_domain_t domain) {
switch (domain) {
case ACTIVITY_DOMAIN_HSA_OPS:
return DomainTraits<ACTIVITY_DOMAIN_HSA_OPS>::kOpIdEnd;
case ACTIVITY_DOMAIN_HSA_API:
return DomainTraits<ACTIVITY_DOMAIN_HSA_API>::kOpIdEnd;
case ACTIVITY_DOMAIN_HSA_EVT:
return DomainTraits<ACTIVITY_DOMAIN_HSA_EVT>::kOpIdEnd;
case ACTIVITY_DOMAIN_HIP_OPS:
return DomainTraits<ACTIVITY_DOMAIN_HIP_OPS>::kOpIdEnd;
case ACTIVITY_DOMAIN_HIP_API:
return DomainTraits<ACTIVITY_DOMAIN_HIP_API>::kOpIdEnd;
case ACTIVITY_DOMAIN_ROCTX:
return DomainTraits<ACTIVITY_DOMAIN_ROCTX>::kOpIdEnd;
case ACTIVITY_DOMAIN_EXT_API:
return get_op_begin(ACTIVITY_DOMAIN_EXT_API);
default:
throw roctracer::ApiError(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "invalid domain ID");
}
}
std::atomic<bool> stopped_status{false};
struct IsStopped {
bool operator()() const { return stopped_status.load(std::memory_order_relaxed); }
};
struct NeverStopped {
constexpr bool operator()() { return false; }
};
using UserCallback = std::pair<activity_rtapi_callback_t, void*>;
template <activity_domain_t domain, typename IsStopped>
using CallbackRegistrationTable =
util::RegistrationTable<UserCallback, DomainTraits<domain>::kOpIdEnd, IsStopped>;
template <activity_domain_t domain, typename IsStopped>
using ActivityRegistrationTable =
util::RegistrationTable<roctracer_pool_t*, DomainTraits<domain>::kOpIdEnd, IsStopped>;
template <activity_domain_t domain> struct ApiTracer {
using ApiData = typename DomainTraits<domain>::ApiData;
using OperationId = typename DomainTraits<domain>::OperationId;
struct TraceData {
ApiData api_data; // API specific data (for example, function arguments).
uint64_t phase_enter_timestamp; // timestamp when phase_enter was executed.
uint64_t phase_data; // data that can be shared between phase_enter and
// phase_exit.
void (*phase_enter)(OperationId operation_id, TraceData* data);
void (*phase_exit)(OperationId operation_id, TraceData* data);
};
static void Exit(OperationId operation_id, TraceData* trace_data) {
uint64_t record_id = 0;
if (rocmtools::GetROCMToolObj()) {
record_id = rocmtools::GetROCMToolObj()->GetUniqueRecordId();
if (auto pool = activity_table.Get(operation_id)) {
if (rocmtools::GetROCMToolObj() &&
rocmtools::GetROCMToolObj()->GetSession((*pool)->session_id) &&
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)) {
if (rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->IsValid()) {
std::lock_guard<std::mutex> lock(rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->GetBufferLock());
assert(trace_data != nullptr);
rocprofiler_record_tracer_t record{};
record.header = rocprofiler_record_header_t{ROCPROFILER_TRACER_RECORD,
rocprofiler_record_id_t{record_id}};
record.domain = domain;
record.operation_id = rocprofiler_tracer_operation_id_t{operation_id};
record.correlation_id =
rocprofiler_tracer_activity_correlation_id_t{trace_data->api_data.correlation_id};
record.timestamps = rocprofiler_record_header_timestamp_t{
rocprofiler_timestamp_t{trace_data->phase_enter_timestamp},
hsa_support::timestamp_ns()};
record.thread_id = rocprofiler_thread_id_t{GetTid()};
if (auto external_id = ExternalCorrelationId()) {
rocprofiler_record_tracer_t ext_record{};
ext_record.header = rocprofiler_record_header_t{ROCPROFILER_TRACER_RECORD,
rocprofiler_record_id_t{record_id}};
ext_record.domain = ACTIVITY_DOMAIN_EXT_API;
ext_record.operation_id = rocprofiler_tracer_operation_id_t{ACTIVITY_EXT_OP_EXTERN_ID};
ext_record.correlation_id =
rocprofiler_tracer_activity_correlation_id_t{record.correlation_id};
ext_record.external_id = rocprofiler_tracer_external_id_t{*external_id};
// Write the external correlation id record directly followed by the
// activity record.
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->AddRecord(std::array<rocprofiler_record_tracer_t, 2>{ext_record, record});
} else {
// Write record to the buffer.
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->AddRecord(record);
}
}
}
}
}
CorrelationIdPop();
}
static void Exit_UserCallback(OperationId operation_id, TraceData* trace_data) {
if (auto user_callback = callback_table.Get(operation_id)) {
assert(trace_data != nullptr);
trace_data->api_data.phase = ACTIVITY_API_PHASE_EXIT;
user_callback->first(domain, operation_id, &trace_data->api_data, user_callback->second);
}
Exit(operation_id, trace_data);
}
static void Enter_UserCallback(OperationId operation_id, TraceData* trace_data) {
if (auto user_callback = callback_table.Get(operation_id)) {
assert(trace_data != nullptr);
trace_data->api_data.phase = ACTIVITY_API_PHASE_ENTER;
trace_data->api_data.phase_data = &trace_data->phase_data;
user_callback->first(domain, operation_id, &trace_data->api_data, user_callback->second);
trace_data->phase_exit = Exit_UserCallback;
} else {
trace_data->phase_exit = Exit;
}
}
static int Enter(OperationId operation_id, TraceData* trace_data) {
bool callback_enabled = callback_table.Get(operation_id).has_value(),
activity_enabled = activity_table.Get(operation_id).has_value();
if (!callback_enabled && !activity_enabled) return -1;
if (trace_data != nullptr) {
// Generate a new correlation ID.
trace_data->api_data.correlation_id = CorrelationIdPush();
if (activity_enabled) {
trace_data->phase_enter_timestamp = hsa_support::timestamp_ns().value;
trace_data->phase_enter = nullptr;
trace_data->phase_exit = Exit;
}
if (callback_enabled) {
trace_data->phase_enter = Enter_UserCallback;
trace_data->phase_exit = [](OperationId, TraceData*) {
rocmtools::fatal("should not reach here");
};
}
}
return 0;
}
static CallbackRegistrationTable<domain, IsStopped> callback_table;
static ActivityRegistrationTable<domain, IsStopped> activity_table;
};
template <activity_domain_t domain>
CallbackRegistrationTable<domain, IsStopped> ApiTracer<domain>::callback_table;
template <activity_domain_t domain>
ActivityRegistrationTable<domain, IsStopped> ApiTracer<domain>::activity_table;
using HIP_ApiTracer = ApiTracer<ACTIVITY_DOMAIN_HIP_API>;
using HSA_ApiTracer = ApiTracer<ACTIVITY_DOMAIN_HSA_API>;
CallbackRegistrationTable<ACTIVITY_DOMAIN_ROCTX, NeverStopped> roctx_api_callback_table;
ActivityRegistrationTable<ACTIVITY_DOMAIN_HIP_OPS, IsStopped> hip_ops_activity_table;
ActivityRegistrationTable<ACTIVITY_DOMAIN_HSA_OPS, IsStopped> hsa_ops_activity_table;
CallbackRegistrationTable<ACTIVITY_DOMAIN_HSA_EVT, IsStopped> hsa_evt_callback_table;
int TracerCallback(activity_domain_t domain, uint32_t operation_id, void* data) {
switch (domain) {
case ACTIVITY_DOMAIN_HSA_API:
return HSA_ApiTracer::Enter(static_cast<HSA_ApiTracer::OperationId>(operation_id),
static_cast<HSA_ApiTracer::TraceData*>(data));
case ACTIVITY_DOMAIN_HIP_API:
return HIP_ApiTracer::Enter(static_cast<HIP_ApiTracer::OperationId>(operation_id),
static_cast<HIP_ApiTracer::TraceData*>(data));
case ACTIVITY_DOMAIN_HIP_OPS:
if (auto pool = hip_ops_activity_table.Get(operation_id)) {
if (auto record = static_cast<activity_record_t*>(data)) {
// If the record is for a kernel dispatch, write the kernel name in the pool's data,
// and make the record point to it. Older HIP runtimes do not provide a kernel name,
// so record.kernel_name might be null.
uint64_t record_id = 0;
if (!rocmtools::GetROCMToolObj()) return 0;
if (rocmtools::GetROCMToolObj() &&
rocmtools::GetROCMToolObj()->GetSession((*pool)->session_id) &&
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)) {
std::lock_guard<std::mutex> lock(rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->GetBufferLock());
record_id = rocmtools::GetROCMToolObj()->GetUniqueRecordId();
rocprofiler_record_tracer_t rocprofiler_record{};
rocprofiler_record.header = rocprofiler_record_header_t{ROCPROFILER_TRACER_RECORD,
rocprofiler_record_id_t{record_id}};
rocprofiler_record.domain = domain;
rocprofiler_record.external_id = rocprofiler_tracer_external_id_t{};
rocprofiler_record.operation_id = rocprofiler_tracer_operation_id_t{record->kind};
rocprofiler_record.api_data_handle = rocprofiler_tracer_api_data_handle_t{};
rocprofiler_record.correlation_id =
rocprofiler_tracer_activity_correlation_id_t{record->correlation_id};
rocprofiler_record.timestamps = rocprofiler_record_header_timestamp_t{
rocprofiler_timestamp_t{record->begin_ns}, rocprofiler_timestamp_t{record->end_ns}};
rocprofiler_record.agent_id = rocprofiler_agent_id_t{(uint64_t)record->device_id};
rocprofiler_record.queue_id = rocprofiler_queue_id_t{record->queue_id};
rocprofiler_record.thread_id = rocprofiler_thread_id_t{GetTid()};
if (operation_id == HIP_OP_ID_DISPATCH && record->kernel_name != nullptr) {
rocprofiler_record.api_data_handle.handle = strdup(record->kernel_name);
rocprofiler_record.api_data_handle.size = (strlen(record->kernel_name) + 1);
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->AddRecord(rocprofiler_record, rocprofiler_record.api_data_handle.handle,
rocprofiler_record.api_data_handle.size,
[](auto& rocprofiler_record, const void* data) {
rocprofiler_record.api_data_handle.handle =
static_cast<const char*>(data);
});
} else {
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->AddRecord(rocprofiler_record);
}
}
}
return 0;
}
break;
case ACTIVITY_DOMAIN_ROCTX:
if (auto user_callback = roctx_api_callback_table.Get(operation_id)) {
if (auto api_data = static_cast<DomainTraits<ACTIVITY_DOMAIN_ROCTX>::ApiData*>(data))
user_callback->first(ACTIVITY_DOMAIN_ROCTX, operation_id, api_data,
user_callback->second);
return 0;
}
break;
case ACTIVITY_DOMAIN_HSA_OPS:
if (auto pool = hsa_ops_activity_table.Get(operation_id)) {
if (auto record = static_cast<activity_record_t*>(data)) {
uint64_t record_id = 0;
if (!rocmtools::GetROCMToolObj()) return 0;
if (rocmtools::GetROCMToolObj() &&
rocmtools::GetROCMToolObj()->GetSession((*pool)->session_id) &&
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)) {
std::lock_guard<std::mutex> lock(rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->GetBufferLock());
record_id = rocmtools::GetROCMToolObj()->GetUniqueRecordId();
rocprofiler_record_tracer_t rocprofiler_record{};
rocprofiler_record.header = rocprofiler_record_header_t{ROCPROFILER_TRACER_RECORD,
rocprofiler_record_id_t{record_id}};
rocprofiler_record.domain = domain;
rocprofiler_record.external_id = rocprofiler_tracer_external_id_t{0};
rocprofiler_record.operation_id = rocprofiler_tracer_operation_id_t{record->op};
rocprofiler_record.api_data_handle = rocprofiler_tracer_api_data_handle_t{};
rocprofiler_record.correlation_id =
rocprofiler_tracer_activity_correlation_id_t{record->correlation_id};
rocprofiler_record.timestamps = rocprofiler_record_header_timestamp_t{
rocprofiler_timestamp_t{record->begin_ns}, rocprofiler_timestamp_t{record->end_ns}};
rocprofiler_record.agent_id = rocprofiler_agent_id_t{(uint64_t)record->device_id};
rocprofiler_record.queue_id = rocprofiler_queue_id_t{record->queue_id};
rocprofiler_record.thread_id = rocprofiler_thread_id_t{GetTid()};
if (record->kernel_name != nullptr && record->op == HSA_OP_ID_DISPATCH) {
rocprofiler_record.api_data_handle.handle = strdup(record->kernel_name);
rocprofiler_record.api_data_handle.size = strlen(record->kernel_name) + 1;
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->AddRecord(rocprofiler_record, rocprofiler_record.api_data_handle.handle,
rocprofiler_record.api_data_handle.size,
[](auto& rocprofiler_record, const void* data) {
rocprofiler_record.api_data_handle.handle =
static_cast<const char*>(data);
});
} else {
rocmtools::GetROCMToolObj()
->GetSession((*pool)->session_id)
->GetBuffer((*pool)->buffer_id)
->AddRecord(rocprofiler_record);
}
}
}
return 0;
}
break;
case ACTIVITY_DOMAIN_HSA_EVT:
if (auto user_callback = hsa_evt_callback_table.Get(operation_id)) {
if (auto api_data = static_cast<DomainTraits<ACTIVITY_DOMAIN_HSA_EVT>::ApiData*>(data))
user_callback->first(ACTIVITY_DOMAIN_HSA_EVT, operation_id, api_data,
user_callback->second);
return 0;
}
break;
default:
break;
} // namespace
return -1;
}
template <typename... Tables> struct RegistrationTableGroup {
private:
bool AllEmpty() const {
return std::apply([](auto&&... tables) { return (tables.IsEmpty() && ...); }, tables_);
}
public:
template <typename Functor1, typename Functor2>
RegistrationTableGroup(Functor1&& engage_tracer, Functor2&& disengage_tracer, Tables&... tables)
: engage_tracer_(std::forward<Functor1>(engage_tracer)),
disengage_tracer_(std::forward<Functor2>(disengage_tracer)),
tables_(tables...) {}
template <typename T, typename... Args>
void Register(T& table, uint32_t operation_id, Args... args) const {
if (AllEmpty()) engage_tracer_();
table.Register(operation_id, std::forward<Args>(args)...);
}
template <typename T> void Unregister(T& table, uint32_t operation_id) const {
table.Unregister(operation_id);
if (AllEmpty()) disengage_tracer_();
}
private:
const std::function<void()> engage_tracer_, disengage_tracer_;
const std::tuple<const Tables&...> tables_;
};
RegistrationTableGroup HSA_registration_group(
[]() { hsa_support::RegisterTracerCallback(TracerCallback); },
[]() { hsa_support::RegisterTracerCallback(nullptr); }, HSA_ApiTracer::callback_table,
HSA_ApiTracer::activity_table, hsa_ops_activity_table, hsa_evt_callback_table);
RegistrationTableGroup HIP_registration_group(
[]() { HipLoader::Instance().RegisterTracerCallback(TracerCallback); },
[]() { HipLoader::Instance().RegisterTracerCallback(nullptr); }, HIP_ApiTracer::callback_table,
HIP_ApiTracer::activity_table, hip_ops_activity_table);
RegistrationTableGroup ROCTX_registration_group(
[]() { RocTxLoader::Instance().RegisterTracerCallback(TracerCallback); },
[]() { RocTxLoader::Instance().RegisterTracerCallback(nullptr); }, roctx_api_callback_table);
} // namespace
// Enable runtime API callbacks
static void roctracer_enable_op_callback(activity_domain_t domain, uint32_t operation_id,
roctracer_rtapi_callback_t callback, void* user_data) {
std::lock_guard lock(registration_mutex);
if (operation_id >= get_op_end(domain) || callback == nullptr)
throw ApiError(ROCTRACER_STATUS_ERROR_INVALID_ARGUMENT, "invalid argument");
switch (domain) {
case ACTIVITY_DOMAIN_HSA_EVT:
HSA_registration_group.Register(hsa_evt_callback_table, operation_id, callback, user_data);
break;
case ACTIVITY_DOMAIN_HSA_API:
HSA_registration_group.Register(HSA_ApiTracer::callback_table, operation_id, callback,
user_data);
break;
case ACTIVITY_DOMAIN_HSA_OPS:
break;
case ACTIVITY_DOMAIN_HIP_API:
if (HipLoader::Instance().IsEnabled())
HIP_registration_group.Register(HIP_ApiTracer::callback_table, operation_id, callback,
user_data);
break;
case ACTIVITY_DOMAIN_HIP_OPS:
break;
case ACTIVITY_DOMAIN_ROCTX:
if (RocTxLoader::Instance().IsEnabled())
ROCTX_registration_group.Register(roctx_api_callback_table, operation_id, callback,
user_data);
break;
default:
EXC_RAISING(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "invalid domain ID(" << domain << ")");
}
}
void roctracer_enable_domain_callback(activity_domain_t domain, roctracer_rtapi_callback_t callback,
void* user_data) {
const uint32_t op_end = get_op_end(domain);
for (uint32_t op = get_op_begin(domain); op < op_end; ++op)
roctracer_enable_op_callback(domain, op, callback, user_data);
}
// Disable runtime API callbacks
void roctracer_disable_op_callback(activity_domain_t domain, uint32_t operation_id) {
std::lock_guard lock(registration_mutex);
if (operation_id >= get_op_end(domain))
throw ApiError(ROCTRACER_STATUS_ERROR_INVALID_ARGUMENT, "invalid argument");
switch (domain) {
case ACTIVITY_DOMAIN_HSA_EVT:
HSA_registration_group.Unregister(hsa_evt_callback_table, operation_id);
break;
case ACTIVITY_DOMAIN_HSA_API:
HSA_registration_group.Unregister(HSA_ApiTracer::callback_table, operation_id);
break;
case ACTIVITY_DOMAIN_HSA_OPS:
break;
case ACTIVITY_DOMAIN_HIP_API:
if (HipLoader::Instance().IsEnabled())
HIP_registration_group.Unregister(HIP_ApiTracer::callback_table, operation_id);
break;
case ACTIVITY_DOMAIN_HIP_OPS:
break;
case ACTIVITY_DOMAIN_ROCTX:
if (RocTxLoader::Instance().IsEnabled())
ROCTX_registration_group.Unregister(roctx_api_callback_table, operation_id);
break;
default:
EXC_RAISING(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "invalid domain ID(" << domain << ")");
}
}
void roctracer_disable_domain_callback(activity_domain_t domain) {
const uint32_t op_end = get_op_end(domain);
for (uint32_t op = get_op_begin(domain); op < op_end; ++op)
roctracer_disable_op_callback(domain, op);
}
// Enable activity records logging
void roctracer_enable_op_activity(activity_domain_t domain, uint32_t op,
roctracer_pool_t memory_pool) {
std::lock_guard lock(registration_mutex);
if (memory_pool.session_id.handle > 0) {
session_buffer_id.buffer_id = memory_pool.buffer_id;
session_buffer_id.session_id = memory_pool.session_id;
}
if (op >= get_op_end(domain))
throw ApiError(ROCTRACER_STATUS_ERROR_INVALID_ARGUMENT, "invalid argument");
switch (domain) {
case ACTIVITY_DOMAIN_HSA_EVT:
break;
case ACTIVITY_DOMAIN_HSA_API:
HSA_registration_group.Register(HSA_ApiTracer::activity_table, op, &session_buffer_id);
break;
case ACTIVITY_DOMAIN_HSA_OPS:
HSA_registration_group.Register(hsa_ops_activity_table, op, &session_buffer_id);
break;
case ACTIVITY_DOMAIN_HIP_API:
if (HipLoader::Instance().IsEnabled())
HIP_registration_group.Register(HIP_ApiTracer::activity_table, op, &session_buffer_id);
break;
case ACTIVITY_DOMAIN_HIP_OPS:
if (HipLoader::Instance().IsEnabled())
HIP_registration_group.Register(hip_ops_activity_table, op, &session_buffer_id);
break;
case ACTIVITY_DOMAIN_ROCTX:
break;
default:
EXC_RAISING(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "invalid domain ID(" << domain << ")");
}
}
void roctracer_enable_domain_activity(activity_domain_t domain, roctracer_pool_t pool) {
const uint32_t op_end = get_op_end(domain);
for (uint32_t op = get_op_begin(domain); op < op_end; ++op) {
try {
roctracer_enable_op_activity(domain, op, pool);
} catch (const ApiError& err) {
if (err.status() != ROCTRACER_STATUS_ERROR_NOT_IMPLEMENTED) throw;
}
}
}
// Disable activity records logging
void roctracer_disable_activity(activity_domain_t domain, uint32_t op) {
std::lock_guard lock(registration_mutex);
if (op >= get_op_end(domain))
throw ApiError(ROCTRACER_STATUS_ERROR_INVALID_ARGUMENT, "invalid argument");
switch (domain) {
case ACTIVITY_DOMAIN_HSA_EVT:
break;
case ACTIVITY_DOMAIN_HSA_API:
HSA_registration_group.Unregister(HSA_ApiTracer::activity_table, op);
break;
case ACTIVITY_DOMAIN_HSA_OPS:
HSA_registration_group.Unregister(hsa_ops_activity_table, op);
break;
case ACTIVITY_DOMAIN_HIP_API:
if (HipLoader::Instance().IsEnabled())
HIP_registration_group.Unregister(HIP_ApiTracer::activity_table, op);
break;
case ACTIVITY_DOMAIN_HIP_OPS:
if (HipLoader::Instance().IsEnabled())
HIP_registration_group.Unregister(hip_ops_activity_table, op);
break;
case ACTIVITY_DOMAIN_ROCTX:
break;
default:
EXC_RAISING(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "invalid domain ID(" << domain << ")");
}
}
void roctracer_disable_domain_activity(activity_domain_t domain) {
const uint32_t op_end = get_op_end(domain);
for (uint32_t op = get_op_begin(domain); op < op_end; ++op) try {
roctracer_disable_activity(domain, op);
} catch (const ApiError& err) {
if (err.status() != ROCTRACER_STATUS_ERROR_NOT_IMPLEMENTED) throw;
}
}
// Notifies that the calling thread is entering an external API region.
// Push an external correlation id for the calling thread.
void roctracer_activity_push_external_correlation_id(activity_correlation_id_t id) {
ExternalCorrelationIdPush(id);
}
// Notifies that the calling thread is leaving an external API region.
// Pop an external correlation id for the calling thread, and return it in
// 'last_id' if not null.
void roctracer_activity_pop_external_correlation_id(activity_correlation_id_t* last_id) {
auto external_id = ExternalCorrelationIdPop();
if (!external_id) {
if (last_id != nullptr) *last_id = 0;
EXC_RAISING(ROCTRACER_STATUS_ERROR_MISMATCHED_EXTERNAL_CORRELATION_ID,
"unbalanced external correlation id pop");
}
if (last_id != nullptr) *last_id = *external_id;
}
// Start API
void roctracer_start() {
if (stopped_status.exchange(false, std::memory_order_relaxed) && roctracer_start_cb)
roctracer_start_cb();
}
// Stop API
void roctracer_stop() {
if (!stopped_status.exchange(true, std::memory_order_relaxed) && roctracer_stop_cb)
roctracer_stop_cb();
}
// Set properties
void roctracer_set_properties(activity_domain_t domain, void* properties) {
switch (domain) {
case ACTIVITY_DOMAIN_HSA_OPS:
case ACTIVITY_DOMAIN_HSA_EVT:
case ACTIVITY_DOMAIN_HSA_API:
case ACTIVITY_DOMAIN_HIP_OPS:
case ACTIVITY_DOMAIN_HIP_API: {
break;
}
case ACTIVITY_DOMAIN_EXT_API: {
roctracer_ext_properties_t* ops_properties =
reinterpret_cast<roctracer_ext_properties_t*>(properties);
roctracer_start_cb = ops_properties->start_cb;
roctracer_stop_cb = ops_properties->stop_cb;
break;
}
default:
EXC_RAISING(ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID, "invalid domain ID(" << domain << ")");
}
}
static std::string getKernelNameMultiKernelMultiDevice(hipLaunchParams* launchParamsList,
int numDevices) {
std::stringstream name_str;
for (int i = 0; i < numDevices; ++i) {
if (launchParamsList[i].func != nullptr) {
name_str << HipLoader::Instance().KernelNameRefByPtr(launchParamsList[i].func) << ":"
<< HipLoader::Instance().GetStreamDeviceId(launchParamsList[i].stream) << ";";
}
}
return name_str.str();
}
template <typename... Ts> struct Overloaded : Ts... { using Ts::operator()...; };
template <class... Ts> Overloaded(Ts...) -> Overloaded<Ts...>;
std::optional<std::string> GetHipKernelName(uint32_t cid, hip_api_data_t* data) {
std::variant<const void*, hipFunction_t> function;
switch (cid) {
case HIP_API_ID_hipExtLaunchMultiKernelMultiDevice: {
return getKernelNameMultiKernelMultiDevice(
data->args.hipExtLaunchMultiKernelMultiDevice.launchParamsList,
data->args.hipExtLaunchMultiKernelMultiDevice.numDevices);
}
case HIP_API_ID_hipLaunchCooperativeKernelMultiDevice: {
return getKernelNameMultiKernelMultiDevice(
data->args.hipLaunchCooperativeKernelMultiDevice.launchParamsList,
data->args.hipLaunchCooperativeKernelMultiDevice.numDevices);
}
case HIP_API_ID_hipLaunchKernel: {
function = data->args.hipLaunchKernel.function_address;
break;
}
case HIP_API_ID_hipExtLaunchKernel: {
function = data->args.hipExtLaunchKernel.function_address;
break;
}
case HIP_API_ID_hipLaunchCooperativeKernel: {
function = data->args.hipLaunchCooperativeKernel.f;
break;
}
case HIP_API_ID_hipLaunchByPtr: {
function = data->args.hipLaunchByPtr.hostFunction;
break;
}
case HIP_API_ID_hipGraphAddKernelNode: {
function = data->args.hipGraphAddKernelNode.pNodeParams->func;
break;
}
case HIP_API_ID_hipGraphExecKernelNodeSetParams: {
function = data->args.hipGraphExecKernelNodeSetParams.pNodeParams->func;
break;
}
case HIP_API_ID_hipGraphKernelNodeSetParams: {
function = data->args.hipGraphKernelNodeSetParams.pNodeParams->func;
break;
}
case HIP_API_ID_hipModuleLaunchKernel: {
function = data->args.hipModuleLaunchKernel.f;
break;
}
case HIP_API_ID_hipExtModuleLaunchKernel: {
function = data->args.hipExtModuleLaunchKernel.f;
break;
}
case HIP_API_ID_hipHccModuleLaunchKernel: {
function = data->args.hipHccModuleLaunchKernel.f;
break;
}
default:
return {};
}
return std::visit(
Overloaded{
[](const void* func) { return HipLoader::Instance().KernelNameRefByPtr(func); },
[](hipFunction_t func) { return HipLoader::Instance().KernelNameRef(func); },
},
function);
}
+472
View File
@@ -0,0 +1,472 @@
/* Copyright (c) 2018-2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef SRC_TOOLS_TRACER_SRC_ROCTRACER_H_
#define SRC_TOOLS_TRACER_SRC_ROCTRACER_H_
#include <hip/hip_runtime.h>
#include <hsa/hsa.h>
#include <hsa/hsa_ext_amd.h>
#include <stddef.h>
#include <stdint.h>
#include <optional>
#include <string>
#include "hip_ostream_ops.h"
#include "hsa_ostream_ops.h"
#include "hsa_prof_str.h"
#include "inc/rocprofiler.h"
#include "src/core/memory/generic_buffer.h"
typedef struct {
rocprofiler_session_id_t session_id;
rocprofiler_buffer_id_t buffer_id;
} session_buffer_id_t;
typedef session_buffer_id_t roctracer_pool_t;
/* Correlation id */
typedef uint64_t activity_correlation_id_t;
typedef uint32_t activity_kind_t;
typedef uint32_t activity_op_t;
typedef uint64_t roctracer_timestamp_t;
typedef rocprofiler_tracer_activity_domain_t roctracer_domain_t;
typedef rocprofiler_tracer_activity_domain_t activity_domain_t;
// Prof_Protocol
/* Activity record type */
typedef struct activity_record_s {
uint32_t domain; /* activity domain id */
activity_kind_t kind; /* activity kind */
activity_op_t op; /* activity op */
union {
struct {
activity_correlation_id_t correlation_id; /* activity ID */
roctracer_timestamp_t begin_ns; /* host begin timestamp */
roctracer_timestamp_t end_ns; /* host end timestamp */
};
struct {
uint32_t se; /* sampled SE */
uint64_t cycle; /* sample cycle */
uint64_t pc; /* sample PC */
} pc_sample;
};
union {
struct {
int device_id; /* device id */
uint64_t queue_id; /* queue id */
};
struct {
uint32_t process_id; /* device id */
uint32_t thread_id; /* thread id */
};
struct {
activity_correlation_id_t external_id; /* external correlation id */
};
};
union {
size_t bytes; /* data size bytes */
const char* kernel_name; /* kernel name */
const char* mark_message;
};
} activity_record_t;
typedef activity_record_t roctracer_record_t;
/* Activity sync callback type */
typedef void (*activity_sync_callback_t)(activity_domain_t cid, activity_record_t* record,
const void* data, void* arg);
/* Activity async callback type */
typedef void (*activity_async_callback_t)(activity_domain_t op, void* record, void* arg);
/* API callback type */
typedef void (*activity_rtapi_callback_t)(activity_domain_t domain, uint32_t cid, const void* data,
void* arg);
typedef activity_rtapi_callback_t roctracer_rtapi_callback_t;
typedef roctracer_timestamp_t (*roctracer_get_timestamp_t)();
typedef rocprofiler_timestamp_t (*rocprofiler_get_timestamp_t)();
typedef uint32_t activity_kind_t;
typedef uint32_t activity_op_t;
/* API callback phase */
typedef enum { ACTIVITY_API_PHASE_ENTER = 0, ACTIVITY_API_PHASE_EXIT = 1 } activity_api_phase_t;
const char* roctracer_op_string(uint32_t domain, uint32_t op);
/* Trace record types */
/**
* Memory pool allocator callback.
*
* If \p *ptr is NULL, then allocate memory of \p size bytes and save address
* in \p *ptr.
*
* If \p *ptr is non-NULL and size is non-0, then reallocate the memory at \p
* *ptr with size \p size and save the address in \p *ptr. The memory will have
* been allocated by the same callback.
*
* If \p *ptr is non-NULL and size is 0, then deallocate the memory at \p *ptr.
* The memory will have been allocated by the same callback.
*
* \p size is the size of the memory allocation or reallocation, or 0 if
* deallocating.
*
* \p arg Argument provided
*/
typedef void (*roctracer_allocator_t)(char** ptr, size_t size, void* arg);
/**
* Memory pool buffer callback.
*
* The callback that will be invoked when a memory pool buffer becomes full or
* is flushed.
*
* \p begin pointer to first entry entry in the buffer.
*
* \p end pointer to one past the end entry in the buffer.
*
* \p arg the argument specified when the callback was defined.
*/
typedef void (*roctracer_buffer_callback_t)(const char* begin, const char* end, void* arg);
/**
* Memory pool properties.
*
* Defines the properties when a tracer memory pool is created.
*/
typedef struct {
/**
* ROC Tracer mode.
*/
uint32_t mode;
/**
* Size of buffer in bytes.
*/
size_t buffer_size;
/**
* The allocator function to use to allocate and deallocate the buffer. If
* NULL then \p malloc, \p realloc, and \p free are used.
*/
roctracer_allocator_t alloc_fun;
/**
* The argument to pass when invoking the \p alloc_fun allocator.
*/
void* alloc_arg;
/**
* The function to call when a buffer becomes full or is flushed.
*/
roctracer_buffer_callback_t buffer_callback_fun;
/**
* The argument to pass when invoking the \p buffer_callback_fun callback.
*/
void* buffer_callback_arg;
} roctracer_properties_t;
/**
* ROC Tracer API status codes.
*/
typedef enum {
/**
* The function has executed successfully.
*/
ROCTRACER_STATUS_SUCCESS = 0,
/**
* A generic error has occurred.
*/
ROCTRACER_STATUS_ERROR = -1,
/**
* The domain ID is invalid.
*/
ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID = -2,
/**
* An invalid argument was given to the function.
*/
ROCTRACER_STATUS_ERROR_INVALID_ARGUMENT = -3,
/**
* No default pool is defined.
*/
ROCTRACER_STATUS_ERROR_DEFAULT_POOL_UNDEFINED = -4,
/**
* The default pool is already defined.
*/
ROCTRACER_STATUS_ERROR_DEFAULT_POOL_ALREADY_DEFINED = -5,
/**
* Memory allocation error.
*/
ROCTRACER_STATUS_ERROR_MEMORY_ALLOCATION = -6,
/**
* External correlation ID pop mismatch.
*/
ROCTRACER_STATUS_ERROR_MISMATCHED_EXTERNAL_CORRELATION_ID = -7,
/**
* The operation is not currently implemented. This error may be reported by
* any function. Check the \ref known_limitations section to determine the
* status of the library implementation of the interface.
*/
ROCTRACER_STATUS_ERROR_NOT_IMPLEMENTED = -8,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_UNINIT = 2,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_BREAK = 3,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_BAD_DOMAIN = ROCTRACER_STATUS_ERROR_INVALID_DOMAIN_ID,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_BAD_PARAMETER = ROCTRACER_STATUS_ERROR_INVALID_ARGUMENT,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_HIP_API_ERR = 6,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_HIP_OPS_ERR = 7,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_HCC_OPS_ERR = ROCTRACER_STATUS_HIP_OPS_ERR,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_HSA_ERR = 7,
/**
* Deprecated error code.
*/
ROCTRACER_STATUS_ROCTX_ERR = 8,
} roctracer_status_t;
/**
* Query textual name of an operation of a domain.
* @param[in] domain Domain being queried.
* @param[in] op Operation within \p domain.
* @param[in] kind \todo Define kind.
* @return Returns the NUL terminated string for the operation name, or NULL if
* the domain or operation are invalid. The string is owned by the ROC Tracer
* library.
*/
const char* roctracer_op_string(uint32_t domain, uint32_t op, uint32_t kind);
/**
* Query the operation code given a domain and the name of an operation.
* @param[in] domain The domain being queried.
* @param[in] str The NUL terminated name of the operation name being queried.
* @param[out] op The operation code.
* @param[out] kind If not NULL then the operation kind code.
*/
void roctracer_op_code(uint32_t domain, const char* str, uint32_t* op, uint32_t* kind);
/**
* Set the properties of a domain.
* @param[in] domain The domain.
* @param[in] properties The properties. Each domain defines its own type for
* the properties. Some domains require the properties to be set before they
* can be enabled.
*/
void roctracer_set_properties(roctracer_domain_t domain, void* properties);
/**
* Enable runtime API callback for a specific operation of a domain.
* @param domain The domain.
* @param op The operation ID in \p domain.
* @param callback The callback to invoke each time the operation is performed
* on entry and exit.
* @param pool Value to pass as last argument of \p callback.
*/
void roctracer_enable_op_callback(roctracer_domain_t domain, uint32_t op,
roctracer_rtapi_callback_t callback);
/**
* Enable runtime API callback for all operations of a domain.
* @param domain The domain
* @param callback The callback to invoke each time the operation is performed
* on entry and exit.
* @param arg Value to pass as last argument of \p callback.
*/
void roctracer_enable_domain_callback(roctracer_domain_t domain,
roctracer_rtapi_callback_t callback,
void* user_data = nullptr);
/**
* Disable runtime API callback for a specific operation of a domain.
* @param domain The domain
* @param op The operation in \p domain.
*/
void roctracer_disable_op_callback(roctracer_domain_t domain, uint32_t op);
/**
* Disable runtime API callback for all operations of a domain.
* @param domain The domain
*/
void roctracer_disable_domain_callback(roctracer_domain_t domain);
/**
* Enable activity record logging for a specified operation of a domain using
* the default memory pool.
* @param[in] domain The domain.
* @param[in] op The activity operation ID in \p domain.
*/
void roctracer_enable_op_activity(roctracer_domain_t domain, uint32_t op, roctracer_pool_t pool);
/**
* Enable activity record logging for all operations of a domain using the
* default memory pool.
* @param[in] domain The domain.
*/
void roctracer_enable_domain_activity(roctracer_domain_t domain, roctracer_pool_t pool);
/**
* Disable activity record logging for a specified operation of a domain.
* @param[in] domain The domain.
* @param[in] op The activity operation ID in \p domain.
*/
void roctracer_disable_op_activity(roctracer_domain_t domain, uint32_t op);
/**
* Disable activity record logging for all operations of a domain.
* @param[in] domain The domain.
*/
void roctracer_disable_domain_activity(roctracer_domain_t domain);
std::optional<std::string> GetHipKernelName(uint32_t cid, hip_api_data_t* data);
// HIP Support
typedef enum {
HIP_OP_ID_DISPATCH = 0,
HIP_OP_ID_COPY = 1,
HIP_OP_ID_BARRIER = 2,
HIP_OP_ID_NUMBER = 3
} hip_op_id_t;
// HSA Support
// HSA OP ID enumeration
enum hsa_op_id_t {
HSA_OP_ID_DISPATCH = 0,
HSA_OP_ID_COPY = 1,
HSA_OP_ID_BARRIER = 2,
HSA_OP_ID_RESERVED1 = 3,
HSA_OP_ID_NUMBER
};
// HSA EVT ID enumeration
enum hsa_evt_id_t {
HSA_EVT_ID_ALLOCATE = 0, // Memory allocate callback
HSA_EVT_ID_DEVICE = 1, // Device assign callback
HSA_EVT_ID_MEMCOPY = 2, // Memcopy callback
HSA_EVT_ID_SUBMIT = 3, // Packet submission callback
HSA_EVT_ID_KSYMBOL = 4, // Loading/unloading of kernel symbol
HSA_EVT_ID_CODEOBJ = 5, // Loading/unloading of device code object
HSA_EVT_ID_NUMBER
};
struct hsa_ops_properties_t {
void* reserved1[4];
};
// ROCTx Support
typedef uint64_t roctx_range_id_t;
/**
* ROCTX API ID enumeration
*/
enum roctx_api_id_t {
ROCTX_API_ID_roctxMarkA = 0,
ROCTX_API_ID_roctxRangePushA = 1,
ROCTX_API_ID_roctxRangePop = 2,
ROCTX_API_ID_roctxRangeStartA = 3,
ROCTX_API_ID_roctxRangeStop = 4,
ROCTX_API_ID_NUMBER,
};
/**
* ROCTX callbacks data type
*/
typedef struct roctx_api_data_s {
union {
struct {
const char* message;
roctx_range_id_t id;
};
struct {
const char* message;
} roctxMarkA;
struct {
const char* message;
} roctxRangePushA;
struct {
const char* message;
} roctxRangePop;
struct {
const char* message;
roctx_range_id_t id;
} roctxRangeStartA;
struct {
const char* message;
roctx_range_id_t id;
} roctxRangeStop;
} args;
} roctx_api_data_t;
// External Support
/* Extension opcodes */
typedef enum { ACTIVITY_EXT_OP_MARK = 0, ACTIVITY_EXT_OP_EXTERN_ID = 1 } activity_ext_op_t;
typedef void (*roctracer_start_cb_t)();
typedef void (*roctracer_stop_cb_t)();
typedef struct {
roctracer_start_cb_t start_cb;
roctracer_stop_cb_t stop_cb;
} roctracer_ext_properties_t;
// Tracing start
void roctracer_start();
// Tracing stop
void roctracer_stop();
// Notifies that the calling thread is entering an external region.
// Push an external correlation id for the calling thread.
void roctracer_activity_push_external_correlation_id(activity_correlation_id_t id);
// Notifies that the calling thread is leaving an external region.
// Pop an external correlation id for the calling thread.
// 'lastId' returns the last external correlation if not NULL
void roctracer_activity_pop_external_correlation_id(activity_correlation_id_t* last_id);
#endif /* SRC_TOOLS_TRACER_SRC_ROCTRACER_H_ */
+434
View File
@@ -0,0 +1,434 @@
#include "tracer.h"
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <atomic>
#include <cassert>
#include <cstddef>
#include <map>
#include <mutex>
#include <utility>
#include "src/api/rocmtool.h"
#include "src/utils/helper.h"
#include "src/core/hsa/hsa_support.h"
#include "src/core/memory/generic_buffer.h"
namespace rocmtools {
namespace tracer {
std::mutex stream_ids_map_lock;
std::map<uint64_t, std::pair<uint64_t, uint64_t>> stream_ids;
std::map<uint64_t, uint64_t> used_stream_ids;
std::atomic<uint64_t> stream_count{1};
uint32_t GetPid() {
static uint32_t pid = syscall(__NR_getpid);
return pid;
}
uint32_t GetTid() {
static thread_local uint32_t tid = syscall(__NR_gettid);
return tid;
}
Tracer::Tracer(rocprofiler_session_id_t session_id, rocprofiler_sync_callback_t callback,
rocprofiler_buffer_id_t buffer_id,
std::vector<rocprofiler_tracer_activity_domain_t> domains)
: domains_(domains), callback_(callback), buffer_id_(buffer_id), session_id_(session_id) {
assert(!is_active_.load(std::memory_order_release) && "Error: The tracer was initialized!");
std::lock_guard<std::mutex> lock(tracer_lock_);
callback_data_ = api_callback_data_t{callback, session_id};
is_active_.exchange(true, std::memory_order_release);
}
void Tracer::StartRoctracer() {
if (!roctracer_initiated_.load(std::memory_order_release)) {
std::map<rocprofiler_tracer_activity_domain_t, is_filtered_domain_t> domains_filteration_map;
// TODO(aelwazir): get filter property and parse it here
for (auto& domain : domains_) {
domains_filteration_map.emplace(domain, false);
}
std::vector<std::string> api_filter_data_vector;
InitRoctracer(domains_filteration_map, api_filter_data_vector);
roctracer_initiated_.exchange(true, std::memory_order_release);
} else {
roctracer_start();
}
}
void Tracer::StopRoctracer() {
if (roctracer_initiated_.load(std::memory_order_release)) roctracer_stop();
}
void Tracer::DisableRoctracer() {
std::lock_guard<std::mutex> lock(tracer_lock_);
for (auto domain : domains_) {
switch (domain) {
case ACTIVITY_DOMAIN_ROCTX: {
roctracer_disable_domain_callback(ACTIVITY_DOMAIN_ROCTX);
break;
}
case ACTIVITY_DOMAIN_HSA_API: {
roctracer_disable_domain_callback(ACTIVITY_DOMAIN_HSA_API);
break;
}
case ACTIVITY_DOMAIN_HSA_OPS: {
roctracer_disable_domain_activity(ACTIVITY_DOMAIN_HSA_OPS);
break;
}
case ACTIVITY_DOMAIN_HIP_API: {
roctracer_disable_domain_callback(ACTIVITY_DOMAIN_HIP_API);
break;
}
case ACTIVITY_DOMAIN_HIP_OPS: {
roctracer_disable_domain_activity(ACTIVITY_DOMAIN_HIP_OPS);
break;
}
// TODO(aelwazir): Make sure if any other domain is needed by the
// API(User Usage)
default: {
fatal("Error: Provided Domain is not supported!");
}
}
}
}
Tracer::~Tracer() {
assert(is_active_.load(std::memory_order_release) && "Error: The tracer was not initialized!");
std::lock_guard<std::mutex> lock(tracer_lock_);
is_active_.exchange(false, std::memory_order_release);
// tracer_lock_.unlock();
}
std::mutex& Tracer::GetTracerLock() { return tracer_lock_; }
// TODO(aelwazir): To be implemented from here
bool Tracer::FindROCTxApiData(rocprofiler_tracer_api_data_handle_t api_data_handler) {
// std::lock_guard<std::mutex> lock(tracer_lock_);
return true;
}
bool Tracer::FindHSAApiData(rocprofiler_tracer_api_data_handle_t api_data_handler) {
// std::lock_guard<std::mutex> lock(tracer_lock_);
return true;
}
bool Tracer::FindHIPApiData(rocprofiler_tracer_api_data_handle_t api_data_handler) {
// std::lock_guard<std::mutex> lock(tracer_lock_);
return true;
}
size_t Tracer::GetROCTxApiDataInfoSize(rocprofiler_tracer_roctx_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id) {
const roctx_api_data_t* roctx_data =
reinterpret_cast<const roctx_api_data_t*>(api_data_id.handle);
switch (kind) {
case ROCPROFILER_ROCTX_MESSAGE: {
if (roctx_data && roctx_data->args.message)
return strlen(reinterpret_cast<const roctx_api_data_t*>(api_data_id.handle)->args.message) +
1;
else
return 0;
}
case ROCPROFILER_ROCTX_ID: {
if (roctx_data && roctx_data->args.id >= 0)
return std::to_string(roctx_data->args.id).size() + 1;
else
return 0;
}
default:
warning("ROCTX API Data Not Supported!");
}
return 0;
}
size_t Tracer::GetHSAApiDataInfoSize(rocprofiler_tracer_hsa_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id) {
switch (kind) {
case ROCPROFILER_HSA_FUNCTION_NAME: {
return strlen(roctracer_op_string(ACTIVITY_DOMAIN_HSA_API, operation_id.id)) + 1;
}
case ROCPROFILER_HSA_ACTIVITY_NAME: {
return strlen(roctracer_op_string(ACTIVITY_DOMAIN_HSA_OPS, operation_id.id)) + 1;
}
case ROCPROFILER_HSA_API_DATA: {
return api_data_id.size;
}
default:
warning("HSA API Data Not Supported!");
}
return 0;
}
size_t Tracer::GetHIPApiDataInfoSize(rocprofiler_tracer_hip_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id) {
switch (kind) {
case ROCPROFILER_HIP_KERNEL_NAME: {
hip_api_data_t* hip_data =
const_cast<hip_api_data_t*>(reinterpret_cast<const hip_api_data_t*>(api_data_id.handle));
if (api_data_id.handle && hip_data) {
auto kernel_name = GetHipKernelName(operation_id.id, hip_data);
if (kernel_name) return kernel_name->size() + 1;
}
return 0;
}
case ROCPROFILER_HIP_FUNCTION_NAME: {
return strlen(roctracer_op_string(ACTIVITY_DOMAIN_HIP_API, operation_id.id)) + 1;
}
case ROCPROFILER_HIP_ACTIVITY_NAME: {
return strlen(roctracer_op_string(ACTIVITY_DOMAIN_HIP_OPS, operation_id.id)) + 1;
}
case ROCPROFILER_HIP_STREAM_ID: {
std::lock_guard<std::mutex> lock(stream_ids_map_lock);
if (!stream_ids.empty() && stream_ids.find(operation_id.id) != stream_ids.end())
return std::to_string(stream_ids.at(operation_id.id).second).size() + 1;
else
return 0;
}
case ROCPROFILER_HIP_API_DATA: {
return api_data_id.size;
}
default:
warning("HIP API Data Not Supported!");
}
return 0;
}
char* Tracer::GetROCTxApiDataInfo(rocprofiler_tracer_roctx_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id) {
switch (kind) {
case ROCPROFILER_ROCTX_MESSAGE: {
return const_cast<char*>(
reinterpret_cast<const roctx_api_data_t*>(api_data_id.handle)->args.message);
}
case ROCPROFILER_ROCTX_ID: {
const roctx_api_data_t* roctx_data =
reinterpret_cast<const roctx_api_data_t*>(api_data_id.handle);
if (roctx_data && roctx_data->args.id >= 0)
return strdup(std::to_string(roctx_data->args.id).c_str());
else
return nullptr;
}
default:
warning("HSA API Data Not Supported!");
}
return nullptr;
}
char* Tracer::GetHSAApiDataInfo(rocprofiler_tracer_hsa_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id) {
switch (kind) {
case ROCPROFILER_HSA_FUNCTION_NAME: {
return const_cast<char*>(roctracer_op_string(ACTIVITY_DOMAIN_HSA_API, operation_id.id));
}
case ROCPROFILER_HSA_ACTIVITY_NAME: {
return const_cast<char*>(roctracer_op_string(ACTIVITY_DOMAIN_HSA_OPS, operation_id.id));
}
case ROCPROFILER_HSA_API_DATA: {
return const_cast<char*>(reinterpret_cast<const char*>(api_data_id.handle));
}
default:
warning("HSA API Data Not Supported!");
}
return nullptr;
}
char* Tracer::GetHIPApiDataInfo(rocprofiler_tracer_hip_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id) {
switch (kind) {
case ROCPROFILER_HIP_KERNEL_NAME: {
std::optional<std::string> kernel_name = GetHipKernelName(
operation_id.id,
const_cast<hip_api_data_t*>(reinterpret_cast<const hip_api_data_t*>(api_data_id.handle)));
if (kernel_name && kernel_name->find(" ") == std::string::npos) {
return strdup(kernel_name->c_str());
}
return nullptr;
}
case ROCPROFILER_HIP_FUNCTION_NAME: {
return const_cast<char*>(roctracer_op_string(ACTIVITY_DOMAIN_HIP_API, operation_id.id));
}
case ROCPROFILER_HIP_ACTIVITY_NAME: {
return const_cast<char*>(roctracer_op_string(ACTIVITY_DOMAIN_HIP_OPS, operation_id.id));
}
case ROCPROFILER_HIP_STREAM_ID: {
std::lock_guard<std::mutex> lock(stream_ids_map_lock);
if (!stream_ids.empty() && stream_ids.find(operation_id.id) != stream_ids.end())
return strdup(
const_cast<char*>(std::to_string(stream_ids.at(operation_id.id).second).c_str()));
else
return nullptr;
}
case ROCPROFILER_HIP_API_DATA: {
return const_cast<char*>(reinterpret_cast<const char*>(api_data_id.handle));
}
default:
warning("HIP API Data Not Supported!");
}
return nullptr;
}
// TODO(aelwazir): Till here
void api_callback(activity_domain_t domain, uint32_t cid, const void* callback_data, void* args) {
api_callback_data_t* args_data = reinterpret_cast<api_callback_data_t*>(args);
if (args_data && rocmtools::GetROCMToolObj() &&
rocmtools::GetROCMToolObj()->GetSession(args_data->session_id) &&
rocmtools::GetROCMToolObj()->GetSession(args_data->session_id)->GetTracer()) {
switch (domain) {
case ACTIVITY_DOMAIN_ROCTX: {
const roctx_api_data_t* data = reinterpret_cast<const roctx_api_data_t*>(callback_data);
// if (data->args.message) roctx_labels.emplace(data->args.id, data->args.message);
args_data->user_sync_callback(
rocprofiler_record_tracer_t{
rocprofiler_record_header_t{
ROCPROFILER_TRACER_RECORD,
rocprofiler_record_id_t{rocmtools::GetROCMToolObj()->GetUniqueRecordId()}},
rocprofiler_tracer_external_id_t{0}, ACTIVITY_DOMAIN_ROCTX,
rocprofiler_tracer_operation_id_t{cid},
rocprofiler_tracer_api_data_handle_t{callback_data, sizeof(*data)},
rocprofiler_tracer_activity_correlation_id_t{0},
rocprofiler_record_header_timestamp_t{roctracer::hsa_support::timestamp_ns(),
rocprofiler_timestamp_t{0}},
0, 0, GetTid()},
args_data->session_id);
break;
}
case ACTIVITY_DOMAIN_HSA_API: {
hsa_api_data_t* data =
const_cast<hsa_api_data_t*>(reinterpret_cast<const hsa_api_data_t*>(callback_data));
if (data->phase == ACTIVITY_API_PHASE_ENTER) {
*(data->phase_data) = roctracer::hsa_support::timestamp_ns().value;
} else {
args_data->user_sync_callback(
rocprofiler_record_tracer_t{
rocprofiler_record_header_t{
ROCPROFILER_TRACER_RECORD,
rocprofiler_record_id_t{rocmtools::GetROCMToolObj()->GetUniqueRecordId()}},
rocprofiler_tracer_external_id_t{0}, ACTIVITY_DOMAIN_HSA_API,
rocprofiler_tracer_operation_id_t{cid},
rocprofiler_tracer_api_data_handle_t{callback_data, sizeof(*data)},
rocprofiler_tracer_activity_correlation_id_t{data->correlation_id},
rocprofiler_record_header_timestamp_t{rocprofiler_timestamp_t{*(data->phase_data)},
roctracer::hsa_support::timestamp_ns()},
0, 0, GetTid()},
args_data->session_id);
}
break;
}
case ACTIVITY_DOMAIN_HIP_API: {
hip_api_data_t* data =
const_cast<hip_api_data_t*>(reinterpret_cast<const hip_api_data_t*>(callback_data));
if (data->phase == ACTIVITY_API_PHASE_ENTER) {
*(data->phase_data) = roctracer::hsa_support::timestamp_ns().value;
} else {
hipApiArgsInit((hip_api_id_t)cid, data);
std::string hip_api_data_string = hipApiString((hip_api_id_t)cid, data);
std::string start_str = "stream=";
int start = hip_api_data_string.find(start_str);
uint64_t stream_id = 0;
if (start >= 0) {
int end = hip_api_data_string.find(",", start);
std::string stream_id_str = hip_api_data_string.substr(start + start_str.length(), end);
std::stringstream ss;
ss << std::hex << stream_id_str;
ss >> stream_id;
}
{
std::lock_guard<std::mutex> lock(stream_ids_map_lock);
if (used_stream_ids.find(stream_id) == used_stream_ids.end()) {
uint64_t stream_generated_id = stream_count.fetch_add(1, std::memory_order_release);
used_stream_ids.emplace(stream_id, stream_generated_id);
stream_ids.emplace(data->correlation_id,
std::make_pair(stream_id, stream_generated_id));
} else {
stream_ids.emplace(data->correlation_id,
std::make_pair(stream_id, used_stream_ids.at(stream_id)));
}
}
args_data->user_sync_callback(
rocprofiler_record_tracer_t{
rocprofiler_record_header_t{
ROCPROFILER_TRACER_RECORD,
rocprofiler_record_id_t{rocmtools::GetROCMToolObj()->GetUniqueRecordId()}},
rocprofiler_tracer_external_id_t{0}, ACTIVITY_DOMAIN_HIP_API,
rocprofiler_tracer_operation_id_t{cid},
rocprofiler_tracer_api_data_handle_t{callback_data, sizeof(*data)},
rocprofiler_tracer_activity_correlation_id_t{data->correlation_id},
rocprofiler_record_header_timestamp_t{rocprofiler_timestamp_t{*(data->phase_data)},
roctracer::hsa_support::timestamp_ns()},
0, 0, GetTid()},
args_data->session_id);
}
break;
}
default:
warning("Domain(%u) is not supported for Synchronous callbacks!", domain);
}
}
}
void Tracer::InitRoctracer(
const std::map<rocprofiler_tracer_activity_domain_t, is_filtered_domain_t>& domains,
const std::vector<std::string>& api_filter_data_vector) {
for (auto domain : domains) {
switch (domain.first) {
case ACTIVITY_DOMAIN_ROCTX: {
assert(!domain.second && "Error: ROCTX API can't be filtered!");
roctracer_enable_domain_callback(ACTIVITY_DOMAIN_ROCTX, api_callback, &callback_data_);
break;
}
case ACTIVITY_DOMAIN_HSA_API: {
if (!domain.second) {
roctracer_enable_domain_callback(ACTIVITY_DOMAIN_HSA_API, api_callback, &callback_data_);
} else {
assert(!api_filter_data_vector.empty() &&
"Error: HSA API calls filter data is empty and domain "
"filter option was enabled!");
}
break;
}
case ACTIVITY_DOMAIN_HIP_API: {
if (!domain.second) {
roctracer_enable_domain_callback(ACTIVITY_DOMAIN_HIP_API, api_callback, &callback_data_);
} else {
assert(!api_filter_data_vector.empty() &&
"Error: HIP API calls filter data is empty and domain "
"filter option was enabled!");
}
break;
}
case ACTIVITY_DOMAIN_HSA_OPS: {
// assert(!domain.second && "Error: HSA OPS can't be filtered!");
// Tracer_enable_domain_activity(ACTIVITY_DOMAIN_HSA_OPS, pool);
// TODO(aelwazir): to be replaced with the above lines after the
// whole integeration is done, make sure that tracer is responsible
// of kernel dispatches alongside mem copies, profiler will be only
// responsible for counter collection
roctracer_enable_op_activity(ACTIVITY_DOMAIN_HSA_OPS, HSA_OP_ID_COPY,
session_buffer_id_t{session_id_, buffer_id_});
break;
}
case ACTIVITY_DOMAIN_HIP_OPS: {
assert(!domain.second && "Error: HIP OPS can't be filtered!");
roctracer_enable_domain_activity(ACTIVITY_DOMAIN_HIP_OPS,
session_buffer_id_t{session_id_, buffer_id_});
break;
}
// TODO(aelwazir): Make sure if any other domain is needed by the
// API(User Usage)
default: {
fatal("Error: Provided Domain is not supported!");
}
}
}
}
} // namespace tracer
} // namespace rocmtools
+104
View File
@@ -0,0 +1,104 @@
/* Copyright (c) 2022 Advanced Micro Devices, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. */
#ifndef SRC_TOOLS_TRACER_TRACER_H_
#define SRC_TOOLS_TRACER_TRACER_H_
#include <atomic>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#include "inc/rocprofiler.h"
#include "src/roctracer.h"
typedef bool is_filtered_domain_t;
typedef struct {
rocprofiler_sync_callback_t user_sync_callback;
rocprofiler_session_id_t session_id;
} api_callback_data_t;
namespace rocmtools {
namespace tracer {
class Tracer {
public:
// Getting Buffer and/or sync callback
Tracer(rocprofiler_session_id_t session_id, rocprofiler_sync_callback_t callback,
rocprofiler_buffer_id_t buffer_id, std::vector<rocprofiler_tracer_activity_domain_t> domains);
~Tracer();
rocprofiler_tracer_api_data_handle_t AddROCTxApiData(std::string api_data);
rocprofiler_tracer_api_data_handle_t AddHSAApiData(hsa_api_data_t api_data);
rocprofiler_tracer_api_data_handle_t AddHIPApiData(hip_api_data_t api_data);
bool FindROCTxApiData(rocprofiler_tracer_api_data_handle_t api_data_handler);
bool FindHSAApiData(rocprofiler_tracer_api_data_handle_t api_data_handler);
bool FindHIPApiData(rocprofiler_tracer_api_data_handle_t api_data_handler);
size_t GetROCTxApiDataInfoSize(rocprofiler_tracer_roctx_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id);
size_t GetHSAApiDataInfoSize(rocprofiler_tracer_hsa_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id);
size_t GetHIPApiDataInfoSize(rocprofiler_tracer_hip_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id);
char* GetROCTxApiDataInfo(rocprofiler_tracer_roctx_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id);
char* GetHSAApiDataInfo(rocprofiler_tracer_hsa_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id);
char* GetHIPApiDataInfo(rocprofiler_tracer_hip_api_data_info_t kind,
rocprofiler_tracer_api_data_handle_t api_data_id,
rocprofiler_tracer_operation_id_t operation_id);
void InitRoctracer(
const std::map<rocprofiler_tracer_activity_domain_t, is_filtered_domain_t>& domains,
const std::vector<std::string>& api_filter_data_vector);
std::mutex& GetTracerLock();
void DisableRoctracer();
void StartRoctracer();
void StopRoctracer();
private:
std::atomic<bool> is_active_{false};
std::atomic<bool> roctracer_initiated_{false};
std::atomic<int (*)(rocprofiler_tracer_activity_domain_t domain, uint32_t operation_id, void* data)>
roctx_report_activity_;
std::vector<rocprofiler_tracer_activity_domain_t> domains_;
rocprofiler_sync_callback_t callback_;
rocprofiler_buffer_id_t buffer_id_;
rocprofiler_session_id_t session_id_;
api_callback_data_t callback_data_;
std::mutex tracer_lock_;
};
} // namespace tracer
} // namespace rocmtools
#endif // SRC_TOOLS_TRACER_TRACER_H_