ROC profiler prototype sources importing
이 커밋은 다음에 포함됨:
@@ -0,0 +1,14 @@
|
||||
#
|
||||
# Build dynamic Library object
|
||||
#
|
||||
set ( TARGET_LIB "${TARGET_NAME}" )
|
||||
set ( LIB_SRC
|
||||
${LIB_DIR}/core/rocprofiler.cpp
|
||||
${LIB_DIR}/core/proxy_queue.cpp
|
||||
${LIB_DIR}/core/simple_proxy_queue.cpp
|
||||
${LIB_DIR}/core/intercept_queue.cpp
|
||||
${LIB_DIR}/util/hsa_rsrc_factory.cpp
|
||||
)
|
||||
add_library ( ${TARGET_LIB} SHARED ${LIB_SRC} )
|
||||
target_include_directories ( ${TARGET_LIB} PRIVATE ${LIB_DIR} ${ROOT_DIR} ${HSA_RUNTIME_INC_PATH} )
|
||||
target_link_libraries( ${TARGET_LIB} PRIVATE ${HSA_RUNTIME_LIB} c stdc++)
|
||||
+399
@@ -0,0 +1,399 @@
|
||||
#ifndef SRC_CORE_CONTEXT_H_
|
||||
#define SRC_CORE_CONTEXT_H_
|
||||
|
||||
#include "inc/rocprofiler.h"
|
||||
|
||||
#include <hsa.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "core/metrics.h"
|
||||
#include "core/profile.h"
|
||||
#include "core/queue.h"
|
||||
#include "core/types.h"
|
||||
#include "util/exception.h"
|
||||
#include "util/hsa_rsrc_factory.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
struct rocprofiler_contex_t;
|
||||
class Context;
|
||||
|
||||
inline unsigned align_size(unsigned size, unsigned alignment) { return ((size + alignment - 1) & ~(alignment - 1)); }
|
||||
|
||||
// Block descriptor
|
||||
struct block_des_t {
|
||||
uint32_t id;
|
||||
uint32_t index;
|
||||
};
|
||||
|
||||
// block_des_t less-then functor
|
||||
struct lt_block_des {
|
||||
bool operator()(const block_des_t& a1, const block_des_t& a2) const {
|
||||
return (a1.id < a2.id) || ((a1.id == a2.id) && (a1.index < a2.index));
|
||||
}
|
||||
};
|
||||
|
||||
// Block status
|
||||
struct block_status_t {
|
||||
uint32_t max_counters;
|
||||
uint32_t counter_index;
|
||||
uint32_t group_index;
|
||||
};
|
||||
|
||||
// Metrics arguments
|
||||
template <class Map>
|
||||
class MetricArgs : public xml::args_cache_t {
|
||||
public:
|
||||
MetricArgs(const Map& map) : map_(map) {}
|
||||
bool Lookup(const std::string& name, uint64_t& result) const {
|
||||
rocprofiler_info_t* info = NULL;
|
||||
auto it = map_.find(name);
|
||||
if (it == map_.end()) EXC_RAISING(HSA_STATUS_ERROR, "var '" << name << "' is not found");
|
||||
info = it->second;
|
||||
if (info) {
|
||||
result = info->data.result_int64;
|
||||
if (info->data.kind == ROCPROFILER_UNINIT) EXC_RAISING(HSA_STATUS_ERROR, "var '" << name << "' is uninitialized");
|
||||
if (info->data.kind != ROCPROFILER_INT64) EXC_RAISING(HSA_STATUS_ERROR, "var '" << name << "' is of incompatible type, not INT64");
|
||||
} else EXC_RAISING(HSA_STATUS_ERROR, "var '" << name << "' info is NULL");
|
||||
return (info != NULL);
|
||||
}
|
||||
private:
|
||||
const Map& map_;
|
||||
};
|
||||
|
||||
// Profiling group
|
||||
class Group {
|
||||
public:
|
||||
Group(const util::AgentInfo* agent_info, Context *context, const uint32_t& index) :
|
||||
pmc_profile_(agent_info),
|
||||
sqtt_profile_(agent_info),
|
||||
context_(context),
|
||||
index_(index)
|
||||
{}
|
||||
|
||||
void Insert(const profile_info_t& info) {
|
||||
const rocprofiler_type_t type = info.rinfo->type;
|
||||
info_vector_.push_back(info.rinfo);
|
||||
switch (type) {
|
||||
case ROCPROFILER_TYPE_METRIC:
|
||||
pmc_profile_.Insert(info);
|
||||
break;
|
||||
case ROCPROFILER_TYPE_TRACE:
|
||||
sqtt_profile_.Insert(info);
|
||||
break;
|
||||
default:
|
||||
EXC_RAISING(HSA_STATUS_ERROR, "bad rocprofiler type (" << type << ")");
|
||||
}
|
||||
}
|
||||
|
||||
hsa_status_t Finalize() {
|
||||
hsa_status_t status = pmc_profile_.Finalize(start_vector_, stop_vector_);
|
||||
if (status == HSA_STATUS_SUCCESS) {
|
||||
status = sqtt_profile_.Finalize(start_vector_, stop_vector_);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void GetProfiles(profile_vector_t& vec) {
|
||||
pmc_profile_.GetProfiles(vec);
|
||||
sqtt_profile_.GetProfiles(vec);
|
||||
}
|
||||
|
||||
void GetTraceProfiles(profile_vector_t& vec) {
|
||||
sqtt_profile_.GetProfiles(vec);
|
||||
}
|
||||
|
||||
info_vector_t& GetInfoVector() { return info_vector_; }
|
||||
const pkt_vector_t& GetStartVector() const { return start_vector_; }
|
||||
const pkt_vector_t& GetStopVector() const { return stop_vector_; }
|
||||
Context* GetContext() { return context_; }
|
||||
uint32_t GetIndex() const { return index_; }
|
||||
|
||||
private:
|
||||
PmcProfile pmc_profile_;
|
||||
SqttProfile sqtt_profile_;
|
||||
info_vector_t info_vector_;
|
||||
pkt_vector_t start_vector_;
|
||||
pkt_vector_t stop_vector_;
|
||||
Context* const context_;
|
||||
const uint32_t index_;
|
||||
};
|
||||
|
||||
// Profiling context
|
||||
class Context {
|
||||
public:
|
||||
typedef std::map<std::string, rocprofiler_info_t*> info_map_t;
|
||||
|
||||
Context(const util::AgentInfo* agent_info, Queue* queue, rocprofiler_info_t* info, const uint32_t info_count) :
|
||||
agent_(agent_info->dev_id),
|
||||
agent_info_(agent_info),
|
||||
queue_(queue),
|
||||
hsa_rsrc_(&util::HsaRsrcFactory::Instance()),
|
||||
api_(hsa_rsrc_->AqlProfileApi()),
|
||||
metrics_(agent_info)
|
||||
{
|
||||
Initialize(info, info_count);
|
||||
Finalize();
|
||||
}
|
||||
|
||||
~Context() {
|
||||
for (const auto& v : info_map_) {
|
||||
const std::string& name = v.first;
|
||||
const rocprofiler_info_t* info = v.second;
|
||||
if ((info->type == ROCPROFILER_TYPE_METRIC) && (metrics_map_.find(name) == metrics_map_.end())) {
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Initialize(rocprofiler_info_t* info_array, const uint32_t info_count) {
|
||||
info_map_t input_map;
|
||||
for (unsigned i = 0; i < info_count; ++i) {
|
||||
rocprofiler_info_t* info = &info_array[i];
|
||||
input_map[info->name] = info;
|
||||
info->data.kind = ROCPROFILER_UNINIT;
|
||||
}
|
||||
|
||||
if (info_count) set_.push_back(Group(agent_info_, this, 0));
|
||||
|
||||
for (unsigned i = 0; i < info_count; ++i) {
|
||||
rocprofiler_info_t* info = &info_array[i];
|
||||
info_map_[info->name] = info;
|
||||
const rocprofiler_type_t type = info->type;
|
||||
const char* name = info->name;
|
||||
|
||||
if (type == ROCPROFILER_TYPE_METRIC) {
|
||||
const Metric* metric = metrics_.Get(name);
|
||||
if (metric == NULL) EXC_RAISING(HSA_STATUS_ERROR, "metric '" << name << "' is not found");
|
||||
auto ret = metrics_map_.insert({name, metric});
|
||||
if (!ret.second) EXC_RAISING(HSA_STATUS_ERROR, "metric '" << name << "' is registered more then once");
|
||||
|
||||
counters_vec_t counters_vec = metric->GetCounters();
|
||||
if (counters_vec.empty()) EXC_RAISING(HSA_STATUS_ERROR, "metric name '" << name << "' is not found");
|
||||
|
||||
for (const counter_t* counter : counters_vec) {
|
||||
if (metric->GetExpr()) {
|
||||
auto it = input_map.find(counter->name);
|
||||
if (it != input_map.end()) {
|
||||
continue;
|
||||
} else {
|
||||
info = NewCounterInfo(counter);
|
||||
info_map_[info->name] = info;
|
||||
}
|
||||
}
|
||||
|
||||
const event_t* event = &(counter->event);
|
||||
const block_des_t block_des = {event->block_name, event->block_index};
|
||||
auto ret = groups_map_.insert({block_des, {}});
|
||||
block_status_t& block_status = ret.first->second;
|
||||
if (block_status.max_counters == 0) {
|
||||
profile_t query = {};
|
||||
query.agent = agent_;
|
||||
query.type = HSA_VEN_AMD_AQLPROFILE_EVENT_TYPE_PMC;
|
||||
query.events = event;
|
||||
|
||||
uint32_t block_counters;
|
||||
hsa_status_t status = api_->hsa_ven_amd_aqlprofile_get_info(&query, HSA_VEN_AMD_AQLPROFILE_INFO_BLOCK_COUNTERS, &block_counters);
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "get block_counters info");
|
||||
block_status.max_counters = block_counters;
|
||||
}
|
||||
if (block_status.counter_index >= block_status.max_counters) {
|
||||
block_status.counter_index = 0;
|
||||
block_status.group_index += 1;
|
||||
}
|
||||
if (block_status.group_index >= set_.size()) {
|
||||
set_.push_back(Group(agent_info_, this, block_status.group_index));
|
||||
}
|
||||
const uint32_t group_index = block_status.group_index;
|
||||
set_[group_index].Insert(profile_info_t{event, NULL, 0, info});
|
||||
}
|
||||
} else if (type == ROCPROFILER_TYPE_TRACE) {
|
||||
set_[0].Insert(profile_info_t{NULL, info->parameters, info->parameter_count, info});
|
||||
} else {
|
||||
EXC_RAISING(HSA_STATUS_ERROR, "bad rocprofiler type (" << type << ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Finalize() {
|
||||
for (unsigned index = 0; index < set_.size(); ++index) {
|
||||
const hsa_status_t status = set_[index].Finalize();
|
||||
if (status != HSA_STATUS_SUCCESS) EXC_RAISING(status, "context finalize failed");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t GetGroupCount() const { return set_.size(); }
|
||||
|
||||
rocprofiler_group_t GetGroupInfo(const uint32_t& index) {
|
||||
rocprofiler::info_vector_t& info_vector = set_[index].GetInfoVector();
|
||||
rocprofiler_group_t group = {};
|
||||
group.info_count = info_vector.size();
|
||||
group.info = &info_vector[0];
|
||||
group.context = reinterpret_cast<rocprofiler_t*>(this);
|
||||
group.index = index;
|
||||
return group;
|
||||
}
|
||||
|
||||
const pkt_vector_t& StartPackets(const uint32_t& group_index) const { return set_[group_index].GetStartVector(); }
|
||||
const pkt_vector_t& StopPackets(const uint32_t& group_index) const { return set_[group_index].GetStopVector(); }
|
||||
|
||||
void Start(const uint32_t& group_index, Queue* const queue = NULL) {
|
||||
const pkt_vector_t& start_packets = StartPackets(group_index);
|
||||
Queue* const submit_queue = (queue != NULL) ? queue : queue_;
|
||||
submit_queue->Submit(&start_packets[0], start_packets.size());
|
||||
}
|
||||
void Stop(const uint32_t& group_index, Queue* const queue = NULL) {
|
||||
const pkt_vector_t& stop_packets = StopPackets(group_index);
|
||||
Queue* const submit_queue = (queue != NULL) ? queue : queue_;
|
||||
submit_queue->Submit(&stop_packets[0], stop_packets.size());
|
||||
}
|
||||
void Submit(const uint32_t& group_index, const packet_t* packet, Queue* const queue = NULL) {
|
||||
Queue* const submit_queue = (queue != NULL) ? queue : queue_;
|
||||
Start(group_index, submit_queue);
|
||||
submit_queue->Submit(packet);
|
||||
Stop(group_index, submit_queue);
|
||||
}
|
||||
|
||||
struct callback_data_t {
|
||||
info_vector_t* info_vector;
|
||||
size_t index;
|
||||
char* ptr;
|
||||
};
|
||||
|
||||
void GetData(const uint32_t& group_index) {
|
||||
const profile_vector_t profile_vector = GetProfiles(group_index);
|
||||
for (auto& tuple : profile_vector) {
|
||||
// Wait for stop packet to complete
|
||||
hsa_signal_wait_scacquire(
|
||||
tuple.completion_signal,
|
||||
HSA_SIGNAL_CONDITION_LT,
|
||||
1,
|
||||
(uint64_t)-1,
|
||||
HSA_WAIT_STATE_BLOCKED);
|
||||
callback_data_t callback_data{tuple.info_vector, tuple.info_vector->size(), NULL};
|
||||
const hsa_status_t status = api_->hsa_ven_amd_aqlprofile_iterate_data(tuple.profile, DataCallback, &callback_data);
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "context iterate data failed");
|
||||
}
|
||||
}
|
||||
|
||||
void GetMetricsData() const {
|
||||
const MetricArgs<info_map_t> args(info_map_);
|
||||
for (const auto v : metrics_map_) {
|
||||
const std::string& name = v.first;
|
||||
const Metric* metric = v.second;
|
||||
const xml::Expr* expr = metric->GetExpr();
|
||||
if (expr) {
|
||||
auto it = info_map_.find(name);
|
||||
if (it == info_map_.end()) EXC_RAISING(HSA_STATUS_ERROR, "metric '" << name << "', rocprofiler info is not found");
|
||||
rocprofiler_info_t* info = it->second;
|
||||
info->data.result_int64 = expr->Eval(args);
|
||||
info->data.kind = ROCPROFILER_INT64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IterateTraceData(rocprofiler_trace_data_callback_t callback, void *data) {
|
||||
profile_vector_t profile_vector;
|
||||
set_[0].GetTraceProfiles(profile_vector);
|
||||
for (auto& tuple : profile_vector) {
|
||||
const hsa_status_t status = api_->hsa_ven_amd_aqlprofile_iterate_data(tuple.profile, callback, data);
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "context iterate data failed");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Getting profling packets
|
||||
profile_vector_t GetProfiles(const uint32_t& index) {
|
||||
profile_vector_t vec;
|
||||
if (index >= set_.size()) {
|
||||
EXC_RAISING(HSA_STATUS_ERROR, "index exceeding the maximum " << set_.size());
|
||||
}
|
||||
set_[index].GetProfiles(vec);
|
||||
return vec;
|
||||
}
|
||||
|
||||
static hsa_status_t DataCallback(hsa_ven_amd_aqlprofile_info_type_t ainfo_type,
|
||||
hsa_ven_amd_aqlprofile_info_data_t* ainfo_data,
|
||||
void* data) {
|
||||
hsa_status_t status = HSA_STATUS_SUCCESS;
|
||||
callback_data_t* callback_data = reinterpret_cast<callback_data_t*>(data);
|
||||
info_vector_t& info_vector = *(callback_data->info_vector);
|
||||
uint32_t index = callback_data->index;
|
||||
const uint32_t sample_id = ainfo_data->sample_id;
|
||||
if (info_vector.size() == index) {
|
||||
index = 0;
|
||||
} else {
|
||||
if (sample_id == 0) index += 1;
|
||||
}
|
||||
callback_data->index = index;
|
||||
|
||||
if (index < info_vector.size()) {
|
||||
rocprofiler_info_t* rinfo = info_vector[index];
|
||||
if (ainfo_type == HSA_VEN_AMD_AQLPROFILE_INFO_PMC_DATA) {
|
||||
if (ainfo_data->sample_id == 0) rinfo->data.result_int64 = 0;
|
||||
rinfo->data.result_int64 += ainfo_data->pmc_data.result;
|
||||
rinfo->data.kind = ROCPROFILER_INT64;
|
||||
} else if (ainfo_type == HSA_VEN_AMD_AQLPROFILE_INFO_SQTT_DATA) {
|
||||
if (rinfo->data.result_bytes.copy) {
|
||||
char* result_bytes_ptr = reinterpret_cast<char*>(rinfo->data.result_bytes.ptr);
|
||||
const char* end = result_bytes_ptr + rinfo->data.result_bytes.size;
|
||||
const char* src = reinterpret_cast<char*>(ainfo_data->sqtt_data.ptr);
|
||||
const uint32_t size = ainfo_data->sqtt_data.size;
|
||||
char* ptr = (sample_id == 0) ? result_bytes_ptr : callback_data->ptr;
|
||||
uint64_t* header = reinterpret_cast<uint64_t*>(ptr);
|
||||
char* dest = ptr + sizeof(*header);
|
||||
|
||||
if ((dest + size) < end) {
|
||||
hsa_status_t status = hsa_memory_copy(dest, src, size);
|
||||
if (status == HSA_STATUS_SUCCESS) {
|
||||
*header = size;
|
||||
rinfo->data.kind = ROCPROFILER_BYTES;
|
||||
rinfo->data.result_bytes.instance_count = sample_id + 1;
|
||||
callback_data->ptr = dest + align_size(size, sizeof(uint64_t));
|
||||
}
|
||||
} else status = HSA_STATUS_ERROR;
|
||||
} else {
|
||||
if (sample_id == 0) {
|
||||
rinfo->data.kind = ROCPROFILER_BYTES;
|
||||
rinfo->data.result_bytes.ptr = ainfo_data->sqtt_data.ptr;
|
||||
rinfo->data.result_bytes.instance_count = UINT32_MAX;
|
||||
}
|
||||
rinfo->data.result_bytes.instance_count += 1;
|
||||
}
|
||||
} else status = HSA_STATUS_ERROR;
|
||||
} else status = HSA_STATUS_ERROR;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
rocprofiler_info_t* NewCounterInfo(const counter_t* counter) {
|
||||
rocprofiler_info_t* info = new rocprofiler_info_t{};
|
||||
info->type = ROCPROFILER_TYPE_METRIC;
|
||||
info->name = counter->name.c_str();
|
||||
return info;
|
||||
}
|
||||
|
||||
// GPU handel
|
||||
const hsa_agent_t agent_;
|
||||
const util::AgentInfo* agent_info_;
|
||||
// Profiling queue
|
||||
Queue* queue_;
|
||||
// HSA resources factory
|
||||
util::HsaRsrcFactory* hsa_rsrc_;
|
||||
// aqlprofile API table
|
||||
const pfn_t* api_;
|
||||
// Profile group set
|
||||
std::vector<Group> set_;
|
||||
// Metrics dictionary
|
||||
MetricsDict metrics_;
|
||||
// Groups map
|
||||
std::map<block_des_t, block_status_t, lt_block_des> groups_map_;
|
||||
// Info map
|
||||
info_map_t info_map_;
|
||||
// Metrics map
|
||||
std::map<std::string, const Metric*> metrics_map_;
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // SRC_CORE_CONTEXT_H_
|
||||
@@ -0,0 +1,49 @@
|
||||
#ifndef _SRC_CORE_HSA_PROXY_QUEUE_H
|
||||
#define _SRC_CORE_HSA_PROXY_QUEUE_H
|
||||
|
||||
#include <hsa.h>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include "core/proxy_queue.h"
|
||||
#include "util/exception.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
extern decltype(hsa_queue_destroy)* hsa_queue_destroy_fn;
|
||||
extern decltype(hsa_amd_queue_intercept_create)* hsa_amd_queue_intercept_create_fn;
|
||||
extern decltype(hsa_amd_queue_intercept_register)* hsa_amd_queue_intercept_register_fn;
|
||||
|
||||
class HsaProxyQueue : public ProxyQueue {
|
||||
public:
|
||||
hsa_status_t SetInterceptCB(on_submit_cb_t on_submit_cb, void* data) {
|
||||
return hsa_amd_queue_intercept_register_fn(queue_, on_submit_cb, data);
|
||||
}
|
||||
|
||||
void Submit(const packet_t* packet) { EXC_RAISING(HSA_STATUS_ERROR, "HsaProxyQueue::Submit() is not supported"); }
|
||||
|
||||
private:
|
||||
hsa_status_t Init(
|
||||
hsa_agent_t agent,
|
||||
uint32_t size,
|
||||
hsa_queue_type32_t type,
|
||||
void (*callback)(hsa_status_t status, hsa_queue_t *source, void *data),
|
||||
void *data,
|
||||
uint32_t private_segment_size,
|
||||
uint32_t group_segment_size,
|
||||
hsa_queue_t **queue)
|
||||
{
|
||||
printf("HsaProxyQueue::Init()\n");
|
||||
const auto status = hsa_amd_queue_intercept_create_fn(agent, size, type, callback, data, private_segment_size, group_segment_size, &queue_);
|
||||
*queue = queue_;
|
||||
return status;
|
||||
}
|
||||
|
||||
hsa_status_t Cleanup() const { return hsa_queue_destroy_fn(queue_); }
|
||||
|
||||
hsa_queue_t* queue_;
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // _SRC_CORE_HSA_PROXY_QUEUE_H
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef _SRC_CORE_HSA_QUEUE_H
|
||||
#define _SRC_CORE_HSA_QUEUE_H
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "core/queue.h"
|
||||
#include "core/types.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
|
||||
class HsaQueue : public Queue {
|
||||
public:
|
||||
typedef void (HsaQueue::*submit_fptr_t)(const packet_t* packet);
|
||||
enum {
|
||||
LEGACY_SLOT_SIZE_W = HSA_VEN_AMD_AQLPROFILE_LEGACY_PM4_PACKET_SIZE / sizeof(packet_word_t),
|
||||
LEGACY_SLOT_SIZE_P = HSA_VEN_AMD_AQLPROFILE_LEGACY_PM4_PACKET_SIZE / sizeof(packet_t)
|
||||
};
|
||||
struct slot_pm4_t {
|
||||
packet_word_t words[LEGACY_SLOT_SIZE_W];
|
||||
};
|
||||
|
||||
HsaQueue(const util::AgentInfo* agent_info, hsa_queue_t* queue) :
|
||||
queue_(queue)
|
||||
{}
|
||||
|
||||
void Submit(const packet_t* packet) {
|
||||
// Compute the write index of queue and copy Aql packet into it
|
||||
const uint64_t que_idx = hsa_queue_load_write_index_relaxed(queue_);
|
||||
// Increment the write index
|
||||
hsa_queue_store_write_index_relaxed(queue_, que_idx + 1);
|
||||
|
||||
const uint32_t mask = queue_->size - 1;
|
||||
|
||||
// Copy packet to the queue
|
||||
const packet_word_t* src = reinterpret_cast<const packet_word_t*>(packet);
|
||||
packet_t* slot = reinterpret_cast<packet_t*>(queue_->base_address) + (que_idx & mask);
|
||||
packet_word_t* dst = reinterpret_cast<packet_word_t*>(slot);
|
||||
const uint32_t nwords = sizeof(packet_t) / sizeof(packet_word_t);
|
||||
for (unsigned i = 1; i < nwords; ++i) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
// To maintain global order to ensure the prior copy of the packet contents is made visible
|
||||
// before the header is updated.
|
||||
// With in-order CP it will wait until the first packet in the blob will be valid
|
||||
std::atomic<packet_word_t>* header_atomic_ptr =
|
||||
reinterpret_cast<std::atomic<packet_word_t>*>(&dst[0]);
|
||||
header_atomic_ptr->store(src[0], std::memory_order_release);
|
||||
|
||||
// Doorbell signaling
|
||||
hsa_signal_store_relaxed(queue_->doorbell_signal, que_idx);
|
||||
}
|
||||
|
||||
private:
|
||||
hsa_queue_t* queue_;
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // _SRC_CORE_HSA_QUEUE_H
|
||||
@@ -0,0 +1,15 @@
|
||||
#include "core/intercept_queue.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
void InterceptQueue::HsaIntercept(HsaApiTable* table) {
|
||||
table->core_->hsa_queue_create_fn = rocprofiler::InterceptQueue::QueueCreate;
|
||||
table->core_->hsa_queue_destroy_fn = rocprofiler::InterceptQueue::QueueDestroy;
|
||||
}
|
||||
|
||||
InterceptQueue::mutex_t InterceptQueue::mutex_;
|
||||
rocprofiler_callback_t InterceptQueue::on_dispatch_cb_ = NULL;
|
||||
void* InterceptQueue::on_dispatch_cb_data_ = NULL;
|
||||
const char* InterceptQueue::tool_lib_ = NULL;
|
||||
void* InterceptQueue::tool_handle_ = NULL;
|
||||
InterceptQueue::obj_map_t* InterceptQueue::obj_map_ = NULL;
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,161 @@
|
||||
#ifndef _SRC_CORE_INTERCEPT_QUEUE_H
|
||||
#define _SRC_CORE_INTERCEPT_QUEUE_H
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include "core/context.h"
|
||||
#include "core/proxy_queue.h"
|
||||
#include "core/types.h"
|
||||
#include "util/hsa_rsrc_factory.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
extern decltype(hsa_queue_create)* hsa_queue_create_fn;
|
||||
extern decltype(hsa_queue_destroy)* hsa_queue_destroy_fn;
|
||||
|
||||
class InterceptQueue {
|
||||
public:
|
||||
typedef std::recursive_mutex mutex_t;
|
||||
typedef std::map<uint64_t, InterceptQueue*> obj_map_t;
|
||||
|
||||
static void HsaIntercept(HsaApiTable* table);
|
||||
|
||||
static void SetTool(const char* tool) { tool_lib_ = tool; }
|
||||
|
||||
static void UnloadTool() { if (tool_handle_) dlclose(tool_handle_); }
|
||||
|
||||
static hsa_status_t QueueCreate(
|
||||
hsa_agent_t agent,
|
||||
uint32_t size,
|
||||
hsa_queue_type32_t type,
|
||||
void (*callback)(hsa_status_t status, hsa_queue_t *source, void *data),
|
||||
void *data,
|
||||
uint32_t private_segment_size,
|
||||
uint32_t group_segment_size,
|
||||
hsa_queue_t **queue)
|
||||
{
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
|
||||
hsa_status_t status = HSA_STATUS_ERROR;
|
||||
|
||||
if (tool_lib_) {
|
||||
tool_handle_ = dlopen(tool_lib_, RTLD_NOW);
|
||||
if (tool_handle_ == NULL) {
|
||||
fprintf(stderr, "ROCProfiler: can't load tool library \"%s\"\n", tool_lib_);
|
||||
fprintf(stderr, "%s\n", dlerror());
|
||||
exit(1);
|
||||
}
|
||||
tool_lib_ = NULL;
|
||||
}
|
||||
|
||||
if (!obj_map_) obj_map_ = new obj_map_t;
|
||||
|
||||
ProxyQueue* proxy = ProxyQueue::Create(agent, size, type, callback, data, private_segment_size, group_segment_size, queue, &status);
|
||||
if (status != HSA_STATUS_SUCCESS) {
|
||||
InterceptQueue* obj = new InterceptQueue(agent, proxy);
|
||||
(*obj_map_)[(uint64_t)(*queue)] = obj;
|
||||
status = proxy->SetInterceptCB(OnSubmitCB, obj);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static hsa_status_t QueueDestroy(hsa_queue_t *queue) {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
hsa_status_t status = HSA_STATUS_ERROR;
|
||||
|
||||
obj_map_t::iterator it = obj_map_->find((uint64_t)queue);
|
||||
if (it != obj_map_->end()) {
|
||||
const InterceptQueue* obj = it->second;
|
||||
delete obj;
|
||||
obj_map_->erase(it);
|
||||
status = HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void OnSubmitCB(const void* in_packets, uint64_t count, uint64_t user_que_idx, void* data, hsa_amd_queue_intercept_packet_writer writer) {
|
||||
const packet_t* packets_arr = reinterpret_cast<const packet_t*>(in_packets);
|
||||
InterceptQueue* obj = reinterpret_cast<InterceptQueue*>(data);
|
||||
Queue* proxy = obj->proxy_;
|
||||
|
||||
for (uint64_t j = 0; j < count; ++j) {
|
||||
bool to_submit = true;
|
||||
const packet_t* packet = &packets_arr[j];
|
||||
|
||||
if ((GetHeaderType(packet) == HSA_PACKET_TYPE_KERNEL_DISPATCH) && (on_dispatch_cb_ != NULL)) {
|
||||
rocprofiler_group_t* group = NULL;
|
||||
const hsa_kernel_dispatch_packet_t* dispatch_packet = reinterpret_cast<const hsa_kernel_dispatch_packet_t*>(packet);
|
||||
rocprofiler_callback_data_t data = {dispatch_packet->kernel_object, user_que_idx, obj->agent_info_->dev_index};
|
||||
hsa_status_t status = on_dispatch_cb_(&data, on_dispatch_cb_data_, &group);
|
||||
if ((status == HSA_STATUS_SUCCESS) && (group != NULL)) {
|
||||
Context* context = reinterpret_cast<Context*>(group->context);
|
||||
const pkt_vector_t& start_vector = context->StartPackets(group->index);
|
||||
const pkt_vector_t& stop_vector = context->StopPackets(group->index);
|
||||
|
||||
pkt_vector_t packets = start_vector;
|
||||
packets.insert(packets.end(), *packet);
|
||||
packets.insert(packets.end(), stop_vector.begin(), stop_vector.end());
|
||||
if (writer != NULL) {
|
||||
writer(&packets[0], packets.size());
|
||||
} else {
|
||||
proxy->Submit(&packets[0], packets.size());
|
||||
}
|
||||
to_submit = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (to_submit) {
|
||||
if (writer != NULL) {
|
||||
writer(packet, 1);
|
||||
} else {
|
||||
proxy->Submit(packet, 1);
|
||||
}
|
||||
}
|
||||
|
||||
packet += 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void SetDispatchCB(rocprofiler_callback_t on_dispatch_cb, void* data) {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
on_dispatch_cb_ = on_dispatch_cb;
|
||||
on_dispatch_cb_data_ = data;
|
||||
}
|
||||
|
||||
static void UnsetDispatchCB() {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
on_dispatch_cb_ = NULL;
|
||||
on_dispatch_cb_data_ = NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
InterceptQueue(const hsa_agent_t& agent, ProxyQueue* proxy) : proxy_(proxy) {
|
||||
agent_info_ = util::HsaRsrcFactory::Instance().GetAgentInfo(agent);
|
||||
}
|
||||
~InterceptQueue() { ProxyQueue::Destroy(proxy_); }
|
||||
|
||||
static packet_word_t GetHeaderType(const packet_t* packet) {
|
||||
const packet_word_t* header = reinterpret_cast<const packet_word_t*>(packet);
|
||||
return (*header >> HSA_PACKET_HEADER_TYPE) & header_type_mask;
|
||||
}
|
||||
|
||||
static mutex_t mutex_;
|
||||
static const packet_word_t header_type_mask = (1ul << HSA_PACKET_HEADER_WIDTH_TYPE) - 1;
|
||||
static rocprofiler_callback_t on_dispatch_cb_;
|
||||
static void* on_dispatch_cb_data_;
|
||||
static const char* tool_lib_;
|
||||
static void* tool_handle_;
|
||||
static obj_map_t* obj_map_;
|
||||
|
||||
ProxyQueue* const proxy_;
|
||||
const util::AgentInfo* agent_info_;
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // _SRC_CORE_INTERCEPT_QUEUE_H
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
#ifndef SRC_CORE_METRICS_H_
|
||||
#define SRC_CORE_METRICS_H_
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "core/types.h"
|
||||
#include "util/exception.h"
|
||||
#include "util/hsa_rsrc_factory.h"
|
||||
#include "xml/expr.h"
|
||||
#include "xml/xml.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
struct counter_t {
|
||||
std::string name;
|
||||
event_t event;
|
||||
};
|
||||
typedef std::vector<const counter_t*> counters_vec_t;
|
||||
|
||||
class Metric {
|
||||
public:
|
||||
Metric(const std::string& name) : name_(name) {}
|
||||
std::string GetName() const { return name_; }
|
||||
virtual void GetCounters(counters_vec_t &vec) const = 0;
|
||||
counters_vec_t GetCounters() const {
|
||||
counters_vec_t counters;
|
||||
GetCounters(counters);
|
||||
return counters;
|
||||
}
|
||||
virtual const xml::Expr* GetExpr() const = 0;
|
||||
private:
|
||||
std::string name_;
|
||||
};
|
||||
|
||||
class BaseMetric : public Metric {
|
||||
public:
|
||||
BaseMetric(const std::string& name, const counter_t& counter) : Metric(name), counter_(counter) {}
|
||||
void GetCounters(counters_vec_t &vec) const { vec.push_back(&counter_); }
|
||||
const xml::Expr* GetExpr() const { return NULL; }
|
||||
private:
|
||||
const counter_t counter_;
|
||||
};
|
||||
|
||||
class ExprMetric : public Metric {
|
||||
public:
|
||||
ExprMetric(const std::string& name, const counters_vec_t& counters, const xml::Expr* expr) : Metric(name), counters_(counters), expr_(expr) {}
|
||||
void GetCounters(counters_vec_t& vec) const { vec.insert(vec.end(), counters_.begin(), counters_.end()); }
|
||||
const xml::Expr* GetExpr() const { return expr_; }
|
||||
private:
|
||||
const counters_vec_t counters_;
|
||||
const xml::Expr* expr_;
|
||||
};
|
||||
|
||||
|
||||
class MetricsDict {
|
||||
public:
|
||||
typedef std::map<std::string, const Metric*> cache_t;
|
||||
typedef cache_t::const_iterator const_iterator_t;
|
||||
|
||||
class ExprCache : public xml::expr_cache_t {
|
||||
public:
|
||||
ExprCache(const cache_t* cache) : cache_(cache) {}
|
||||
bool Lookup(const std::string& name, std::string& result) const {
|
||||
bool ret = false;
|
||||
auto it = cache_->find(name);
|
||||
if (it != cache_->end()) {
|
||||
ret = true;
|
||||
const rocprofiler::ExprMetric* expr_metric = dynamic_cast<const rocprofiler::ExprMetric*>(it->second);
|
||||
if (expr_metric) result = expr_metric->GetExpr()->GetStr();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
private:
|
||||
const cache_t* const cache_;
|
||||
};
|
||||
|
||||
MetricsDict(const util::AgentInfo* agent_info) : xml_(NULL) {
|
||||
const char* xml_name = getenv("ROCP_METRICS");
|
||||
if (xml_name != NULL) {
|
||||
xml_ = new xml::Xml(xml_name);
|
||||
std::cout << "ROCProfiler: importing metrics from '" << xml_name << "':" << std::endl;
|
||||
ImportMetrics(agent_info, agent_info->gfxip);
|
||||
ImportMetrics(agent_info, "global");
|
||||
}
|
||||
}
|
||||
|
||||
const Metric* Get(const std::string& name) const {
|
||||
const Metric* metric = NULL;
|
||||
auto it = cache_.find(name);
|
||||
if (it != cache_.end()) metric = it->second;
|
||||
return metric;
|
||||
}
|
||||
|
||||
private:
|
||||
void ImportMetrics(const util::AgentInfo* agent_info, const char* scope) {
|
||||
auto scope_list = xml_->GetNodes("top." + std::string(scope) + ".metric");
|
||||
if (!scope_list.empty()) {
|
||||
std::cout << " " << scope_list.size() << " " << scope << " metrics found" << std::endl;
|
||||
|
||||
for (auto node : scope_list) {
|
||||
const std::string name = node->opts["name"];
|
||||
if (cache_.find(name) != cache_.end()) EXC_RAISING(HSA_STATUS_ERROR, "ImportMetrics: metrics redefined '" << name << "'");
|
||||
|
||||
const std::string expr_str = node->opts["expr"];
|
||||
if (expr_str.empty()) {
|
||||
const std::string block_name = node->opts["block"];
|
||||
const uint32_t event_id = atoi(node->opts["event"].c_str());
|
||||
|
||||
hsa_ven_amd_aqlprofile_profile_t profile;
|
||||
profile.agent = agent_info->dev_id;
|
||||
hsa_ven_amd_aqlprofile_id_query_t query = {block_name.c_str(), 0, 0};
|
||||
hsa_status_t status = util::HsaRsrcFactory::Instance().AqlProfileApi()->
|
||||
hsa_ven_amd_aqlprofile_get_info(&profile, HSA_VEN_AMD_AQLPROFILE_INFO_BLOCK_ID, &query);
|
||||
if (status == HSA_STATUS_SUCCESS) {
|
||||
const hsa_ven_amd_aqlprofile_block_name_t block_id = (hsa_ven_amd_aqlprofile_block_name_t)query.id;
|
||||
if (query.instance_count > 1) {
|
||||
for (unsigned block_index = 0; block_index < query.instance_count; ++block_index) {
|
||||
std::ostringstream os;
|
||||
os << name << '[' << block_index << ']';
|
||||
const std::string full_name = os.str();
|
||||
const counter_t counter = {full_name, {block_id, block_index, event_id}};
|
||||
cache_[full_name] = new BaseMetric(full_name, counter);
|
||||
}
|
||||
} else {
|
||||
const counter_t counter = {name, {block_id, 0, event_id}};
|
||||
cache_[name] = new BaseMetric(name, counter);
|
||||
}
|
||||
} else AQL_EXC_RAISING(HSA_STATUS_ERROR, "ImportMetrics: bad block name '" << block_name << "'");
|
||||
} else {
|
||||
xml::Expr* expr_obj = new xml::Expr(expr_str, new ExprCache(&cache_));
|
||||
std::cout << " " << name << " = " << expr_obj->String() << std::endl;
|
||||
counters_vec_t counters_vec;
|
||||
for (const std::string var : expr_obj->GetVars()) {
|
||||
auto it = cache_.find(var);
|
||||
if (it == cache_.end()) EXC_RAISING(HSA_STATUS_ERROR, "Bad metric '" << name << "', var '" << var << "' is not found");
|
||||
it->second->GetCounters(counters_vec);
|
||||
}
|
||||
cache_[name] = new ExprMetric(name, counters_vec, expr_obj);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
for (auto& v : cache_) {
|
||||
const Metric* metric = v.second;
|
||||
counters_vec_t counters_vec;
|
||||
printf("> Metric '%s'\n", metric->GetName().c_str());
|
||||
metric->GetCounters(counters_vec);
|
||||
for (auto c : counters_vec) {
|
||||
printf(" counter %s, b(%u), i (%u), e (%u)\n", c->name.c_str(), c->event.block_name, c->event.block_index, c->event.counter_id);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Metrics map
|
||||
xml::Xml* xml_;
|
||||
cache_t cache_;
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // SRC_CORE_METRICS_H_
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
#ifndef SRC_CORE_PROFILE_H_
|
||||
#define SRC_CORE_PROFILE_H_
|
||||
|
||||
#include "inc/rocprofiler.h"
|
||||
|
||||
#include <hsa.h>
|
||||
#include <vector>
|
||||
|
||||
#include "core/types.h"
|
||||
#include "util/exception.h"
|
||||
#include "util/hsa_rsrc_factory.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
struct profile_info_t {
|
||||
const event_t* event;
|
||||
const parameter_t* parameters;
|
||||
uint32_t parameter_count;
|
||||
rocprofiler_info_t* rinfo;
|
||||
};
|
||||
typedef std::vector<rocprofiler_info_t*> info_vector_t;
|
||||
typedef std::vector<packet_t> pkt_vector_t;
|
||||
struct profile_tuple_t {
|
||||
const profile_t* profile;
|
||||
info_vector_t* info_vector;
|
||||
hsa_signal_t completion_signal;
|
||||
};
|
||||
typedef std::vector<profile_tuple_t> profile_vector_t;
|
||||
|
||||
template<class Item> class ConfigBase {};
|
||||
|
||||
template<> class ConfigBase<event_t> {
|
||||
public:
|
||||
ConfigBase(profile_t *profile) : profile_(profile) {}
|
||||
|
||||
protected:
|
||||
void* Array() { return const_cast<event_t*>(profile_->events); }
|
||||
unsigned Count() const { return profile_->event_count; }
|
||||
void Set(event_t* events, const unsigned& count) {
|
||||
profile_->events = events;
|
||||
profile_->event_count = count;
|
||||
}
|
||||
profile_t* profile_;
|
||||
};
|
||||
|
||||
template<> class ConfigBase<parameter_t> {
|
||||
public:
|
||||
ConfigBase(profile_t *profile) : profile_(profile) {}
|
||||
|
||||
protected:
|
||||
void* Array() { return const_cast<parameter_t*>(profile_->parameters); }
|
||||
unsigned Count() const { return profile_->parameter_count; }
|
||||
void Set(parameter_t* parameters, const unsigned& count) {
|
||||
profile_->parameters = parameters;
|
||||
profile_->parameter_count = count;
|
||||
}
|
||||
profile_t* profile_;
|
||||
};
|
||||
|
||||
template<class Item>
|
||||
class Config : protected ConfigBase<Item> {
|
||||
typedef ConfigBase<Item> Parent;
|
||||
public:
|
||||
Config(profile_t *profile) : Parent(profile) {}
|
||||
void Insert(const Item& item) {
|
||||
auto count = Parent::Count();
|
||||
count += 1;
|
||||
Item* array = reinterpret_cast<Item*>(realloc(const_cast<void*>(Parent::Array()), count * sizeof(Item)));
|
||||
array[count - 1] = item;
|
||||
Parent::Set(array, count);
|
||||
}
|
||||
};
|
||||
|
||||
class Profile {
|
||||
public:
|
||||
static const uint32_t LEGACY_SLOT_SIZE_PKT = HSA_VEN_AMD_AQLPROFILE_LEGACY_PM4_PACKET_SIZE / sizeof(packet_t);
|
||||
|
||||
Profile(const util::AgentInfo* agent_info) : agent_info_(agent_info) {
|
||||
profile_ = {};
|
||||
profile_.agent = agent_info->dev_id;
|
||||
is_legacy_ = (strncmp(agent_info->name, "gfx8", 4) == 0);
|
||||
}
|
||||
virtual ~Profile() {
|
||||
hsa_memory_free(profile_.command_buffer.ptr);
|
||||
hsa_memory_free(profile_.output_buffer.ptr);
|
||||
free(const_cast<event_t*>(profile_.events));
|
||||
free(const_cast<parameter_t*>(profile_.parameters));
|
||||
}
|
||||
|
||||
virtual void Insert(const profile_info_t& info) {
|
||||
info_vector_.push_back(info.rinfo);
|
||||
}
|
||||
|
||||
hsa_status_t Finalize(pkt_vector_t& start_vector, pkt_vector_t& stop_vector) {
|
||||
hsa_status_t status = HSA_STATUS_SUCCESS;
|
||||
|
||||
if (!info_vector_.empty()) {
|
||||
util::HsaRsrcFactory* rsrc = &util::HsaRsrcFactory::Instance();
|
||||
const pfn_t* api = rsrc->AqlProfileApi();
|
||||
packet_t start{};
|
||||
packet_t stop{};
|
||||
|
||||
// Check the profile buffer sizes
|
||||
status = api->hsa_ven_amd_aqlprofile_start(&profile_, NULL);
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "aqlprofile_start(NULL)");
|
||||
Allocate(rsrc);
|
||||
// Generate start/stop profiling packets
|
||||
status = api->hsa_ven_amd_aqlprofile_start(&profile_, &start);
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "aqlprofile_start");
|
||||
status = api->hsa_ven_amd_aqlprofile_stop(&profile_, &stop);
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "aqlprofile_stop");
|
||||
// Set completion signals
|
||||
hsa_signal_t dummy_signal{};
|
||||
dummy_signal.handle = 0;
|
||||
start.completion_signal = dummy_signal;
|
||||
hsa_signal_t post_signal;
|
||||
status = hsa_signal_create(1, 0, NULL, &post_signal);
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "hsa_signal_create");
|
||||
stop.completion_signal = post_signal;
|
||||
completion_signal_ = post_signal;
|
||||
|
||||
if (is_legacy_) {
|
||||
const uint32_t start_index = start_vector.size();
|
||||
const uint32_t stop_index = stop_vector.size();
|
||||
|
||||
start_vector.insert(start_vector.end(), LEGACY_SLOT_SIZE_PKT, packet_t{});
|
||||
stop_vector.insert(stop_vector.end(), LEGACY_SLOT_SIZE_PKT, packet_t{});
|
||||
status = api->hsa_ven_amd_aqlprofile_legacy_get_pm4(&start, reinterpret_cast<void*>(&start_vector[start_index]));
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "hsa_ven_amd_aqlprofile_legacy_get_pm4");
|
||||
status = api->hsa_ven_amd_aqlprofile_legacy_get_pm4(&stop, reinterpret_cast<void*>(&stop_vector[stop_index]));
|
||||
if (status != HSA_STATUS_SUCCESS) AQL_EXC_RAISING(status, "hsa_ven_amd_aqlprofile_legacy_get_pm4");
|
||||
} else {
|
||||
start_vector.push_back(start);
|
||||
stop_vector.push_back(stop);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void GetProfiles(profile_vector_t& vec) {
|
||||
if (!info_vector_.empty()) {
|
||||
vec.push_back(profile_tuple_t{&profile_, &info_vector_, completion_signal_});
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual hsa_status_t Allocate(util::HsaRsrcFactory* rsrc) = 0;
|
||||
|
||||
const util::AgentInfo* const agent_info_;
|
||||
bool is_legacy_;
|
||||
profile_t profile_;
|
||||
info_vector_t info_vector_;
|
||||
hsa_signal_t completion_signal_;
|
||||
};
|
||||
|
||||
class PmcProfile : public Profile {
|
||||
public:
|
||||
PmcProfile(const util::AgentInfo* agent_info) : Profile(agent_info) {
|
||||
profile_.type = HSA_VEN_AMD_AQLPROFILE_EVENT_TYPE_PMC;
|
||||
}
|
||||
|
||||
void Insert(const profile_info_t& info) {
|
||||
Profile::Insert(info);
|
||||
Config<event_t>(&profile_).Insert(*(info.event));
|
||||
}
|
||||
|
||||
hsa_status_t Allocate(util::HsaRsrcFactory* rsrc) {
|
||||
profile_.command_buffer.ptr = rsrc->AllocateSysMemory(agent_info_, profile_.command_buffer.size);
|
||||
profile_.output_buffer.ptr = rsrc->AllocateSysMemory(agent_info_, profile_.output_buffer.size);
|
||||
return (profile_.command_buffer.ptr && profile_.output_buffer.ptr) ? HSA_STATUS_SUCCESS : HSA_STATUS_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
class SqttProfile : public Profile {
|
||||
public:
|
||||
static const uint32_t output_buffer_size = 0x2000000; // 32M
|
||||
|
||||
SqttProfile(const util::AgentInfo* agent_info) : Profile(agent_info) {
|
||||
profile_.type = HSA_VEN_AMD_AQLPROFILE_EVENT_TYPE_SQTT;
|
||||
}
|
||||
|
||||
void Insert(const profile_info_t& info) {
|
||||
Profile::Insert(info);
|
||||
for (unsigned j = 0; j < info.parameter_count; ++j) {
|
||||
Config<parameter_t>(&profile_).Insert(info.parameters[j]);
|
||||
}
|
||||
|
||||
info.rinfo->data.result_bytes.size = output_buffer_size;
|
||||
if (info.rinfo->data.result_bytes.copy) {
|
||||
const uint32_t output_buffer_size64 = output_buffer_size / sizeof(uint64_t);
|
||||
info.rinfo->data.result_bytes.ptr = calloc(output_buffer_size64, sizeof(uint64_t));
|
||||
memset(info.rinfo->data.result_bytes.ptr, 0, output_buffer_size);
|
||||
}
|
||||
}
|
||||
|
||||
hsa_status_t Allocate(util::HsaRsrcFactory* rsrc) {
|
||||
profile_.output_buffer.size = output_buffer_size;
|
||||
profile_.command_buffer.ptr = rsrc->AllocateSysMemory(agent_info_, profile_.command_buffer.size);
|
||||
profile_.output_buffer.ptr = rsrc->AllocateLocalMemory(agent_info_, profile_.output_buffer.size);
|
||||
return (profile_.command_buffer.ptr && profile_.output_buffer.ptr) ? HSA_STATUS_SUCCESS : HSA_STATUS_ERROR;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // SRC_CORE_PROFILE_H_
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "core/proxy_queue.h"
|
||||
|
||||
#ifdef ROCP_HSA_PROXY
|
||||
#include "core/hsa_proxy_queue.h"
|
||||
#endif
|
||||
#include "core/simple_proxy_queue.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
void ProxyQueue::HsaIntercept(HsaApiTable* table) {
|
||||
if (rocp_type_) SimpleProxyQueue::HsaIntercept(table);
|
||||
}
|
||||
|
||||
ProxyQueue* ProxyQueue::Create(
|
||||
hsa_agent_t agent,
|
||||
uint32_t size,
|
||||
hsa_queue_type32_t type,
|
||||
void (*callback)(hsa_status_t status, hsa_queue_t *source, void *data),
|
||||
void *data,
|
||||
uint32_t private_segment_size,
|
||||
uint32_t group_segment_size,
|
||||
hsa_queue_t **queue,
|
||||
hsa_status_t* status)
|
||||
{
|
||||
hsa_status_t suc = HSA_STATUS_ERROR;
|
||||
#ifdef ROCP_HSA_PROXY
|
||||
ProxyQueue* instance = (rocp_type_) ? (ProxyQueue*) new SimpleProxyQueue() : (ProxyQueue*) new HsaProxyQueue();
|
||||
#else
|
||||
ProxyQueue* instance = new SimpleProxyQueue();
|
||||
#endif
|
||||
if (instance != NULL) {
|
||||
const auto suc = instance->Init(agent, size, type, callback, data, private_segment_size, group_segment_size, queue);
|
||||
if (suc != HSA_STATUS_SUCCESS) {
|
||||
delete instance;
|
||||
instance = NULL;
|
||||
}
|
||||
}
|
||||
*status = suc;
|
||||
return instance;
|
||||
}
|
||||
|
||||
hsa_status_t ProxyQueue::Destroy(const ProxyQueue* obj) {
|
||||
auto suc = obj->Cleanup();
|
||||
delete obj;
|
||||
return suc;
|
||||
}
|
||||
|
||||
bool ProxyQueue::rocp_type_ = false;
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,70 @@
|
||||
#ifndef _SRC_CORE_PROXY_QUEUE_H
|
||||
#define _SRC_CORE_PROXY_QUEUE_H
|
||||
|
||||
#include <hsa.h>
|
||||
#include <hsa_api_trace.h>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include "core/queue.h"
|
||||
#include "core/types.h"
|
||||
|
||||
struct HsaApiTable;
|
||||
|
||||
namespace rocprofiler {
|
||||
typedef void (*hsa_amd_queue_intercept_packet_writer)(const void* packets, uint64_t count);
|
||||
typedef void (*on_submit_cb_t)(const void* packet, uint64_t count, uint64_t que_idx, void* data, hsa_amd_queue_intercept_packet_writer writer);
|
||||
|
||||
class ProxyQueue : public Queue {
|
||||
public:
|
||||
static void InitFactory() {
|
||||
#ifdef ROCP_HSA_PROXY
|
||||
const char* type = getenv("ROCP_PROXY_QUEUE");
|
||||
if (type != NULL) {
|
||||
if (strncmp(type, "rocp", 4) == 0) rocp_type_ = true;
|
||||
}
|
||||
#else
|
||||
rocp_type_ = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void HsaIntercept(HsaApiTable* table);
|
||||
|
||||
static ProxyQueue* Create(
|
||||
hsa_agent_t agent,
|
||||
uint32_t size,
|
||||
hsa_queue_type32_t type,
|
||||
void (*callback)(hsa_status_t status, hsa_queue_t *source, void *data),
|
||||
void *data,
|
||||
uint32_t private_segment_size,
|
||||
uint32_t group_segment_size,
|
||||
hsa_queue_t **queue,
|
||||
hsa_status_t* status);
|
||||
|
||||
static hsa_status_t Destroy(const ProxyQueue* obj);
|
||||
|
||||
virtual hsa_status_t Init(
|
||||
hsa_agent_t agent,
|
||||
uint32_t size,
|
||||
hsa_queue_type32_t type,
|
||||
void (*callback)(hsa_status_t status, hsa_queue_t *source, void *data),
|
||||
void *data,
|
||||
uint32_t private_segment_size,
|
||||
uint32_t group_segment_size,
|
||||
hsa_queue_t **queue
|
||||
) = 0;
|
||||
virtual hsa_status_t Cleanup() const = 0;
|
||||
virtual hsa_status_t SetInterceptCB(on_submit_cb_t on_submit_cb, void* data) = 0;
|
||||
virtual void Submit(const packet_t* packet) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ProxyQueue() {};
|
||||
|
||||
private:
|
||||
static bool rocp_type_;
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // _SRC_CORE_PROXY_QUEUE_H
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef _SRC_CORE_QUEUE_H
|
||||
#define _SRC_CORE_QUEUE_H
|
||||
|
||||
#include "core/types.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
|
||||
class Queue {
|
||||
public:
|
||||
Queue() {}
|
||||
virtual ~Queue() {}
|
||||
virtual void Submit(const packet_t* packet) = 0;
|
||||
virtual void Submit(const packet_t* packet, const size_t& count) {
|
||||
for (const packet_t* p = packet; p < packet + count; ++p) Submit(p);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // _SRC_CORE_QUEUE_H
|
||||
@@ -0,0 +1,251 @@
|
||||
#include "inc/rocprofiler.h"
|
||||
|
||||
#include <hsa.h>
|
||||
#include <hsa_api_trace.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "core/context.h"
|
||||
#include "core/hsa_queue.h"
|
||||
#include "core/intercept_queue.h"
|
||||
#include "core/proxy_queue.h"
|
||||
#include "core/simple_proxy_queue.h"
|
||||
#include "util/exception.h"
|
||||
#include "util/hsa_rsrc_factory.h"
|
||||
#include "util/logger.h"
|
||||
|
||||
#define PUBLIC_API __attribute__((visibility("default")))
|
||||
#define CONSTRUCTOR_API __attribute__((constructor))
|
||||
#define DESTRUCTOR_API __attribute__((destructor))
|
||||
|
||||
#define API_METHOD_PREFIX \
|
||||
hsa_status_t status = HSA_STATUS_SUCCESS; \
|
||||
try {
|
||||
|
||||
#define API_METHOD_SUFFIX \
|
||||
} catch (std::exception& e) { \
|
||||
ERR_LOGGING(__FUNCTION__ << "(), " << e.what()); \
|
||||
status = rocprofiler::GetExcStatus(e); \
|
||||
} \
|
||||
return status;
|
||||
|
||||
namespace rocprofiler {
|
||||
decltype(hsa_queue_create)* hsa_queue_create_fn;
|
||||
decltype(hsa_queue_destroy)* hsa_queue_destroy_fn;
|
||||
decltype(hsa_signal_store_relaxed)* hsa_signal_store_relaxed_fn;
|
||||
decltype(hsa_queue_load_write_index_relaxed)* hsa_queue_load_write_index_relaxed_fn;
|
||||
decltype(hsa_queue_store_write_index_relaxed)* hsa_queue_store_write_index_relaxed_fn;
|
||||
#ifdef ROCP_HSA_PROXY
|
||||
decltype(hsa_amd_queue_intercept_create)* hsa_amd_queue_intercept_create_fn;
|
||||
decltype(hsa_amd_queue_intercept_register)* hsa_amd_queue_intercept_register_fn;
|
||||
#endif
|
||||
|
||||
::HsaApiTable* kHsaApiTable;
|
||||
|
||||
void SaveHsaApi(::HsaApiTable* table) {
|
||||
kHsaApiTable = table;
|
||||
hsa_queue_create_fn = table->core_->hsa_queue_create_fn;
|
||||
hsa_queue_destroy_fn = table->core_->hsa_queue_destroy_fn;
|
||||
hsa_signal_store_relaxed_fn = table->core_->hsa_signal_store_relaxed_fn;
|
||||
hsa_queue_load_write_index_relaxed_fn = table->core_->hsa_queue_load_write_index_relaxed_fn;
|
||||
hsa_queue_store_write_index_relaxed_fn = table->core_->hsa_queue_store_write_index_relaxed_fn;
|
||||
#ifdef ROCP_HSA_PROXY
|
||||
hsa_amd_queue_intercept_create_fn = table->amd_ext_->hsa_amd_queue_intercept_create_fn;
|
||||
hsa_amd_queue_intercept_register_fn = table->amd_ext_->hsa_amd_queue_intercept_register_fn;
|
||||
#endif
|
||||
}
|
||||
|
||||
void RestoreHsaApi() {
|
||||
::HsaApiTable* table = kHsaApiTable;
|
||||
table->core_->hsa_queue_create_fn = hsa_queue_create_fn;
|
||||
table->core_->hsa_queue_destroy_fn = hsa_queue_destroy_fn;
|
||||
table->core_->hsa_signal_store_relaxed_fn = hsa_signal_store_relaxed_fn;
|
||||
table->core_->hsa_queue_load_write_index_relaxed_fn = hsa_queue_load_write_index_relaxed_fn;
|
||||
table->core_->hsa_queue_store_write_index_relaxed_fn = hsa_queue_store_write_index_relaxed_fn;
|
||||
#ifdef ROCP_HSA_PROXY
|
||||
table->amd_ext_->hsa_amd_queue_intercept_create_fn = hsa_amd_queue_intercept_create_fn;
|
||||
table->amd_ext_->hsa_amd_queue_intercept_register_fn = hsa_amd_queue_intercept_register_fn;
|
||||
#endif
|
||||
}
|
||||
|
||||
CONSTRUCTOR_API void constructor() {
|
||||
util::Logger::Create();
|
||||
util::HsaRsrcFactory::Create();
|
||||
}
|
||||
|
||||
DESTRUCTOR_API void destructor() {
|
||||
util::HsaRsrcFactory::Destroy();
|
||||
util::Logger::Destroy();
|
||||
}
|
||||
|
||||
hsa_status_t GetExcStatus(const std::exception& e) {
|
||||
const util::exception* rocprofiler_exc_ptr = dynamic_cast<const util::exception*>(&e);
|
||||
return (rocprofiler_exc_ptr) ? static_cast<hsa_status_t>(rocprofiler_exc_ptr->status()) : HSA_STATUS_ERROR;
|
||||
}
|
||||
|
||||
util::Logger::mutex_t util::Logger::mutex_;
|
||||
util::Logger* util::Logger::instance_ = NULL;
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Returns the last error message
|
||||
PUBLIC_API hsa_status_t rocprofiler_error_string(const char** str) {
|
||||
API_METHOD_PREFIX
|
||||
*str = rocprofiler::util::Logger::LastMessage().c_str();
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Create new profiling context
|
||||
PUBLIC_API hsa_status_t rocprofiler_open(
|
||||
unsigned agent_id,
|
||||
rocprofiler_info_t* info,
|
||||
uint32_t info_count,
|
||||
rocprofiler_t** handle,
|
||||
uint32_t mode,
|
||||
rocprofiler_properties_t* properties)
|
||||
{
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::util::HsaRsrcFactory* hsa_rsrc = &rocprofiler::util::HsaRsrcFactory::Instance();
|
||||
const rocprofiler::util::AgentInfo* agent_info;
|
||||
if (!hsa_rsrc->GetGpuAgentInfo(agent_id, &agent_info)) {
|
||||
EXC_RAISING(HSA_STATUS_ERROR, "agent[" << agent_id << "] is not found");
|
||||
}
|
||||
|
||||
rocprofiler::Queue* queue = NULL;
|
||||
if (mode != 0) {
|
||||
if (mode & ROCPROFILER_MODE_STANDALONE) {
|
||||
if (mode & ROCPROFILER_MODE_CREATEQUEUE) {
|
||||
if (hsa_rsrc->CreateQueue(agent_info, properties->queue_depth, &(properties->queue)) == false) {
|
||||
EXC_RAISING(HSA_STATUS_ERROR, "CreateQueue() failed");
|
||||
}
|
||||
}
|
||||
queue = new rocprofiler::HsaQueue(agent_info, properties->queue);
|
||||
} else {
|
||||
EXC_RAISING(HSA_STATUS_ERROR, "invalid mode (" << mode << ")");
|
||||
}
|
||||
}
|
||||
|
||||
*handle = (void*) new rocprofiler::Context(agent_info, queue, info, info_count);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Delete profiling info
|
||||
PUBLIC_API hsa_status_t rocprofiler_close(rocprofiler_t* handle)
|
||||
{
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::Context* context = reinterpret_cast<rocprofiler::Context*>(handle);
|
||||
if (context) delete context;
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Get profiling groups
|
||||
PUBLIC_API hsa_status_t rocprofiler_get_groups(rocprofiler_t* handle, rocprofiler_group_t** group_array, uint32_t* group_count) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::Context* context = reinterpret_cast<rocprofiler::Context*>(handle);
|
||||
const uint32_t count = context->GetGroupCount();
|
||||
rocprofiler_group_t* groups = (rocprofiler_group_t*) calloc(count, sizeof(rocprofiler_group_t));
|
||||
for (unsigned i = 0; i < count; ++i) groups[i] = context->GetGroupInfo(i);
|
||||
*group_array = groups;
|
||||
*group_count = count;
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Start profiling
|
||||
PUBLIC_API hsa_status_t rocprofiler_start(rocprofiler_t* handle, uint32_t group_index) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::Context* context = reinterpret_cast<rocprofiler::Context*>(handle);
|
||||
context->Start(group_index);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Stop profiling
|
||||
PUBLIC_API hsa_status_t rocprofiler_stop(rocprofiler_t* handle, uint32_t group_index) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::Context* context = reinterpret_cast<rocprofiler::Context*>(handle);
|
||||
context->Stop(group_index);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Get profiling data
|
||||
PUBLIC_API hsa_status_t rocprofiler_get_data(rocprofiler_t* handle, uint32_t group_index) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::Context* context = reinterpret_cast<rocprofiler::Context*>(handle);
|
||||
context->GetData(group_index);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Start profiling
|
||||
PUBLIC_API hsa_status_t rocprofiler_group_start(rocprofiler_group_t* group) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler_start(group->context, group->index);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Stop profiling
|
||||
PUBLIC_API hsa_status_t rocprofiler_group_stop(rocprofiler_group_t* group) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler_stop(group->context, group->index);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Get profiling data
|
||||
PUBLIC_API hsa_status_t rocprofiler_get_group_data(rocprofiler_group_t* group) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::Context* context = reinterpret_cast<rocprofiler::Context*>(group->context);
|
||||
context->GetData(group->index);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Get metrics data
|
||||
PUBLIC_API hsa_status_t rocprofiler_get_metrics_data(const rocprofiler_t* handle) {
|
||||
API_METHOD_PREFIX
|
||||
const rocprofiler::Context* context = reinterpret_cast<const rocprofiler::Context*>(handle);
|
||||
context->GetMetricsData();
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Set kernel dispatch observer
|
||||
PUBLIC_API hsa_status_t rocprofiler_set_dispatch_observer(rocprofiler_callback_t callback, void* data) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::InterceptQueue::SetDispatchCB(callback, data);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Set kernel dispatch observer
|
||||
PUBLIC_API hsa_status_t rocprofiler_remove_dispatch_observer() {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::InterceptQueue::UnsetDispatchCB();
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
// Method for iterating the events output data
|
||||
PUBLIC_API hsa_status_t rocprofiler_iterate_trace_data(rocprofiler_t* handle, hsa_ven_amd_aqlprofile_data_callback_t callback, void* data) {
|
||||
API_METHOD_PREFIX
|
||||
rocprofiler::Context* context = reinterpret_cast<rocprofiler::Context*>(handle);
|
||||
context->IterateTraceData(callback, data);
|
||||
API_METHOD_SUFFIX
|
||||
}
|
||||
|
||||
PUBLIC_API bool OnLoad(
|
||||
HsaApiTable* table,
|
||||
uint64_t runtime_version,
|
||||
uint64_t failed_tool_count,
|
||||
const char* const * failed_tool_names) {
|
||||
rocprofiler::SaveHsaApi(table);
|
||||
rocprofiler::ProxyQueue::InitFactory();
|
||||
rocprofiler::InterceptQueue::SetTool(getenv("ROCP_TOOL_LIB"));
|
||||
// HSA intercepting
|
||||
if (getenv("ROCP_HSA_INTERCEPT") != NULL) {
|
||||
rocprofiler::InterceptQueue::HsaIntercept(table);
|
||||
rocprofiler::ProxyQueue::HsaIntercept(table);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PUBLIC_API void OnUnload() {
|
||||
rocprofiler::RestoreHsaApi();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "core/simple_proxy_queue.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
void SimpleProxyQueue::HsaIntercept(HsaApiTable* table) {
|
||||
table->core_->hsa_signal_store_relaxed_fn = rocprofiler::SimpleProxyQueue::SignalStore;
|
||||
table->core_->hsa_queue_load_write_index_relaxed_fn = rocprofiler::SimpleProxyQueue::LoadIndex;
|
||||
table->core_->hsa_queue_store_write_index_relaxed_fn = rocprofiler::SimpleProxyQueue::StoreIndex;
|
||||
}
|
||||
|
||||
std::map<signal_handle_t, SimpleProxyQueue*> SimpleProxyQueue::queue_map_;
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,187 @@
|
||||
#ifndef _SRC_CORE_SIMPLE_PROXY_QUEUE_H
|
||||
#define _SRC_CORE_SIMPLE_PROXY_QUEUE_H
|
||||
|
||||
#include <hsa.h>
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
#include "core/proxy_queue.h"
|
||||
#include "core/types.h"
|
||||
#include "util/hsa_rsrc_factory.h"
|
||||
|
||||
namespace rocprofiler {
|
||||
extern decltype(hsa_queue_create)* hsa_queue_create_fn;
|
||||
extern decltype(hsa_queue_destroy)* hsa_queue_destroy_fn;
|
||||
extern decltype(hsa_signal_store_relaxed)* hsa_signal_store_relaxed_fn;
|
||||
extern decltype(hsa_queue_load_write_index_relaxed)* hsa_queue_load_write_index_relaxed_fn;
|
||||
extern decltype(hsa_queue_store_write_index_relaxed)* hsa_queue_store_write_index_relaxed_fn;
|
||||
typedef decltype(hsa_signal_t::handle) signal_handle_t;
|
||||
|
||||
|
||||
class SimpleProxyQueue : public ProxyQueue {
|
||||
public:
|
||||
static void HsaIntercept(HsaApiTable* table);
|
||||
|
||||
static void SignalStore(
|
||||
hsa_signal_t signal,
|
||||
hsa_signal_value_t que_idx)
|
||||
{
|
||||
auto it = queue_map_.find(signal.handle);
|
||||
if (it != queue_map_.end()) {
|
||||
SimpleProxyQueue* instance = it->second;
|
||||
const uint64_t begin = instance->submit_index_;
|
||||
const uint64_t end = que_idx + 1;
|
||||
instance->submit_index_ = end;
|
||||
for (uint64_t j = begin; j < end; ++j) {
|
||||
// Submited packet
|
||||
const uint32_t idx = j & instance->queue_mask_;
|
||||
packet_t* packet = reinterpret_cast<packet_t*>(instance->queue_->base_address) + idx;
|
||||
if (instance->on_submit_cb_ != NULL) instance->on_submit_cb_(packet, 1, j, instance->on_submit_cb_data_, NULL);
|
||||
else instance->Submit(packet);
|
||||
}
|
||||
} else {
|
||||
hsa_signal_store_relaxed_fn(signal, que_idx);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t LoadIndex(
|
||||
const hsa_queue_t *queue)
|
||||
{
|
||||
uint64_t index = 0;
|
||||
auto it = queue_map_.find(queue->doorbell_signal.handle);
|
||||
if (it != queue_map_.end()) {
|
||||
SimpleProxyQueue* instance = it->second;
|
||||
instance->mutex_.lock();
|
||||
index = instance->queue_index_;
|
||||
} else {
|
||||
index = hsa_queue_load_write_index_relaxed_fn(queue);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
static void StoreIndex(
|
||||
const hsa_queue_t *queue,
|
||||
uint64_t value)
|
||||
{
|
||||
auto it = queue_map_.find(queue->doorbell_signal.handle);
|
||||
if (it != queue_map_.end()) {
|
||||
SimpleProxyQueue* instance = it->second;
|
||||
instance->queue_index_ = value;
|
||||
instance->mutex_.unlock();
|
||||
} else {
|
||||
hsa_queue_store_write_index_relaxed_fn(queue, value);
|
||||
}
|
||||
}
|
||||
|
||||
hsa_status_t SetInterceptCB(on_submit_cb_t on_submit_cb, void* data) {
|
||||
on_submit_cb_ = on_submit_cb;
|
||||
on_submit_cb_data_ = data;
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void Submit(const packet_t* packet) {
|
||||
// Compute the write index of queue and copy Aql packet into it
|
||||
const uint64_t que_idx = hsa_queue_load_write_index_relaxed_fn(queue_);
|
||||
// Increment the write index and ring the doorbell to submit the packet.
|
||||
hsa_queue_store_write_index_relaxed_fn(queue_, que_idx + 1);
|
||||
|
||||
const uint32_t mask = queue_->size - 1;
|
||||
const uint32_t idx = que_idx & mask;
|
||||
|
||||
// Copy packet to the queue
|
||||
const packet_word_t* src = reinterpret_cast<const packet_word_t*>(packet);
|
||||
packet_word_t* dst = reinterpret_cast<packet_word_t*>(base_address_ + idx);
|
||||
for (unsigned i = 1; i < sizeof(packet_t) / sizeof(packet_word_t); ++i) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
|
||||
// To maintain global order to ensure the prior copy of the packet contents is made visible
|
||||
// before the header is updated.
|
||||
// With in-order CP it will wait until the first packet in the blob will be valid
|
||||
std::atomic<packet_word_t>* header_atomic_ptr =
|
||||
reinterpret_cast<std::atomic<packet_word_t>*>(&dst[0]);
|
||||
header_atomic_ptr->store(src[0], std::memory_order_release);
|
||||
|
||||
// Doorbell signaling
|
||||
hsa_signal_store_relaxed_fn(doorbell_signal_, que_idx);
|
||||
}
|
||||
|
||||
SimpleProxyQueue() :
|
||||
agent_info_(NULL),
|
||||
queue_(NULL),
|
||||
base_address_(NULL),
|
||||
doorbell_signal_({}),
|
||||
queue_index_(0),
|
||||
queue_mask_(0),
|
||||
submit_index_(0),
|
||||
on_submit_cb_(0),
|
||||
on_submit_cb_data_(0)
|
||||
{}
|
||||
|
||||
~SimpleProxyQueue() {}
|
||||
|
||||
private:
|
||||
hsa_status_t Init(
|
||||
hsa_agent_t agent,
|
||||
uint32_t size,
|
||||
hsa_queue_type32_t type,
|
||||
void (*callback)(hsa_status_t status, hsa_queue_t *source, void *data),
|
||||
void *data,
|
||||
uint32_t private_segment_size,
|
||||
uint32_t group_segment_size,
|
||||
hsa_queue_t **queue)
|
||||
{
|
||||
auto status = Init(agent, size);
|
||||
*queue = queue_;
|
||||
return status;
|
||||
}
|
||||
|
||||
hsa_status_t Init(hsa_agent_t agent, uint32_t size) {
|
||||
hsa_status_t status = HSA_STATUS_ERROR;
|
||||
agent_info_ = util::HsaRsrcFactory::Instance().GetAgentInfo(agent);
|
||||
if (agent_info_ != NULL) {
|
||||
if (agent_info_->dev_type == HSA_DEVICE_TYPE_GPU) {
|
||||
status = hsa_queue_create_fn(agent, size, HSA_QUEUE_TYPE_MULTI, NULL, NULL, UINT32_MAX, UINT32_MAX, &queue_);
|
||||
if (status == HSA_STATUS_SUCCESS) {
|
||||
base_address_ = reinterpret_cast<packet_t*>(queue_->base_address);
|
||||
doorbell_signal_ = queue_->doorbell_signal;
|
||||
data_array_ = calloc(size + 1, sizeof(packet_t));
|
||||
uintptr_t addr = (uintptr_t)data_array_;
|
||||
queue_->base_address = (void*) ((addr + align_mask_) & ~align_mask_);
|
||||
status = hsa_signal_create(1, 0, NULL, &(queue_->doorbell_signal));
|
||||
queue_mask_ = size - 1;
|
||||
queue_map_[queue_->doorbell_signal.handle] = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
hsa_status_t Cleanup() const {
|
||||
hsa_status_t status = HSA_STATUS_SUCCESS;
|
||||
queue_->base_address = base_address_;
|
||||
queue_->doorbell_signal = doorbell_signal_;
|
||||
status = hsa_queue_destroy_fn(queue_);
|
||||
free(data_array_);
|
||||
return status;
|
||||
}
|
||||
|
||||
static std::map<signal_handle_t, SimpleProxyQueue*> queue_map_;
|
||||
const util::AgentInfo* agent_info_;
|
||||
hsa_queue_t* queue_;
|
||||
static const uintptr_t align_mask_ = sizeof(packet_t) - 1;
|
||||
packet_t* base_address_;
|
||||
hsa_signal_t doorbell_signal_;
|
||||
uint64_t queue_index_;
|
||||
uint64_t queue_mask_;
|
||||
uint64_t submit_index_;
|
||||
std::mutex mutex_;
|
||||
on_submit_cb_t on_submit_cb_;
|
||||
void* on_submit_cb_data_;
|
||||
void* data_array_;
|
||||
};
|
||||
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // _SRC_CORE_SIMPLE_PROXY_QUEUE_H
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef SRC_CORE_TYPES_H_
|
||||
#define SRC_CORE_TYPES_H_
|
||||
|
||||
#include <hsa_ven_amd_aqlprofile.h>
|
||||
|
||||
namespace rocprofiler {
|
||||
typedef hsa_ven_amd_aqlprofile_1_00_pfn_t pfn_t;
|
||||
typedef hsa_ven_amd_aqlprofile_event_t event_t;
|
||||
typedef hsa_ven_amd_aqlprofile_parameter_t parameter_t;
|
||||
typedef hsa_ven_amd_aqlprofile_profile_t profile_t;
|
||||
typedef hsa_ext_amd_aql_pm4_packet_t packet_t;
|
||||
typedef uint32_t packet_word_t;
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // SRC_CORE_TYPES_H_
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef SRC_UTIL_EXCEPTION_H_
|
||||
#define SRC_UTIL_EXCEPTION_H_
|
||||
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
#include <hsa_ven_amd_aqlprofile.h>
|
||||
|
||||
#define EXC_RAISING(error, stream) { \
|
||||
std::ostringstream oss; oss << __FUNCTION__ << "(), " << stream; \
|
||||
throw rocprofiler::util::exception(error, oss.str()); \
|
||||
}
|
||||
|
||||
#define AQL_EXC_RAISING(error, stream) { \
|
||||
const char* error_string = NULL; \
|
||||
const rocprofiler::pfn_t* api = util::HsaRsrcFactory::Instance().AqlProfileApi(); \
|
||||
api->hsa_ven_amd_aqlprofile_error_string(&error_string); \
|
||||
EXC_RAISING(error, stream << ", " << error_string); \
|
||||
}
|
||||
|
||||
namespace rocprofiler {
|
||||
namespace util {
|
||||
|
||||
class exception : public std::exception {
|
||||
public:
|
||||
explicit exception(const uint32_t &status, const std::string& msg) : status_(status), str_(msg) {}
|
||||
const char* what() const throw() { return str_.c_str(); }
|
||||
uint32_t status() const throw() { return status_; }
|
||||
|
||||
protected:
|
||||
const uint32_t status_;
|
||||
const std::string str_;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // SRC_UTIL_EXCEPTION_H_
|
||||
@@ -0,0 +1,392 @@
|
||||
/**********************************************************************
|
||||
Copyright ©2013 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
<95> Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
<95> Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
********************************************************************/
|
||||
|
||||
#include "util/hsa_rsrc_factory.h"
|
||||
|
||||
#include <hsa.h>
|
||||
#include <hsa_ext_finalize.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace rocprofiler {
|
||||
namespace util {
|
||||
|
||||
// Callback function to get available in the system agents
|
||||
hsa_status_t HsaRsrcFactory::GetHsaAgentsCallback(hsa_agent_t agent, void* data) {
|
||||
hsa_status_t status = HSA_STATUS_ERROR;
|
||||
HsaRsrcFactory* hsa_rsrc = reinterpret_cast<HsaRsrcFactory*>(data);
|
||||
const AgentInfo* agent_info = hsa_rsrc->AddAgentInfo(agent);
|
||||
if (agent_info != NULL) status = HSA_STATUS_SUCCESS;
|
||||
return status;
|
||||
}
|
||||
|
||||
// Callback function to find and bind kernarg region of an agent
|
||||
hsa_status_t HsaRsrcFactory::FindMemRegionsCallback(hsa_region_t region, void* data) {
|
||||
hsa_region_global_flag_t flags;
|
||||
hsa_region_segment_t segment_id;
|
||||
|
||||
hsa_region_get_info(region, HSA_REGION_INFO_SEGMENT, &segment_id);
|
||||
if (segment_id != HSA_REGION_SEGMENT_GLOBAL) {
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
AgentInfo* agent_info = (AgentInfo*)data;
|
||||
hsa_region_get_info(region, HSA_REGION_INFO_GLOBAL_FLAGS, &flags);
|
||||
if (flags & HSA_REGION_GLOBAL_FLAG_COARSE_GRAINED) {
|
||||
agent_info->coarse_region = region;
|
||||
}
|
||||
|
||||
if (flags & HSA_REGION_GLOBAL_FLAG_KERNARG) {
|
||||
agent_info->kernarg_region = region;
|
||||
}
|
||||
|
||||
return HSA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Constructor of the class
|
||||
HsaRsrcFactory::HsaRsrcFactory() {
|
||||
// Initialize the Hsa Runtime
|
||||
hsa_status_t status = hsa_init();
|
||||
CHECK_STATUS("Error in hsa_init", status);
|
||||
|
||||
// Discover the set of Gpu devices available on the platform
|
||||
status = hsa_iterate_agents(GetHsaAgentsCallback, this);
|
||||
CHECK_STATUS("Error Calling hsa_iterate_agents", status);
|
||||
|
||||
// Get AqlProfile API table
|
||||
status = hsa_system_get_extension_table(HSA_EXTENSION_AMD_AQLPROFILE, 1, 0, &aqlprofile_api_);
|
||||
CHECK_STATUS("aqlprofile API table query failed", status);
|
||||
}
|
||||
|
||||
// Destructor of the class
|
||||
HsaRsrcFactory::~HsaRsrcFactory() {
|
||||
hsa_status_t status = hsa_shut_down();
|
||||
CHECK_STATUS("Error in hsa_shut_down", status);
|
||||
}
|
||||
|
||||
// Add system agent info
|
||||
const AgentInfo* HsaRsrcFactory::AddAgentInfo(const hsa_agent_t agent) {
|
||||
// Determine if device is a Gpu agent
|
||||
hsa_status_t status;
|
||||
AgentInfo* agent_info = NULL;
|
||||
|
||||
hsa_device_type_t type;
|
||||
status = hsa_agent_get_info(agent, HSA_AGENT_INFO_DEVICE, &type);
|
||||
CHECK_STATUS("Error Calling hsa_agent_get_info", status);
|
||||
|
||||
if (type == HSA_DEVICE_TYPE_CPU) {
|
||||
agent_info = new AgentInfo{};
|
||||
agent_info->dev_id = agent;
|
||||
agent_info->dev_type = HSA_DEVICE_TYPE_CPU;
|
||||
agent_info->dev_index = cpu_list_.size();
|
||||
cpu_list_.push_back(agent_info);
|
||||
}
|
||||
|
||||
if (type == HSA_DEVICE_TYPE_GPU) {
|
||||
agent_info = new AgentInfo{};
|
||||
agent_info->dev_id = agent;
|
||||
agent_info->dev_type = HSA_DEVICE_TYPE_GPU;
|
||||
hsa_agent_get_info(agent, HSA_AGENT_INFO_NAME, agent_info->name);
|
||||
strncpy(agent_info->gfxip, agent_info->name, 4);
|
||||
agent_info->gfxip[4] = '\0';
|
||||
agent_info->max_wave_size = 0;
|
||||
hsa_agent_get_info(agent, HSA_AGENT_INFO_WAVEFRONT_SIZE, &agent_info->max_wave_size);
|
||||
agent_info->max_queue_size = 0;
|
||||
hsa_agent_get_info(agent, HSA_AGENT_INFO_QUEUE_MAX_SIZE, &agent_info->max_queue_size);
|
||||
agent_info->profile = hsa_profile_t(108);
|
||||
hsa_agent_get_info(agent, HSA_AGENT_INFO_PROFILE, &agent_info->profile);
|
||||
|
||||
// Initialize memory regions to zero
|
||||
agent_info->kernarg_region.handle = 0;
|
||||
agent_info->coarse_region.handle = 0;
|
||||
|
||||
// Find and Bind Memory regions of the Gpu agent
|
||||
hsa_agent_iterate_regions(agent, FindMemRegionsCallback, agent_info);
|
||||
|
||||
// Set GPU index
|
||||
agent_info->dev_index = gpu_list_.size();
|
||||
gpu_list_.push_back(agent_info);
|
||||
}
|
||||
|
||||
if (agent_info) agent_map_[agent.handle] = agent_info;
|
||||
|
||||
return agent_info;
|
||||
}
|
||||
|
||||
// Return systen agent info
|
||||
const AgentInfo* HsaRsrcFactory::GetAgentInfo(const hsa_agent_t agent) {
|
||||
const AgentInfo* agent_info = NULL;
|
||||
auto it = agent_map_.find(agent.handle);
|
||||
if (it != agent_map_.end()) {
|
||||
agent_info = it->second;
|
||||
}
|
||||
return agent_info;
|
||||
}
|
||||
|
||||
// Get the count of Hsa Gpu Agents available on the platform
|
||||
//
|
||||
// @return uint32_t Number of Gpu agents on platform
|
||||
//
|
||||
uint32_t HsaRsrcFactory::GetCountOfGpuAgents() { return uint32_t(gpu_list_.size()); }
|
||||
|
||||
// Get the count of Hsa Cpu Agents available on the platform
|
||||
//
|
||||
// @return uint32_t Number of Cpu agents on platform
|
||||
//
|
||||
uint32_t HsaRsrcFactory::GetCountOfCpuAgents() { return uint32_t(cpu_list_.size()); }
|
||||
|
||||
// Get the AgentInfo handle of a Gpu device
|
||||
//
|
||||
// @param idx Gpu Agent at specified index
|
||||
//
|
||||
// @param agent_info Output parameter updated with AgentInfo
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool HsaRsrcFactory::GetGpuAgentInfo(uint32_t idx, const AgentInfo** agent_info) {
|
||||
// Determine if request is valid
|
||||
uint32_t size = uint32_t(gpu_list_.size());
|
||||
if (idx >= size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy AgentInfo from specified index
|
||||
*agent_info = gpu_list_[idx];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get the AgentInfo handle of a Cpu device
|
||||
//
|
||||
// @param idx Cpu Agent at specified index
|
||||
//
|
||||
// @param agent_info Output parameter updated with AgentInfo
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool HsaRsrcFactory::GetCpuAgentInfo(uint32_t idx, const AgentInfo** agent_info) {
|
||||
// Determine if request is valid
|
||||
uint32_t size = uint32_t(cpu_list_.size());
|
||||
if (idx >= size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Copy AgentInfo from specified index
|
||||
*agent_info = cpu_list_[idx];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a Queue object and return its handle. The queue object is expected
|
||||
// to support user requested number of Aql dispatch packets.
|
||||
//
|
||||
// @param agent_info Gpu Agent on which to create a queue object
|
||||
//
|
||||
// @param num_Pkts Number of packets to be held by queue
|
||||
//
|
||||
// @param queue Output parameter updated with handle of queue object
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool HsaRsrcFactory::CreateQueue(const AgentInfo* agent_info, uint32_t num_pkts, hsa_queue_t** queue) {
|
||||
hsa_status_t status;
|
||||
status = hsa_queue_create(agent_info->dev_id, num_pkts, HSA_QUEUE_TYPE_MULTI, NULL, NULL,
|
||||
UINT32_MAX, UINT32_MAX, queue);
|
||||
return (status == HSA_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// Create a Signal object and return its handle.
|
||||
//
|
||||
// @param value Initial value of signal object
|
||||
//
|
||||
// @param signal Output parameter updated with handle of signal object
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool HsaRsrcFactory::CreateSignal(uint32_t value, hsa_signal_t* signal) {
|
||||
hsa_status_t status;
|
||||
status = hsa_signal_create(value, 0, NULL, signal);
|
||||
return (status == HSA_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// Allocate memory for use by a kernel of specified size in specified
|
||||
// agent's memory region. Currently supports Global segment whose Kernarg
|
||||
// flag set.
|
||||
//
|
||||
// @param agent_info Agent from whose memory region to allocate
|
||||
//
|
||||
// @param size Size of memory in terms of bytes
|
||||
//
|
||||
// @return uint8_t* Pointer to buffer, null if allocation fails.
|
||||
//
|
||||
uint8_t* HsaRsrcFactory::AllocateLocalMemory(const AgentInfo* agent_info, size_t size) {
|
||||
hsa_status_t status;
|
||||
uint8_t* buffer = NULL;
|
||||
size = (size + MEM_PAGE_MASK) & ~MEM_PAGE_MASK;
|
||||
|
||||
if (agent_info->coarse_region.handle != 0) {
|
||||
// Allocate in local memory if it is available
|
||||
status = hsa_memory_allocate(agent_info->coarse_region, size, (void**)&buffer);
|
||||
if (status == HSA_STATUS_SUCCESS) {
|
||||
status = hsa_memory_assign_agent(buffer, agent_info->dev_id, HSA_ACCESS_PERMISSION_RW);
|
||||
}
|
||||
} else {
|
||||
// Allocate in system memory if local memory is not available
|
||||
status = hsa_memory_allocate(agent_info->kernarg_region, size, (void**)&buffer);
|
||||
}
|
||||
|
||||
return (status == HSA_STATUS_SUCCESS) ? buffer : NULL;
|
||||
}
|
||||
|
||||
// Allocate memory tp pass kernel parameters.
|
||||
//
|
||||
// @param agent_info Agent from whose memory region to allocate
|
||||
//
|
||||
// @param size Size of memory in terms of bytes
|
||||
//
|
||||
// @return uint8_t* Pointer to buffer, null if allocation fails.
|
||||
//
|
||||
uint8_t* HsaRsrcFactory::AllocateSysMemory(const AgentInfo* agent_info, size_t size) {
|
||||
hsa_status_t status;
|
||||
size = (size + MEM_PAGE_MASK) & ~MEM_PAGE_MASK;
|
||||
|
||||
uint8_t* buffer = NULL;
|
||||
status = hsa_memory_allocate(agent_info->kernarg_region, size, (void**)&buffer);
|
||||
return (status == HSA_STATUS_SUCCESS) ? buffer : NULL;
|
||||
}
|
||||
|
||||
// Transfer data method
|
||||
bool HsaRsrcFactory::TransferData(void* dest_buff, void* src_buff, uint32_t length,
|
||||
bool host_to_dev) {
|
||||
hsa_status_t status;
|
||||
status = hsa_memory_copy(dest_buff, src_buff, length);
|
||||
return (status == HSA_STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
// Loads an Assembled Brig file and Finalizes it into Device Isa
|
||||
//
|
||||
// @param agent_info Gpu device for which to finalize
|
||||
//
|
||||
// @param brig_path File path of the Assembled Brig file
|
||||
//
|
||||
// @param kernel_name Name of the kernel to finalize
|
||||
//
|
||||
// @param code_desc Handle of finalized Code Descriptor that could
|
||||
// be used to submit for execution
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool HsaRsrcFactory::LoadAndFinalize(const AgentInfo* agent_info, const char* brig_path,
|
||||
char* kernel_name, hsa_executable_symbol_t* code_desc) {
|
||||
// Finalize the Hsail object into code object
|
||||
hsa_status_t status;
|
||||
hsa_code_object_t code_object;
|
||||
|
||||
// Build the code object filename
|
||||
std::string filename(brig_path);
|
||||
std::clog << "Code object filename: " << filename << std::endl;
|
||||
|
||||
// Open the file containing code object
|
||||
std::ifstream codeStream(filename.c_str(), std::ios::binary | std::ios::ate);
|
||||
if (!codeStream) {
|
||||
std::cerr << "Error: failed to load " << filename << std::endl;
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allocate memory to read in code object from file
|
||||
size_t size = std::string::size_type(codeStream.tellg());
|
||||
char* codeBuff = (char*)AllocateSysMemory(agent_info, size);
|
||||
if (!codeBuff) {
|
||||
std::cerr << "Error: failed to allocate memory for code object." << std::endl;
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the code object into allocated memory
|
||||
codeStream.seekg(0, std::ios::beg);
|
||||
std::copy(std::istreambuf_iterator<char>(codeStream), std::istreambuf_iterator<char>(), codeBuff);
|
||||
|
||||
// De-Serialize the code object that has been read into memory
|
||||
status = hsa_code_object_deserialize(codeBuff, size, NULL, &code_object);
|
||||
if (status != HSA_STATUS_SUCCESS) {
|
||||
std::cerr << "Failed to deserialize code object" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create executable.
|
||||
hsa_executable_t hsaExecutable;
|
||||
status =
|
||||
hsa_executable_create(HSA_PROFILE_FULL, HSA_EXECUTABLE_STATE_UNFROZEN, "", &hsaExecutable);
|
||||
CHECK_STATUS("Error in creating executable object", status);
|
||||
|
||||
// Load code object.
|
||||
status = hsa_executable_load_code_object(hsaExecutable, agent_info->dev_id, code_object, "");
|
||||
CHECK_STATUS("Error in loading executable object", status);
|
||||
|
||||
// Freeze executable.
|
||||
status = hsa_executable_freeze(hsaExecutable, "");
|
||||
CHECK_STATUS("Error in freezing executable object", status);
|
||||
|
||||
// Get symbol handle.
|
||||
hsa_executable_symbol_t kernelSymbol;
|
||||
status = hsa_executable_get_symbol(hsaExecutable, NULL, kernel_name, agent_info->dev_id, 0,
|
||||
&kernelSymbol);
|
||||
CHECK_STATUS("Error in looking up kernel symbol", status);
|
||||
|
||||
// Update output parameter
|
||||
*code_desc = kernelSymbol;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Print the various fields of Hsa Gpu Agents
|
||||
bool HsaRsrcFactory::PrintGpuAgents(const std::string& header) {
|
||||
std::clog << header << " :" << std::endl;
|
||||
|
||||
const AgentInfo* agent_info;
|
||||
int size = uint32_t(gpu_list_.size());
|
||||
for (int idx = 0; idx < size; idx++) {
|
||||
agent_info = gpu_list_[idx];
|
||||
|
||||
std::clog << "> agent[" << idx << "] :" << std::endl;
|
||||
std::clog << ">> Name : " << agent_info->name << std::endl;
|
||||
std::clog << ">> Max Wave Size : " << agent_info->max_wave_size << std::endl;
|
||||
std::clog << ">> Max Queue Size : " << agent_info->max_queue_size << std::endl;
|
||||
std::clog << ">> Kernarg Region Id : " << agent_info->coarse_region.handle << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
HsaRsrcFactory* HsaRsrcFactory::instance_ = NULL;
|
||||
HsaRsrcFactory::mutex_t HsaRsrcFactory::mutex_;
|
||||
|
||||
} // namespace util
|
||||
} // namespace rocprofiler
|
||||
@@ -0,0 +1,262 @@
|
||||
/**********************************************************************
|
||||
Copyright ©2013 Advanced Micro Devices, Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
<95> Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
<95> Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
********************************************************************/
|
||||
|
||||
#ifndef SRC_UTIL_HSA_RSRC_FACTORY_H_
|
||||
#define SRC_UTIL_HSA_RSRC_FACTORY_H_
|
||||
|
||||
#include <hsa.h>
|
||||
#include <hsa_ext_finalize.h>
|
||||
#include <hsa_ven_amd_aqlprofile.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define HSA_ARGUMENT_ALIGN_BYTES 16
|
||||
#define HSA_QUEUE_ALIGN_BYTES 64
|
||||
#define HSA_PACKET_ALIGN_BYTES 64
|
||||
|
||||
#define CHECK_STATUS(msg, status) \
|
||||
if (status != HSA_STATUS_SUCCESS) { \
|
||||
const char* emsg = 0; \
|
||||
hsa_status_string(status, &emsg); \
|
||||
printf("%s: %s\n", msg, emsg ? emsg : "<unknown error>"); \
|
||||
exit(1); \
|
||||
}
|
||||
|
||||
namespace rocprofiler {
|
||||
namespace util {
|
||||
static const unsigned MEM_PAGE_BYTES = 0x1000;
|
||||
static const unsigned MEM_PAGE_MASK = MEM_PAGE_BYTES - 1;
|
||||
typedef decltype(hsa_agent_t::handle) hsa_agent_handle_t;
|
||||
|
||||
// Encapsulates information about a Hsa Agent such as its
|
||||
// handle, name, max queue size, max wavefront size, etc.
|
||||
struct AgentInfo {
|
||||
// Handle of Agent
|
||||
hsa_agent_t dev_id;
|
||||
|
||||
// Agent type - Cpu = 0, Gpu = 1 or Dsp = 2
|
||||
uint32_t dev_type;
|
||||
|
||||
// Agent system index
|
||||
uint32_t dev_index;
|
||||
|
||||
// GFXIP name
|
||||
char gfxip[64];
|
||||
|
||||
// Name of Agent whose length is less than 64
|
||||
char name[64];
|
||||
|
||||
// Max size of Wavefront size
|
||||
uint32_t max_wave_size;
|
||||
|
||||
// Max size of Queue buffer
|
||||
uint32_t max_queue_size;
|
||||
|
||||
// Hsail profile supported by agent
|
||||
hsa_profile_t profile;
|
||||
|
||||
// Memory region supporting kernel parameters
|
||||
hsa_region_t coarse_region;
|
||||
|
||||
// Memory region supporting kernel arguments
|
||||
hsa_region_t kernarg_region;
|
||||
};
|
||||
|
||||
class HsaRsrcFactory {
|
||||
public:
|
||||
typedef std::recursive_mutex mutex_t;
|
||||
|
||||
static HsaRsrcFactory* Create() { return NULL; }
|
||||
|
||||
static HsaRsrcFactory* CreateInstance() {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
if (instance_ == NULL) {
|
||||
instance_ = new HsaRsrcFactory();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
static HsaRsrcFactory& Instance() {
|
||||
CreateInstance();
|
||||
hsa_status_t status = (instance_ != NULL) ? HSA_STATUS_SUCCESS : HSA_STATUS_ERROR;
|
||||
CHECK_STATUS("HsaRsrcFactory::Instance() is not found", status);
|
||||
return *instance_;
|
||||
}
|
||||
|
||||
static void Destroy() {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
if (instance_) delete instance_;
|
||||
instance_ = NULL;
|
||||
}
|
||||
|
||||
// Return system agent info
|
||||
const AgentInfo* GetAgentInfo(const hsa_agent_t agent);
|
||||
|
||||
// Get the count of Hsa Gpu Agents available on the platform
|
||||
//
|
||||
// @return uint32_t Number of Gpu agents on platform
|
||||
//
|
||||
uint32_t GetCountOfGpuAgents();
|
||||
|
||||
// Get the count of Hsa Cpu Agents available on the platform
|
||||
//
|
||||
// @return uint32_t Number of Cpu agents on platform
|
||||
//
|
||||
uint32_t GetCountOfCpuAgents();
|
||||
|
||||
// Get the AgentInfo handle of a Gpu device
|
||||
//
|
||||
// @param idx Gpu Agent at specified index
|
||||
//
|
||||
// @param agent_info Output parameter updated with AgentInfo
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool GetGpuAgentInfo(uint32_t idx, const AgentInfo** agent_info);
|
||||
|
||||
// Get the AgentInfo handle of a Cpu device
|
||||
//
|
||||
// @param idx Cpu Agent at specified index
|
||||
//
|
||||
// @param agent_info Output parameter updated with AgentInfo
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool GetCpuAgentInfo(uint32_t idx, const AgentInfo** agent_info);
|
||||
|
||||
// Create a Queue object and return its handle. The queue object is expected
|
||||
// to support user requested number of Aql dispatch packets.
|
||||
//
|
||||
// @param agent_info Gpu Agent on which to create a queue object
|
||||
//
|
||||
// @param num_Pkts Number of packets to be held by queue
|
||||
//
|
||||
// @param queue Output parameter updated with handle of queue object
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool CreateQueue(const AgentInfo* agent_info, uint32_t num_pkts, hsa_queue_t** queue);
|
||||
|
||||
// Create a Signal object and return its handle.
|
||||
//
|
||||
// @param value Initial value of signal object
|
||||
//
|
||||
// @param signal Output parameter updated with handle of signal object
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool CreateSignal(uint32_t value, hsa_signal_t* signal);
|
||||
|
||||
// Allocate memory for use by a kernel of specified size in specified
|
||||
// agent's memory region. Currently supports Global segment whose Kernarg
|
||||
// flag set.
|
||||
//
|
||||
// @param agent_info Agent from whose memory region to allocate
|
||||
//
|
||||
// @param size Size of memory in terms of bytes
|
||||
//
|
||||
// @return uint8_t* Pointer to buffer, null if allocation fails.
|
||||
//
|
||||
uint8_t* AllocateLocalMemory(const AgentInfo* agent_info, size_t size);
|
||||
|
||||
// Allocate memory tp pass kernel parameters.
|
||||
//
|
||||
// @param agent_info Agent from whose memory region to allocate
|
||||
//
|
||||
// @param size Size of memory in terms of bytes
|
||||
//
|
||||
// @return uint8_t* Pointer to buffer, null if allocation fails.
|
||||
//
|
||||
uint8_t* AllocateSysMemory(const AgentInfo* agent_info, size_t size);
|
||||
|
||||
// Transfer data method
|
||||
bool TransferData(void* dest_buff, void* src_buff, uint32_t length, bool host_to_dev);
|
||||
|
||||
// Loads an Assembled Brig file and Finalizes it into Device Isa
|
||||
//
|
||||
// @param agent_info Gpu device for which to finalize
|
||||
//
|
||||
// @param brig_path File path of the Assembled Brig file
|
||||
//
|
||||
// @param kernel_name Name of the kernel to finalize
|
||||
//
|
||||
// @param code_desc Handle of finalized Code Descriptor that could
|
||||
// be used to submit for execution
|
||||
//
|
||||
// @return bool true if successful, false otherwise
|
||||
//
|
||||
bool LoadAndFinalize(const AgentInfo* agent_info, const char* brig_path, char* kernel_name,
|
||||
hsa_executable_symbol_t* code_desc);
|
||||
|
||||
// Print the various fields of Hsa Gpu Agents
|
||||
bool PrintGpuAgents(const std::string& header);
|
||||
|
||||
// Return AqlProfile API table
|
||||
const hsa_ven_amd_aqlprofile_1_00_pfn_t* AqlProfileApi() const { return &aqlprofile_api_; }
|
||||
|
||||
private:
|
||||
// System agents iterating callback
|
||||
static hsa_status_t GetHsaAgentsCallback(hsa_agent_t agent, void* data);
|
||||
|
||||
// Callback function to find and bind kernarg region of an agent
|
||||
static hsa_status_t FindMemRegionsCallback(hsa_region_t region, void* data);
|
||||
|
||||
// Constructor of the class. Will initialize the Hsa Runtime and
|
||||
// query the system topology to get the list of Cpu and Gpu devices
|
||||
HsaRsrcFactory();
|
||||
|
||||
// Destructor of the class
|
||||
~HsaRsrcFactory();
|
||||
|
||||
// Add an instance of AgentInfo representing a Hsa Gpu agent
|
||||
const AgentInfo* AddAgentInfo(const hsa_agent_t agent);
|
||||
|
||||
static HsaRsrcFactory* instance_;
|
||||
static mutex_t mutex_;
|
||||
|
||||
// Used to maintain a list of Hsa Gpu Agent Info
|
||||
std::vector<const AgentInfo*> gpu_list_;
|
||||
|
||||
// Used to maintain a list of Hsa Cpu Agent Info
|
||||
std::vector<const AgentInfo*> cpu_list_;
|
||||
|
||||
// System agents map
|
||||
std::map<hsa_agent_handle_t, const AgentInfo*> agent_map_;
|
||||
|
||||
// AqlProfile API table
|
||||
hsa_ven_amd_aqlprofile_1_00_pfn_t aqlprofile_api_;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace rocprofiler
|
||||
|
||||
#endif // SRC_UTIL_HSA_RSRC_FACTORY_H_
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
#ifndef SRC_UTIL_LOGGER_H_
|
||||
#define SRC_UTIL_LOGGER_H_
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/file.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
|
||||
namespace rocprofiler {
|
||||
namespace util {
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
typedef std::recursive_mutex mutex_t;
|
||||
|
||||
template <typename T> Logger& operator<<(const T& m) {
|
||||
std::ostringstream oss;
|
||||
oss << m;
|
||||
if (!streaming_)
|
||||
Log(oss.str());
|
||||
else
|
||||
Put(oss.str());
|
||||
streaming_ = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
typedef void (*manip_t)();
|
||||
Logger& operator<<(manip_t f) {
|
||||
f();
|
||||
return *this;
|
||||
}
|
||||
|
||||
static void begm() { Instance().ResetStreaming(true); }
|
||||
static void endl() { Instance().ResetStreaming(false); }
|
||||
|
||||
static const std::string& LastMessage() {
|
||||
Logger& logger = Instance();
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
return logger.message_[GetTid()];
|
||||
}
|
||||
|
||||
static Logger* Create() {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
if (instance_ == NULL) instance_ = new Logger();
|
||||
return instance_;
|
||||
}
|
||||
|
||||
static void Destroy() {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
if (instance_ != NULL) delete instance_;
|
||||
instance_ = NULL;
|
||||
}
|
||||
|
||||
static Logger& Instance() {
|
||||
Create();
|
||||
return *instance_;
|
||||
}
|
||||
|
||||
private:
|
||||
static uint32_t GetPid() { return syscall(__NR_getpid); }
|
||||
static uint32_t GetTid() { return syscall(__NR_gettid); }
|
||||
|
||||
Logger() : file_(NULL), dirty_(false), streaming_(false), messaging_(false) {
|
||||
const char* path = getenv("ROCPROFILER_LOG");
|
||||
if (path != NULL) {
|
||||
file_ = fopen("/tmp/rocprofiler_log.txt", "a");
|
||||
}
|
||||
ResetStreaming(false);
|
||||
}
|
||||
|
||||
~Logger() {
|
||||
if (file_ != NULL) {
|
||||
if (dirty_) Put("\n");
|
||||
fclose(file_);
|
||||
}
|
||||
}
|
||||
|
||||
void ResetStreaming(const bool messaging) {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
if (messaging) {
|
||||
message_[GetTid()] = "";
|
||||
}
|
||||
messaging_ = messaging;
|
||||
streaming_ = messaging;
|
||||
}
|
||||
|
||||
void Put(const std::string& m) {
|
||||
std::lock_guard<mutex_t> lck(mutex_);
|
||||
if (messaging_) {
|
||||
message_[GetTid()] += m;
|
||||
}
|
||||
if (file_ != NULL) {
|
||||
dirty_ = true;
|
||||
flock(fileno(file_), LOCK_EX);
|
||||
fprintf(file_, "%s", m.c_str());
|
||||
fflush(file_);
|
||||
flock(fileno(file_), LOCK_UN);
|
||||
}
|
||||
}
|
||||
|
||||
void Log(const std::string& m) {
|
||||
const time_t rawtime = time(NULL);
|
||||
tm tm_info;
|
||||
localtime_r(&rawtime, &tm_info);
|
||||
char tm_str[26];
|
||||
strftime(tm_str, 26, "%Y-%m-%d %H:%M:%S", &tm_info);
|
||||
std::ostringstream oss;
|
||||
oss << "<" << tm_str << std::dec << " pid" << GetPid() << " tid" << GetTid() << "> " << m;
|
||||
Put(oss.str());
|
||||
}
|
||||
|
||||
FILE* file_;
|
||||
bool dirty_;
|
||||
bool streaming_;
|
||||
bool messaging_;
|
||||
|
||||
static mutex_t mutex_;
|
||||
static Logger* instance_;
|
||||
std::map<uint32_t, std::string> message_;
|
||||
};
|
||||
|
||||
} // namespace util
|
||||
} // namespace rocprofiler
|
||||
|
||||
#define ERR_LOGGING(stream) { \
|
||||
rocprofiler::util::Logger::Instance() \
|
||||
<< "error: " \
|
||||
<< rocprofiler::util::Logger::begm \
|
||||
<< stream \
|
||||
<< rocprofiler::util::Logger::endl; \
|
||||
}
|
||||
|
||||
#define INFO_LOGGING(stream) { \
|
||||
rocprofiler::util::Logger::Instance() \
|
||||
<< "info: " \
|
||||
<< rocprofiler::util::Logger::begm \
|
||||
<< stream \
|
||||
<< rocprofiler::util::Logger::endl; \
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
# define DBG_LOGGING(stream) { \
|
||||
rocprofiler::util::Logger::Instance() << rocprofiler::util::Logger::begm \
|
||||
<< "debug: \"" << stream << "\"" << \
|
||||
<< " in " << __FUNCTION__ \
|
||||
<< " at " << __FILE__ << " line " << __LINE__ \
|
||||
<< rocprofiler::util::Logger::endl; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // SRC_UTIL_LOGGER_H_
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
#ifndef _SRC_XML_EXPR_H
|
||||
#define _SRC_XML_EXPR_H
|
||||
|
||||
#include <exception>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
|
||||
namespace xml {
|
||||
class exception_t : public std::exception {
|
||||
public:
|
||||
explicit exception_t(const std::string& msg) : str_(msg) {}
|
||||
const char* what() const throw() { return str_.c_str(); }
|
||||
protected:
|
||||
const std::string str_;
|
||||
};
|
||||
|
||||
typedef uint64_t args_t;
|
||||
typedef std::map<std::string, args_t> args_map_t;
|
||||
class Expr;
|
||||
|
||||
template <class T>
|
||||
class any_cache_t {
|
||||
public:
|
||||
virtual ~any_cache_t() {}
|
||||
virtual bool Lookup(const std::string& name, T& result) const = 0;
|
||||
};
|
||||
|
||||
typedef any_cache_t<std::string> expr_cache_t;
|
||||
typedef any_cache_t<args_t> args_cache_t;
|
||||
|
||||
class bin_expr_t {
|
||||
public:
|
||||
static const bin_expr_t* CreateExpr(const bin_expr_t* arg1, const bin_expr_t* arg2, const char op);
|
||||
static const bin_expr_t* CreateArg(Expr *obj, const std::string str);
|
||||
|
||||
bin_expr_t() : arg1_(NULL), arg2_(NULL) {}
|
||||
bin_expr_t(const bin_expr_t* arg1, const bin_expr_t* arg2) : arg1_(arg1), arg2_(arg2) {}
|
||||
|
||||
virtual args_t Eval(const args_cache_t& args) const = 0;
|
||||
virtual std::string Symbol() const = 0;
|
||||
|
||||
std::string String() const {
|
||||
std::string str;
|
||||
if (arg1_) { str = "(" + arg1_->String() + " " + Symbol() + " " + arg2_->String() + ")"; }
|
||||
else str = Symbol();
|
||||
return str;
|
||||
}
|
||||
|
||||
protected:
|
||||
const bin_expr_t* arg1_;
|
||||
const bin_expr_t* arg2_;
|
||||
};
|
||||
|
||||
class Expr {
|
||||
public:
|
||||
explicit Expr(const std::string& expr, const expr_cache_t* cache) :
|
||||
expr_(expr),
|
||||
pos_(0),
|
||||
sub_count_(0),
|
||||
cache_(cache)
|
||||
{
|
||||
sub_vec_ = new std::vector<const Expr*>;
|
||||
var_vec_ = new std::vector<std::string>;
|
||||
tree_ = ParseExpr();
|
||||
}
|
||||
|
||||
explicit Expr(const std::string& expr, const Expr* obj) :
|
||||
expr_(expr),
|
||||
pos_(0),
|
||||
sub_count_(0),
|
||||
cache_(obj->cache_),
|
||||
sub_vec_(obj->sub_vec_),
|
||||
var_vec_(obj->var_vec_)
|
||||
{
|
||||
sub_vec_->push_back(this);
|
||||
tree_ = ParseExpr();
|
||||
if (!SubCheck()) throw exception_t("expr '" + expr_ + "', bad parenthesis count");
|
||||
}
|
||||
|
||||
~Expr() {
|
||||
delete cache_;
|
||||
for (auto it : *sub_vec_) delete it;
|
||||
delete sub_vec_;
|
||||
delete var_vec_;
|
||||
}
|
||||
|
||||
std::string GetStr() const { return expr_; }
|
||||
const expr_cache_t* GetCache() const { return cache_; }
|
||||
const bin_expr_t* GetTree() const { return tree_; }
|
||||
|
||||
args_t Eval(const args_cache_t& args) const { return tree_->Eval(args); }
|
||||
|
||||
std::string Lookup(const std::string& str) const {
|
||||
std::string result;
|
||||
if (cache_ && !(cache_->Lookup(str, result))) throw exception_t("expr '" + expr_ + "', lookup '" + str + "' failed");
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddVar(const std::string& str) {
|
||||
bool found = false;
|
||||
for (std::string s : *var_vec_) if (s == str) found = true;
|
||||
if (!found) var_vec_->push_back(str);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& GetVars() const { return *var_vec_; }
|
||||
|
||||
std::string String() const { return tree_->String(); }
|
||||
|
||||
private:
|
||||
const bin_expr_t* ParseExpr() {
|
||||
const bin_expr_t* expr = ParseArg();
|
||||
while (!IsEnd()) {
|
||||
const char op = Symb();
|
||||
const bin_expr_t* second_arg = NULL;
|
||||
if (IsSymb(')')) {
|
||||
Next();
|
||||
SubClose();
|
||||
break;
|
||||
} if (IsSymb('*') || IsSymb('/')) {
|
||||
Next();
|
||||
second_arg = ParseArg();
|
||||
expr = bin_expr_t::CreateExpr(expr, second_arg, op);
|
||||
} else if (IsSymb('+') || IsSymb('-')) {
|
||||
Next();
|
||||
second_arg = ParseExpr();
|
||||
expr = bin_expr_t::CreateExpr(expr, second_arg, op);
|
||||
break;
|
||||
} else {
|
||||
throw exception_t("expr '" + expr_ + "', bad operator '" + op + "'");
|
||||
}
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
const bin_expr_t* ParseArg() {
|
||||
const bin_expr_t* arg = NULL;
|
||||
if (IsSymb('(')) {
|
||||
Next();
|
||||
SubOpen();
|
||||
arg = ParseExpr();
|
||||
} else {
|
||||
const unsigned pos = FindOp();
|
||||
const std::string str = CutTill(pos);
|
||||
arg = bin_expr_t::CreateArg(this, str);
|
||||
if (arg == NULL) throw exception_t("expr '" + expr_ + "', bad argument '" + str + "'");
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
char Symb() const { return Symb(pos_); }
|
||||
char Symb(const unsigned ind) const { return expr_[ind]; }
|
||||
bool IsEnd() const { return (pos_ >= expr_.length()); }
|
||||
bool IsSymb(const char c) const { return IsSymb(pos_, c); }
|
||||
bool IsSymb(const unsigned ind, const char c) const { return (expr_[ind] == c); }
|
||||
void Next() { ++pos_; }
|
||||
void SubOpen() { ++sub_count_; }
|
||||
void SubClose() { --sub_count_; }
|
||||
bool SubCheck() const { return (sub_count_ == 0); }
|
||||
unsigned FindOp() const {
|
||||
unsigned i = pos_;
|
||||
while (i < expr_.length()) {
|
||||
switch (Symb(i)) {
|
||||
case '*':
|
||||
case '/':
|
||||
case '+':
|
||||
case '-':
|
||||
case '(':
|
||||
case ')':
|
||||
goto end;
|
||||
}
|
||||
++i;
|
||||
}
|
||||
end:
|
||||
return i;
|
||||
}
|
||||
std::string CutTill(const unsigned pos) {
|
||||
const std::string str = (pos > pos_) ? expr_.substr(pos_, pos - pos_) : "";
|
||||
pos_ = pos;
|
||||
return str;
|
||||
}
|
||||
|
||||
const std::string expr_;
|
||||
unsigned pos_;
|
||||
unsigned sub_count_;
|
||||
const bin_expr_t* tree_;
|
||||
const expr_cache_t* const cache_;
|
||||
std::vector<const Expr*>* sub_vec_;
|
||||
std::vector<std::string>* var_vec_;
|
||||
};
|
||||
|
||||
class add_expr_t : public bin_expr_t {
|
||||
public:
|
||||
add_expr_t(const bin_expr_t* arg1, const bin_expr_t* arg2) : bin_expr_t(arg1, arg2) {}
|
||||
args_t Eval(const args_cache_t& args) const { return (arg1_->Eval(args) + arg2_->Eval(args)); }
|
||||
std::string Symbol() const { return "+"; }
|
||||
};
|
||||
class sub_expr_t : public bin_expr_t {
|
||||
public:
|
||||
sub_expr_t(const bin_expr_t* arg1, const bin_expr_t* arg2) : bin_expr_t(arg1, arg2) {}
|
||||
args_t Eval(const args_cache_t& args) const { return (arg1_->Eval(args) - arg2_->Eval(args)); }
|
||||
std::string Symbol() const { return "-"; }
|
||||
};
|
||||
class mul_expr_t : public bin_expr_t {
|
||||
public:
|
||||
mul_expr_t(const bin_expr_t* arg1, const bin_expr_t* arg2) : bin_expr_t(arg1, arg2) {}
|
||||
args_t Eval(const args_cache_t& args) const { return (arg1_->Eval(args) * arg2_->Eval(args)); }
|
||||
std::string Symbol() const { return "*"; }
|
||||
};
|
||||
class div_expr_t : public bin_expr_t {
|
||||
public:
|
||||
div_expr_t(const bin_expr_t* arg1, const bin_expr_t* arg2) : bin_expr_t(arg1, arg2) {}
|
||||
args_t Eval(const args_cache_t& args) const { return (arg1_->Eval(args) / arg2_->Eval(args)); }
|
||||
std::string Symbol() const { return "/"; }
|
||||
};
|
||||
class const_expr_t : public bin_expr_t {
|
||||
public:
|
||||
const_expr_t(const args_t value) : value_(value) {}
|
||||
args_t Eval(const args_cache_t&) const { return value_; }
|
||||
std::string Symbol() const { std::ostringstream os; os << value_; return os.str(); }
|
||||
private:
|
||||
const args_t value_;
|
||||
};
|
||||
class var_expr_t : public bin_expr_t {
|
||||
public:
|
||||
var_expr_t(const std::string name) : name_(name) {}
|
||||
args_t Eval(const args_cache_t& args) const {
|
||||
args_t result = 0;
|
||||
if (!args.Lookup(name_, result)) throw exception_t("expr arg lookup '" + name_ + "' failed");
|
||||
return result;
|
||||
}
|
||||
std::string Symbol() const { return name_; }
|
||||
private:
|
||||
const std::string name_;
|
||||
};
|
||||
|
||||
inline const bin_expr_t* bin_expr_t::CreateExpr(const bin_expr_t* arg1, const bin_expr_t* arg2, const char op) {
|
||||
const bin_expr_t* expr = NULL;
|
||||
switch (op) {
|
||||
case '+':
|
||||
expr = new add_expr_t(arg1, arg2);
|
||||
break;
|
||||
case '-':
|
||||
expr = new sub_expr_t(arg1, arg2);
|
||||
break;
|
||||
case '*':
|
||||
expr = new mul_expr_t(arg1, arg2);
|
||||
break;
|
||||
case '/':
|
||||
expr = new div_expr_t(arg1, arg2);
|
||||
break;
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
inline const bin_expr_t* bin_expr_t::CreateArg(Expr* obj, const std::string str) {
|
||||
const bin_expr_t* arg = NULL;
|
||||
const unsigned i = strspn(str.c_str(), "1234567890");
|
||||
if (i == str.length()) {
|
||||
const unsigned value = atoi(str.c_str());
|
||||
arg = new const_expr_t(value);
|
||||
} else {
|
||||
const std::string sub_expr = obj->Lookup(str);
|
||||
if (sub_expr.empty()) {
|
||||
arg = new var_expr_t(str);
|
||||
obj->AddVar(str);
|
||||
} else {
|
||||
const Expr* expr = new Expr(sub_expr, obj);
|
||||
arg = expr->GetTree();
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
} // namespace xml
|
||||
|
||||
#endif // _SRC_XML_EXPR_H
|
||||
+221
@@ -0,0 +1,221 @@
|
||||
#ifndef SRC_XML_XML_H_
|
||||
#define SRC_XML_XML_H_
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace xml {
|
||||
|
||||
class Xml {
|
||||
public:
|
||||
typedef std::vector<char> token_t;
|
||||
struct level_t {
|
||||
std::string tag;
|
||||
std::vector<level_t*> nodes;
|
||||
std::map<std::string, std::string> opts;
|
||||
};
|
||||
typedef std::vector<level_t*> nodes_vec_t;
|
||||
|
||||
enum {
|
||||
DECL_STATE,
|
||||
BODY_STATE
|
||||
};
|
||||
|
||||
Xml(const char* file_name) :
|
||||
file_name_(file_name),
|
||||
file_line_(0),
|
||||
data_size_(0),
|
||||
index_(0),
|
||||
state_(BODY_STATE),
|
||||
level_(NULL),
|
||||
comment_(false)
|
||||
{
|
||||
AddLevel("top");
|
||||
|
||||
fd_ = open(file_name, O_RDONLY);
|
||||
if (fd_ == -1) {
|
||||
std::cout << "XML file not found: " << file_name << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
token_t remainder;
|
||||
while (1) {
|
||||
token_t token = (remainder.size()) ? remainder : NextToken();
|
||||
remainder.clear();
|
||||
// End of file
|
||||
if (token.size() == 0) break;
|
||||
|
||||
// token_t token1 = token;
|
||||
// token1.push_back('\0');
|
||||
// std::cout << "> " << &token1[0] << std::endl;
|
||||
|
||||
switch(state_) {
|
||||
case BODY_STATE:
|
||||
if (token[0] == '<') {
|
||||
bool node_begin = true;
|
||||
unsigned ind = 1;
|
||||
if (token[1] == '/') {
|
||||
node_begin = false;
|
||||
++ind;
|
||||
}
|
||||
|
||||
unsigned i = ind;
|
||||
while (i < token.size()) { if (token[i] == '>') break; ++i; }
|
||||
for (unsigned j = i + 1; j < token.size(); ++j) remainder.push_back(token[j]);
|
||||
|
||||
if (i == token.size()) {
|
||||
if (node_begin) state_ = DECL_STATE;
|
||||
else BadFormat(token);
|
||||
token.push_back('\0');
|
||||
} else token[i] = '\0';
|
||||
|
||||
const char* tag = strdup(&token[ind]);
|
||||
if (node_begin) {
|
||||
AddLevel(tag);
|
||||
} else {
|
||||
if (strncmp(CurrentLevel().c_str(), tag, strlen(tag))) {
|
||||
token.back() = '>';
|
||||
BadFormat(token);
|
||||
}
|
||||
UpLevel();
|
||||
}
|
||||
} else BadFormat(token);
|
||||
break;
|
||||
case DECL_STATE:
|
||||
if (token[0] == '>') {
|
||||
state_ = BODY_STATE;
|
||||
for (unsigned j = 1; j < token.size(); ++j) remainder.push_back(token[j]);
|
||||
continue;
|
||||
} else {
|
||||
token.push_back('\0');
|
||||
unsigned j = 0;
|
||||
for (j = 0; j < token.size(); ++j) if (token[j] == '=') break;
|
||||
if (j == token.size()) BadFormat(token);
|
||||
token[j] = '\0';
|
||||
const char* key = &token[0];
|
||||
const char* value = &token[j + 1];
|
||||
AddOption(key, value);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
std::cout << "Wrong state: " << state_ << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<level_t*> GetNodes(std::string global_tag) {
|
||||
return map_[global_tag];
|
||||
}
|
||||
|
||||
void Print() const {
|
||||
for(auto& elem : map_) {
|
||||
for (auto node : elem.second) {
|
||||
if (node->opts.size()) {
|
||||
std::cout << elem.first << ":" << std::endl;
|
||||
for (auto& opt : node->opts) {
|
||||
std::cout << " " << opt.first << " = " << opt.second << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool LineEndCheck() {
|
||||
bool found = false;
|
||||
if (buffer_[index_] == '\n') {
|
||||
buffer_[index_] = ' ';
|
||||
++file_line_;
|
||||
found = true;
|
||||
comment_ = false;
|
||||
} else if (comment_ || (buffer_[index_] == '#')) {
|
||||
found = true;
|
||||
comment_ = true;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
token_t NextToken() {
|
||||
token_t token;
|
||||
|
||||
while (1) {
|
||||
if (data_size_ == 0) {
|
||||
data_size_ = read(fd_, buffer_, buf_size_);
|
||||
if (data_size_ <= 0) break;
|
||||
}
|
||||
if (token.empty()) while ((index_ < data_size_) && ((buffer_[index_] == ' ') || LineEndCheck())) {
|
||||
++index_;
|
||||
}
|
||||
while ((index_ < data_size_) && (buffer_[index_] != ' ') && !LineEndCheck()) {
|
||||
token.push_back(buffer_[index_++]);
|
||||
}
|
||||
if (index_ == data_size_) {
|
||||
index_ = 0;
|
||||
data_size_ = 0;
|
||||
} else break;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
void BadFormat(token_t token) {
|
||||
token.push_back('\0');
|
||||
std::cout << "Error: " << file_name_ << ", line " << file_line_ << ", bad XML token '" << &token[0] << "'" << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void AddLevel(const std::string& tag) {
|
||||
level_t* level = new level_t;
|
||||
level->tag = tag;
|
||||
if (level_) {
|
||||
level_->nodes.push_back(level);
|
||||
stack_.push_back(level_);
|
||||
}
|
||||
level_ = level;
|
||||
|
||||
std::string global_tag;
|
||||
for (level_t* level : stack_) { global_tag += level->tag + "."; }
|
||||
global_tag += tag;
|
||||
map_[global_tag].push_back(level_);
|
||||
}
|
||||
|
||||
void UpLevel() {
|
||||
level_ = stack_.back();
|
||||
stack_.pop_back();
|
||||
}
|
||||
|
||||
std::string CurrentLevel() const {
|
||||
return level_->tag;
|
||||
}
|
||||
|
||||
void AddOption(const std::string& key, const std::string& value) {
|
||||
level_->opts[key] = value;
|
||||
}
|
||||
|
||||
const char* file_name_;
|
||||
unsigned file_line_;
|
||||
int fd_;
|
||||
static const unsigned buf_size_ = 256;
|
||||
char buffer_[buf_size_];
|
||||
unsigned data_size_;
|
||||
unsigned index_;
|
||||
unsigned state_;
|
||||
level_t* level_;
|
||||
std::vector<level_t*> stack_;
|
||||
std::map<std::string, nodes_vec_t> map_;
|
||||
bool comment_;
|
||||
};
|
||||
|
||||
} // namespace xml
|
||||
|
||||
#endif // SRC_XML_XML_H_
|
||||
새 이슈에서 참조
사용자 차단