Adding rocprofilerv2
Change-Id: Ic0cc280ba207d2b8f6ccae1cd4ac3184152fc1ad
This commit is contained in:
@@ -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_;
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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_
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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
|
||||
@@ -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_
|
||||
Reference in New Issue
Block a user