2019-06-24 21:05:12 -05:00
|
|
|
#ifndef SRC_CORE_TRACE_BUFFER_H_
|
|
|
|
|
#define SRC_CORE_TRACE_BUFFER_H_
|
|
|
|
|
|
2019-10-02 15:29:09 -05:00
|
|
|
#include <atomic>
|
2020-01-27 14:30:44 -06:00
|
|
|
#include <iostream>
|
2019-07-13 00:38:12 -05:00
|
|
|
#include <list>
|
|
|
|
|
#include <mutex>
|
2020-01-27 14:30:44 -06:00
|
|
|
#include <sstream>
|
|
|
|
|
|
2019-07-13 00:38:12 -05:00
|
|
|
#include <pthread.h>
|
2019-08-10 00:14:04 -05:00
|
|
|
#include <string.h>
|
2019-08-11 08:56:39 -05:00
|
|
|
#include <unistd.h>
|
2019-07-13 00:38:12 -05:00
|
|
|
|
2020-01-27 14:30:44 -06:00
|
|
|
#define FATAL(stream) \
|
|
|
|
|
do { \
|
|
|
|
|
std::ostringstream oss; \
|
|
|
|
|
oss << __FUNCTION__ << "(), " << stream; \
|
|
|
|
|
std::cout << oss.str() << std::endl; \
|
|
|
|
|
abort(); \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2019-07-13 00:38:12 -05:00
|
|
|
#define PTHREAD_CALL(call) \
|
|
|
|
|
do { \
|
|
|
|
|
int err = call; \
|
|
|
|
|
if (err != 0) { \
|
|
|
|
|
errno = err; \
|
|
|
|
|
perror(#call); \
|
|
|
|
|
abort(); \
|
|
|
|
|
} \
|
|
|
|
|
} while (0)
|
|
|
|
|
|
2019-06-24 21:05:12 -05:00
|
|
|
namespace roctracer {
|
|
|
|
|
enum {
|
|
|
|
|
TRACE_ENTRY_INV = 0,
|
|
|
|
|
TRACE_ENTRY_INIT = 1,
|
|
|
|
|
TRACE_ENTRY_COMPL = 2
|
|
|
|
|
};
|
2019-07-13 00:38:12 -05:00
|
|
|
|
2020-08-15 02:23:43 -05:00
|
|
|
enum entry_type_t {
|
|
|
|
|
DFLT_ENTRY_TYPE = 0,
|
|
|
|
|
API_ENTRY_TYPE = 1,
|
|
|
|
|
COPY_ENTRY_TYPE = 2,
|
|
|
|
|
KERNEL_ENTRY_TYPE = 3,
|
|
|
|
|
NUM_ENTRY_TYPE = 4
|
2019-06-24 21:05:12 -05:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct trace_entry_t {
|
|
|
|
|
std::atomic<uint32_t> valid;
|
2020-08-15 02:23:43 -05:00
|
|
|
entry_type_t type;
|
2019-06-24 21:05:12 -05:00
|
|
|
uint64_t dispatch;
|
|
|
|
|
uint64_t begin; // kernel begin timestamp, ns
|
|
|
|
|
uint64_t end; // kernel end timestamp, ns
|
|
|
|
|
uint64_t complete;
|
|
|
|
|
hsa_agent_t agent;
|
2019-08-11 08:56:39 -05:00
|
|
|
uint32_t dev_index;
|
2019-06-24 21:05:12 -05:00
|
|
|
hsa_signal_t orig;
|
|
|
|
|
hsa_signal_t signal;
|
|
|
|
|
union {
|
|
|
|
|
struct {
|
|
|
|
|
} copy;
|
|
|
|
|
struct {
|
|
|
|
|
const char* name;
|
|
|
|
|
hsa_agent_t agent;
|
|
|
|
|
uint32_t tid;
|
|
|
|
|
} kernel;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
2020-01-27 14:30:44 -06:00
|
|
|
template <class T>
|
|
|
|
|
struct push_element_fun {
|
|
|
|
|
T* const elem_;
|
2020-08-15 02:23:43 -05:00
|
|
|
T** prev_;
|
|
|
|
|
bool fun(T* node) {
|
|
|
|
|
if (node->priority_ > elem_->priority_) {
|
|
|
|
|
*prev_ = elem_;
|
|
|
|
|
elem_->next_elem_ = node;
|
|
|
|
|
} else if (node->next_elem_ == NULL) {
|
|
|
|
|
node->next_elem_ = elem_;
|
|
|
|
|
} else {
|
|
|
|
|
prev_ = &(node->next_elem_);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
push_element_fun(T* elem, T** prev) : elem_(elem), prev_(prev) {}
|
2020-01-27 14:30:44 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
|
struct call_element_fun {
|
|
|
|
|
void (T::*fptr_)();
|
2020-08-15 02:23:43 -05:00
|
|
|
bool fun(T* node) const { (node->*fptr_)(); return false; }
|
2020-01-27 14:30:44 -06:00
|
|
|
call_element_fun(void (T::*f)()) : fptr_(f) {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct TraceBufferBase {
|
|
|
|
|
typedef std::mutex mutex_t;
|
|
|
|
|
|
|
|
|
|
virtual void StartWorkerThread() = 0;
|
|
|
|
|
virtual void Flush() = 0;
|
|
|
|
|
|
|
|
|
|
static void StartWorkerThreadAll() { foreach(call_element_fun<TraceBufferBase>(&TraceBufferBase::StartWorkerThread)); }
|
|
|
|
|
static void FlushAll() { foreach(call_element_fun<TraceBufferBase>(&TraceBufferBase::Flush)); }
|
|
|
|
|
|
|
|
|
|
static void Push(TraceBufferBase* elem) {
|
|
|
|
|
if (head_elem_ == NULL) head_elem_ = elem;
|
2020-08-15 02:23:43 -05:00
|
|
|
else foreach(push_element_fun<TraceBufferBase>(elem, &head_elem_));
|
2020-01-27 14:30:44 -06:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 02:23:43 -05:00
|
|
|
TraceBufferBase(const uint32_t& prior) : priority_(prior), next_elem_(NULL) {}
|
2020-01-27 14:30:44 -06:00
|
|
|
|
|
|
|
|
template<class F>
|
|
|
|
|
static void foreach(const F& f_in) {
|
|
|
|
|
std::lock_guard<mutex_t> lck(mutex_);
|
|
|
|
|
F f = f_in;
|
|
|
|
|
TraceBufferBase* p = head_elem_;
|
|
|
|
|
while (p != NULL) {
|
|
|
|
|
TraceBufferBase* next = p->next_elem_;
|
2020-08-15 02:23:43 -05:00
|
|
|
if (f.fun(p) == true) break;
|
2020-01-27 14:30:44 -06:00
|
|
|
p = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-15 02:23:43 -05:00
|
|
|
const uint32_t priority_;
|
2020-01-27 14:30:44 -06:00
|
|
|
TraceBufferBase* next_elem_;
|
|
|
|
|
static TraceBufferBase* head_elem_;
|
|
|
|
|
static mutex_t mutex_;
|
|
|
|
|
};
|
|
|
|
|
|
2019-06-24 21:05:12 -05:00
|
|
|
template <typename Entry>
|
2020-01-27 14:30:44 -06:00
|
|
|
class TraceBuffer : protected TraceBufferBase {
|
2019-06-24 21:05:12 -05:00
|
|
|
public:
|
2019-07-13 00:38:12 -05:00
|
|
|
typedef void (*callback_t)(Entry*);
|
|
|
|
|
typedef TraceBuffer<Entry> Obj;
|
|
|
|
|
typedef uint64_t pointer_t;
|
2020-07-16 04:42:31 -05:00
|
|
|
typedef std::recursive_mutex mutex_t;
|
2020-08-15 02:23:43 -05:00
|
|
|
typedef typename std::list<Entry*> buf_list_t;
|
|
|
|
|
typedef typename buf_list_t::iterator buf_list_it_t;
|
2019-07-13 00:38:12 -05:00
|
|
|
|
|
|
|
|
struct flush_prm_t {
|
2020-08-15 02:23:43 -05:00
|
|
|
entry_type_t type;
|
2019-07-13 00:38:12 -05:00
|
|
|
callback_t fun;
|
|
|
|
|
};
|
2019-06-24 21:05:12 -05:00
|
|
|
|
2020-08-11 04:44:08 -05:00
|
|
|
TraceBuffer(const char* name, uint32_t size, const flush_prm_t* flush_prm_arr, uint32_t flush_prm_count, uint32_t prior = 0) :
|
2020-08-15 02:23:43 -05:00
|
|
|
TraceBufferBase(prior),
|
|
|
|
|
size_(size),
|
2020-01-27 14:30:44 -06:00
|
|
|
work_thread_started_(false)
|
2019-08-10 00:14:04 -05:00
|
|
|
{
|
|
|
|
|
name_ = strdup(name);
|
2019-07-13 00:38:12 -05:00
|
|
|
data_ = allocate_fun();
|
2020-07-16 04:42:31 -05:00
|
|
|
next_ = allocate_fun();
|
2019-07-13 00:38:12 -05:00
|
|
|
read_pointer_ = 0;
|
2020-08-15 02:23:43 -05:00
|
|
|
write_pointer_ = 0;
|
2019-07-13 00:38:12 -05:00
|
|
|
end_pointer_ = size;
|
|
|
|
|
buf_list_.push_back(data_);
|
|
|
|
|
|
2020-08-15 02:23:43 -05:00
|
|
|
memset(f_array_, 0, sizeof(f_array_));
|
|
|
|
|
for (const flush_prm_t* prm = flush_prm_arr; prm < flush_prm_arr + flush_prm_count; prm++) {
|
|
|
|
|
const entry_type_t type = prm->type;
|
|
|
|
|
if (type >= NUM_ENTRY_TYPE) FATAL("out of f_array bounds (" << type << ")");
|
|
|
|
|
if (f_array_[type] != NULL) FATAL("handler function ptr redefinition (" << type << ")");
|
|
|
|
|
f_array_[type] = prm->fun;
|
|
|
|
|
}
|
2020-08-11 04:44:08 -05:00
|
|
|
|
2020-01-27 14:30:44 -06:00
|
|
|
TraceBufferBase::Push(this);
|
2019-07-13 00:38:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~TraceBuffer() {
|
2020-01-27 14:30:44 -06:00
|
|
|
StopWorkerThread();
|
2020-08-15 02:23:43 -05:00
|
|
|
Flush();
|
2019-06-24 21:05:12 -05:00
|
|
|
}
|
|
|
|
|
|
2020-01-27 14:30:44 -06:00
|
|
|
void StartWorkerThread() {
|
|
|
|
|
std::lock_guard<mutex_t> lck(mutex_);
|
|
|
|
|
if (work_thread_started_ == false) {
|
|
|
|
|
PTHREAD_CALL(pthread_mutex_init(&work_mutex_, NULL));
|
|
|
|
|
PTHREAD_CALL(pthread_cond_init(&work_cond_, NULL));
|
|
|
|
|
PTHREAD_CALL(pthread_create(&work_thread_, NULL, allocate_worker, this));
|
|
|
|
|
work_thread_started_ = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void StopWorkerThread() {
|
|
|
|
|
std::lock_guard<mutex_t> lck(mutex_);
|
|
|
|
|
if (work_thread_started_ == true) {
|
|
|
|
|
PTHREAD_CALL(pthread_cancel(work_thread_));
|
|
|
|
|
void *res;
|
|
|
|
|
PTHREAD_CALL(pthread_join(work_thread_, &res));
|
|
|
|
|
if (res != PTHREAD_CANCELED) FATAL("consumer thread wasn't stopped correctly");
|
|
|
|
|
work_thread_started_ = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-07-13 00:38:12 -05:00
|
|
|
|
2019-06-24 21:05:12 -05:00
|
|
|
Entry* GetEntry() {
|
2020-08-15 02:23:43 -05:00
|
|
|
const pointer_t pointer = write_pointer_.fetch_add(1);
|
2019-07-13 00:38:12 -05:00
|
|
|
if (pointer >= end_pointer_) wrap_buffer(pointer);
|
2020-01-27 14:30:44 -06:00
|
|
|
if (pointer >= end_pointer_) FATAL("pointer >= end_pointer_ after buffer wrap");
|
2020-08-15 02:23:43 -05:00
|
|
|
Entry* entry = data_ + (size_ + pointer - end_pointer_);
|
|
|
|
|
entry->valid = TRACE_ENTRY_INV;
|
|
|
|
|
entry->type = DFLT_ENTRY_TYPE;
|
|
|
|
|
return entry;
|
2019-07-13 00:38:12 -05:00
|
|
|
}
|
|
|
|
|
|
2020-01-27 14:30:44 -06:00
|
|
|
void Flush() { flush_buf(); }
|
2019-07-13 00:38:12 -05:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
void flush_buf() {
|
2019-08-19 23:18:07 -05:00
|
|
|
std::lock_guard<mutex_t> lck(mutex_);
|
|
|
|
|
|
2020-08-15 02:23:43 -05:00
|
|
|
pointer_t pointer = read_pointer_;
|
|
|
|
|
pointer_t curr_pointer = write_pointer_.load(std::memory_order_relaxed);
|
|
|
|
|
buf_list_it_t it = buf_list_.begin();
|
|
|
|
|
buf_list_it_t end_it = buf_list_.end();
|
|
|
|
|
while(it != end_it) {
|
|
|
|
|
Entry* buf = *it;
|
|
|
|
|
Entry* ptr = buf + (pointer % size_);
|
|
|
|
|
Entry* end_ptr = buf + size_;
|
|
|
|
|
while ((ptr < end_ptr) && (pointer < curr_pointer)) {
|
|
|
|
|
if (ptr->valid != TRACE_ENTRY_COMPL) break;
|
|
|
|
|
|
|
|
|
|
entry_type_t type = ptr->type;
|
|
|
|
|
if (type >= NUM_ENTRY_TYPE) FATAL("out of f_array bounds (" << type << ")");
|
|
|
|
|
callback_t f_ptr = f_array_[type];
|
|
|
|
|
if (f_ptr == NULL) FATAL("f_ptr == NULL");
|
|
|
|
|
(*f_ptr)(ptr);
|
|
|
|
|
|
|
|
|
|
ptr++;
|
|
|
|
|
pointer++;
|
|
|
|
|
}
|
2020-08-11 04:44:08 -05:00
|
|
|
|
2020-08-15 02:23:43 -05:00
|
|
|
buf_list_it_t prev = it;
|
|
|
|
|
it++;
|
|
|
|
|
if (ptr == end_ptr) {
|
|
|
|
|
free_fun(*prev);
|
|
|
|
|
buf_list_.erase(prev);
|
2019-07-13 00:38:12 -05:00
|
|
|
}
|
2020-08-15 02:23:43 -05:00
|
|
|
if (pointer == curr_pointer) break;
|
2019-06-24 21:05:12 -05:00
|
|
|
}
|
2020-08-15 02:23:43 -05:00
|
|
|
|
|
|
|
|
read_pointer_ = pointer;
|
2019-07-13 00:38:12 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Entry* allocate_fun() {
|
2019-08-19 23:18:07 -05:00
|
|
|
Entry* ptr = (Entry*) malloc(size_ * sizeof(Entry));
|
2020-01-27 14:30:44 -06:00
|
|
|
if (ptr == NULL) FATAL("malloc failed");
|
2019-07-13 00:38:12 -05:00
|
|
|
//memset(ptr, 0, size_ * sizeof(Entry));
|
2019-06-24 21:05:12 -05:00
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-15 02:23:43 -05:00
|
|
|
inline void free_fun(void* ptr) {
|
|
|
|
|
free(ptr);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-13 00:38:12 -05:00
|
|
|
static void* allocate_worker(void* arg) {
|
|
|
|
|
Obj* obj = (Obj*)arg;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
PTHREAD_CALL(pthread_mutex_lock(&(obj->work_mutex_)));
|
|
|
|
|
while (obj->next_ != NULL) {
|
|
|
|
|
PTHREAD_CALL(pthread_cond_wait(&(obj->work_cond_), &(obj->work_mutex_)));
|
2019-06-24 21:05:12 -05:00
|
|
|
}
|
2019-07-13 00:38:12 -05:00
|
|
|
obj->next_ = obj->allocate_fun();
|
|
|
|
|
PTHREAD_CALL(pthread_mutex_unlock(&(obj->work_mutex_)));
|
2019-06-24 21:05:12 -05:00
|
|
|
}
|
2019-07-13 00:38:12 -05:00
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void wrap_buffer(const pointer_t pointer) {
|
2019-08-11 08:56:39 -05:00
|
|
|
std::lock_guard<mutex_t> lck(mutex_);
|
2020-05-07 06:45:29 -05:00
|
|
|
if (work_thread_started_ == false) StartWorkerThread();
|
2020-01-27 14:30:44 -06:00
|
|
|
|
2019-07-13 00:38:12 -05:00
|
|
|
PTHREAD_CALL(pthread_mutex_lock(&work_mutex_));
|
|
|
|
|
if (pointer >= end_pointer_) {
|
|
|
|
|
data_ = next_;
|
|
|
|
|
next_ = NULL;
|
|
|
|
|
PTHREAD_CALL(pthread_cond_signal(&work_cond_));
|
|
|
|
|
end_pointer_ += size_;
|
2020-01-27 14:30:44 -06:00
|
|
|
if (end_pointer_ == 0) FATAL("pointer overflow");
|
2019-07-13 00:38:12 -05:00
|
|
|
buf_list_.push_back(data_);
|
2019-06-24 21:05:12 -05:00
|
|
|
}
|
2019-07-13 00:38:12 -05:00
|
|
|
PTHREAD_CALL(pthread_mutex_unlock(&work_mutex_));
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-10 00:14:04 -05:00
|
|
|
const char* name_;
|
2020-08-15 02:23:43 -05:00
|
|
|
const uint32_t size_;
|
2019-07-13 00:38:12 -05:00
|
|
|
Entry* data_;
|
|
|
|
|
Entry* next_;
|
2020-08-15 02:23:43 -05:00
|
|
|
pointer_t read_pointer_;
|
|
|
|
|
volatile std::atomic<pointer_t> write_pointer_;
|
2019-08-11 08:56:39 -05:00
|
|
|
volatile std::atomic<pointer_t> end_pointer_;
|
2020-08-15 02:23:43 -05:00
|
|
|
buf_list_t buf_list_;
|
|
|
|
|
callback_t f_array_[NUM_ENTRY_TYPE];
|
2019-07-13 00:38:12 -05:00
|
|
|
|
|
|
|
|
pthread_t work_thread_;
|
|
|
|
|
pthread_mutex_t work_mutex_;
|
|
|
|
|
pthread_cond_t work_cond_;
|
2020-01-27 14:30:44 -06:00
|
|
|
bool work_thread_started_;
|
2019-08-11 08:56:39 -05:00
|
|
|
|
|
|
|
|
mutex_t mutex_;
|
2019-06-24 21:05:12 -05:00
|
|
|
};
|
|
|
|
|
} // namespace roctracer
|
|
|
|
|
|
2020-01-27 14:30:44 -06:00
|
|
|
#define TRACE_BUFFER_INSTANTIATE() \
|
|
|
|
|
roctracer::TraceBufferBase* roctracer::TraceBufferBase::head_elem_ = NULL; \
|
|
|
|
|
roctracer::TraceBufferBase::mutex_t roctracer::TraceBufferBase::mutex_;
|
|
|
|
|
|
2019-06-24 21:05:12 -05:00
|
|
|
#endif // SRC_CORE_TRACE_BUFFER_H_
|