mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-20 22:08:22 +00:00
Added profiler to loginserver as initial work cause it's the smallest service or one of the smallest
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
|
||||
|
||||
SET(eqperf_sources
|
||||
eqp_profile_event.cpp
|
||||
eqp_profile_timer.cpp
|
||||
eqp_profiler.cpp
|
||||
eqp_profiler_node.cpp
|
||||
)
|
||||
|
||||
SET(eqperf_headers
|
||||
eqp_profile_event.h
|
||||
eqp_profile_function.h
|
||||
eqp_profile_timer.h
|
||||
eqp_profiler.h
|
||||
eqp_profiler_node.h
|
||||
)
|
||||
|
||||
ADD_LIBRARY(eqperf SHARED ${eqperf_sources} ${eqperf_headers})
|
||||
|
||||
INSTALL(TARGETS eqperf RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
|
||||
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
@@ -0,0 +1,47 @@
|
||||
#include "eqp_profile_event.h"
|
||||
#include "eqp_profile_timer.h"
|
||||
#include "eqp_profiler.h"
|
||||
|
||||
EQP::CPU::ST::Event::Event(const char *function_name) {
|
||||
function_name_ = function_name;
|
||||
name_ = nullptr;
|
||||
start_ = GetCurrentTimer();
|
||||
|
||||
EQP::CPU::ST::GetProfiler().EventStarted(function_name_, name_);
|
||||
}
|
||||
|
||||
EQP::CPU::ST::Event::Event(const char *function_name, const char *name) {
|
||||
function_name_ = function_name;
|
||||
name_ = name;
|
||||
start_ = GetCurrentTimer();
|
||||
|
||||
EQP::CPU::ST::GetProfiler().EventStarted(function_name_, name_);
|
||||
}
|
||||
|
||||
EQP::CPU::ST::Event::~Event() {
|
||||
uint64_t end = GetCurrentTimer();
|
||||
|
||||
EQP::CPU::ST::GetProfiler().EventFinished(end - start_);
|
||||
}
|
||||
|
||||
EQP::CPU::MT::Event::Event(const char *function_name) {
|
||||
function_name_ = function_name;
|
||||
name_ = nullptr;
|
||||
start_ = GetCurrentTimer();
|
||||
|
||||
EQP::CPU::MT::GetProfiler().EventStarted(function_name_, name_);
|
||||
}
|
||||
|
||||
EQP::CPU::MT::Event::Event(const char *function_name, const char *name) {
|
||||
function_name_ = function_name;
|
||||
name_ = name;
|
||||
start_ = GetCurrentTimer();
|
||||
|
||||
EQP::CPU::MT::GetProfiler().EventStarted(function_name_, name_);
|
||||
}
|
||||
|
||||
EQP::CPU::MT::Event::~Event() {
|
||||
uint64_t end = GetCurrentTimer();
|
||||
|
||||
EQP::CPU::MT::GetProfiler().EventFinished(end - start_);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "eqp_profile_function.h"
|
||||
|
||||
namespace EQP
|
||||
{
|
||||
namespace CPU
|
||||
{
|
||||
namespace ST
|
||||
{
|
||||
class EQP_EXPORT Event
|
||||
{
|
||||
public:
|
||||
Event(const char *function_name);
|
||||
Event(const char *function_name, const char *name);
|
||||
~Event();
|
||||
private:
|
||||
const char *function_name_;
|
||||
const char *name_;
|
||||
uint64_t start_;
|
||||
};
|
||||
}
|
||||
|
||||
namespace MT
|
||||
{
|
||||
class EQP_EXPORT Event
|
||||
{
|
||||
public:
|
||||
Event(const char *function_name);
|
||||
Event(const char *function_name, const char *name);
|
||||
~Event();
|
||||
private:
|
||||
const char *function_name_;
|
||||
const char *name_;
|
||||
uint64_t start_;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // Profile
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __PRETTY_FUNCTION__
|
||||
#ifdef _MSC_VER
|
||||
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||
#else
|
||||
#define __PRETTY_FUNCTION__ __FUNCTION__
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define EQP_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define EQP_EXPORT
|
||||
#endif
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "eqp_profile_timer.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
uint64_t EQP::GetCurrentTimer()
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
LARGE_INTEGER qpt_i;
|
||||
QueryPerformanceCounter(&qpt_i);
|
||||
return qpt_i.QuadPart;
|
||||
#else
|
||||
timespec tp;
|
||||
if(clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
|
||||
uint64_t res = tp.tv_sec * 1000000000;
|
||||
res += tp.tv_nsec;
|
||||
return res;
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace EQP
|
||||
{
|
||||
uint64_t GetCurrentTimer();
|
||||
} // EQP
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
#include "eqp_profiler.h"
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
EQP::CPU::ST::Profiler st_profiler;
|
||||
EQP::CPU::MT::Profiler mt_profiler;
|
||||
|
||||
struct EQP::CPU::MT::Profiler::impl
|
||||
{
|
||||
std::mutex lock_;
|
||||
std::unordered_map<std::thread::id, ThreadInfo*> nodes_;
|
||||
};
|
||||
|
||||
EQP::CPU::ST::Profiler &EQP::CPU::ST::GetProfiler() {
|
||||
return st_profiler;
|
||||
}
|
||||
|
||||
EQP::CPU::MT::Profiler &EQP::CPU::MT::GetProfiler() {
|
||||
return mt_profiler;
|
||||
}
|
||||
|
||||
EQP::CPU::ST::Profiler::Profiler() {
|
||||
root_ = new ProfilerNode;
|
||||
root_->SetParent(root_);
|
||||
current_ = root_;
|
||||
}
|
||||
|
||||
EQP::CPU::ST::Profiler::~Profiler() {
|
||||
delete root_;
|
||||
}
|
||||
|
||||
void EQP::CPU::ST::Profiler::EventStarted(const char *func, const char *name) {
|
||||
std::string cur_name = func;
|
||||
if(name) {
|
||||
cur_name += " - ";
|
||||
cur_name += name;
|
||||
}
|
||||
|
||||
auto search = current_->GetNodes().find(cur_name);
|
||||
if(search != current_->GetNodes().end()) {
|
||||
current_ = search->second;
|
||||
} else {
|
||||
ProfilerNode *t = new ProfilerNode;
|
||||
t->SetParent(current_);
|
||||
current_->GetNodes()[cur_name] = t;
|
||||
current_ = t;
|
||||
}
|
||||
}
|
||||
|
||||
void EQP::CPU::ST::Profiler::EventFinished(uint64_t time) {
|
||||
current_->GetTime() += time;
|
||||
current_->GetCount()++;
|
||||
current_ = current_->GetParent();
|
||||
}
|
||||
|
||||
void EQP::CPU::ST::Profiler::Clear() {
|
||||
for(auto &iter : root_->GetNodes()) {
|
||||
delete iter.second;
|
||||
}
|
||||
root_->GetNodes().clear();
|
||||
|
||||
root_->SetTime(0);
|
||||
root_->SetCount(0);
|
||||
current_ = root_;
|
||||
}
|
||||
|
||||
void EQP::CPU::ST::Profiler::Dump(std::ostream &stream) {
|
||||
uint64_t total = 0;
|
||||
std::vector<ProfilerNodeDump> sorted_vec;
|
||||
sorted_vec.reserve(root_->GetNodes().size() + 1);
|
||||
|
||||
for(auto &iter : root_->GetNodes()) {
|
||||
ProfilerNodeDump n;
|
||||
n.name = iter.first;
|
||||
n.node = iter.second;
|
||||
sorted_vec.push_back(n);
|
||||
total += iter.second->GetTime();
|
||||
}
|
||||
|
||||
std::sort(sorted_vec.begin(), sorted_vec.end(),
|
||||
[](const ProfilerNodeDump& a, const ProfilerNodeDump& b) { return a.node->GetTime() > b.node->GetTime(); });
|
||||
|
||||
for(auto &iter : sorted_vec) {
|
||||
iter.node->Dump(stream, iter.name, total, 0);
|
||||
}
|
||||
}
|
||||
|
||||
EQP::CPU::MT::Profiler::ThreadInfo::ThreadInfo() {
|
||||
root_ = new ProfilerNode;
|
||||
root_->SetParent(root_);
|
||||
current_ = root_;
|
||||
}
|
||||
|
||||
EQP::CPU::MT::Profiler::ThreadInfo::~ThreadInfo() {
|
||||
delete root_;
|
||||
}
|
||||
|
||||
EQP::CPU::MT::Profiler::Profiler() {
|
||||
imp_ = new impl;
|
||||
}
|
||||
|
||||
EQP::CPU::MT::Profiler::~Profiler() {
|
||||
delete imp_;
|
||||
}
|
||||
|
||||
void EQP::CPU::MT::Profiler::EventStarted(const char *func, const char *name) {
|
||||
std::string cur_name = func;
|
||||
if(name) {
|
||||
cur_name += " - ";
|
||||
cur_name += name;
|
||||
}
|
||||
|
||||
ThreadInfo *ti = nullptr;
|
||||
imp_->lock_.lock();
|
||||
auto ti_search = imp_->nodes_.find(std::this_thread::get_id());
|
||||
if(ti_search == imp_->nodes_.end()) {
|
||||
ti = new ThreadInfo;
|
||||
imp_->nodes_[std::this_thread::get_id()] = ti;
|
||||
} else {
|
||||
ti = ti_search->second;
|
||||
}
|
||||
imp_->lock_.unlock();
|
||||
|
||||
auto search = ti->current_->GetNodes().find(cur_name);
|
||||
if(search != ti->current_->GetNodes().end()) {
|
||||
ti->current_ = search->second;
|
||||
}
|
||||
else {
|
||||
ProfilerNode *t = new ProfilerNode;
|
||||
t->SetParent(ti->current_);
|
||||
ti->current_->GetNodes()[cur_name] = t;
|
||||
ti->current_ = t;
|
||||
}
|
||||
}
|
||||
|
||||
void EQP::CPU::MT::Profiler::EventFinished(uint64_t time) {
|
||||
ThreadInfo *ti = nullptr;
|
||||
imp_->lock_.lock();
|
||||
auto ti_search = imp_->nodes_.find(std::this_thread::get_id());
|
||||
if(ti_search == imp_->nodes_.end()) {
|
||||
imp_->lock_.unlock();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ti = ti_search->second;
|
||||
}
|
||||
imp_->lock_.unlock();
|
||||
|
||||
ti->current_->GetTime() += time;
|
||||
ti->current_->GetCount()++;
|
||||
ti->current_ = ti->current_->GetParent();
|
||||
}
|
||||
|
||||
void EQP::CPU::MT::Profiler::Clear() {
|
||||
imp_->lock_.lock();
|
||||
for(auto &iter : imp_->nodes_) {
|
||||
delete iter.second;
|
||||
}
|
||||
|
||||
imp_->nodes_.clear();
|
||||
imp_->lock_.unlock();
|
||||
}
|
||||
|
||||
void EQP::CPU::MT::Profiler::Dump(std::ostream &stream) {
|
||||
imp_->lock_.lock();
|
||||
for(auto &iter : imp_->nodes_) {
|
||||
stream << "Thread: " << iter.first << std::endl;
|
||||
uint64_t total = 0;
|
||||
std::vector<ProfilerNodeDump> sorted_vec;
|
||||
sorted_vec.reserve(iter.second->root_->GetNodes().size() + 1);
|
||||
|
||||
for(auto &t_iter : iter.second->root_->GetNodes()) {
|
||||
ProfilerNodeDump n;
|
||||
n.name = t_iter.first;
|
||||
n.node = t_iter.second;
|
||||
sorted_vec.push_back(n);
|
||||
total += t_iter.second->GetTime();
|
||||
}
|
||||
|
||||
std::sort(sorted_vec.begin(), sorted_vec.end(),
|
||||
[](const ProfilerNodeDump& a, const ProfilerNodeDump& b) { return a.node->GetTime() > b.node->GetTime(); });
|
||||
|
||||
for(auto &t_iter : sorted_vec) {
|
||||
t_iter.node->Dump(stream, t_iter.name, total, 1);
|
||||
}
|
||||
|
||||
stream << std::endl;
|
||||
}
|
||||
imp_->lock_.unlock();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef EQPERF_ENABLED
|
||||
|
||||
#include <string>
|
||||
#include "eqp_profile_event.h"
|
||||
#include "eqp_profiler_node.h"
|
||||
|
||||
#define eqp_comb_fin(x, y) x##y
|
||||
#define eqp_comb(x, y) eqp_comb_fin(x, y)
|
||||
#define _eqp EQP::CPU::ST::Event eqp_comb(eq_perf_event_, __LINE__) (__PRETTY_FUNCTION__);
|
||||
#define _eqpn(x) EQP::CPU::ST::Event eqp_comb(eq_perf_event_, __LINE__) (__PRETTY_FUNCTION__, x);
|
||||
#define _eqp_mt EQP::CPU::MT::Event eqp_comb(eq_perf_event_, __LINE__) (__PRETTY_FUNCTION__);
|
||||
#define _eqpn_mt(x) EQP::CPU::MT::Event eqp_comb(eq_perf_event_, __LINE__) (__PRETTY_FUNCTION__, x);
|
||||
|
||||
namespace EQP
|
||||
{
|
||||
namespace CPU
|
||||
{
|
||||
namespace ST
|
||||
{
|
||||
class EQP_EXPORT Profiler
|
||||
{
|
||||
typedef EQP::CPU::ProfilerNode Node;
|
||||
public:
|
||||
Profiler();
|
||||
~Profiler();
|
||||
|
||||
void EventStarted(const char *func, const char *name);
|
||||
void EventFinished(uint64_t time);
|
||||
void Clear();
|
||||
void Dump(std::ostream &stream);
|
||||
private:
|
||||
Node *root_;
|
||||
Node *current_;
|
||||
};
|
||||
|
||||
EQP_EXPORT Profiler &GetProfiler();
|
||||
}
|
||||
|
||||
namespace MT
|
||||
{
|
||||
class EQP_EXPORT Profiler
|
||||
{
|
||||
typedef EQP::CPU::ProfilerNode Node;
|
||||
class ThreadInfo {
|
||||
public:
|
||||
ThreadInfo();
|
||||
~ThreadInfo();
|
||||
Node *root_;
|
||||
Node *current_;
|
||||
};
|
||||
public:
|
||||
Profiler();
|
||||
~Profiler();
|
||||
|
||||
void EventStarted(const char *func, const char *name);
|
||||
void EventFinished(uint64_t time);
|
||||
void Clear();
|
||||
void Dump(std::ostream &stream);
|
||||
private:
|
||||
struct impl;
|
||||
impl *imp_;
|
||||
};
|
||||
|
||||
EQP_EXPORT Profiler &GetProfiler();
|
||||
}
|
||||
} // CPU
|
||||
} // EQP
|
||||
|
||||
#else
|
||||
|
||||
#define _eqp
|
||||
#define _eqpn(x)
|
||||
#define _eqp_mt
|
||||
#define _eqpn_mt(x)
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "eqp_profiler_node.h"
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
EQP::CPU::ProfilerNode::ProfilerNode() {
|
||||
count_ = 0;
|
||||
time_ = 0;
|
||||
parent_ = nullptr;;
|
||||
}
|
||||
|
||||
EQP::CPU::ProfilerNode::~ProfilerNode() {
|
||||
for(auto &iter : nodes_) {
|
||||
delete iter.second;
|
||||
}
|
||||
}
|
||||
|
||||
void EQP::CPU::ProfilerNode::Dump(std::ostream &stream, const std::string &func, uint64_t total_time, int node_level) {
|
||||
|
||||
if(node_level >= 1) {
|
||||
stream << std::setw(node_level * 2) << " ";
|
||||
}
|
||||
|
||||
double m_cycles = time_ / 1000000.0;
|
||||
double m_avg_cycles = m_cycles / count_;
|
||||
double percentage = time_ * 100 / static_cast<double>(total_time);
|
||||
|
||||
std::streamsize p = stream.precision();
|
||||
|
||||
stream << std::fixed;
|
||||
stream.precision(2);
|
||||
stream << m_cycles << "M cycles, " << count_ << " calls, " << m_avg_cycles << "M cycles avg, ";
|
||||
stream << func.c_str() << " ";
|
||||
stream << percentage << "%";
|
||||
stream << std::endl;
|
||||
stream.precision(p);
|
||||
|
||||
std::vector<ProfilerNodeDump> sorted_vec;
|
||||
sorted_vec.reserve(nodes_.size() + 1);
|
||||
|
||||
for(auto &iter : nodes_) {
|
||||
ProfilerNodeDump n;
|
||||
n.name = iter.first;
|
||||
n.node = iter.second;
|
||||
sorted_vec.push_back(n);
|
||||
}
|
||||
|
||||
std::sort(sorted_vec.begin(), sorted_vec.end(),
|
||||
[](const ProfilerNodeDump& a, const ProfilerNodeDump& b) { return a.node->GetTime() > b.node->GetTime(); });
|
||||
|
||||
for(auto &iter : sorted_vec) {
|
||||
iter.node->Dump(stream, iter.name, total_time, node_level + 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace EQP
|
||||
{
|
||||
namespace CPU
|
||||
{
|
||||
class ProfilerNode
|
||||
{
|
||||
public:
|
||||
ProfilerNode();
|
||||
~ProfilerNode();
|
||||
|
||||
inline void SetCount(uint64_t c) { count_ = c; }
|
||||
inline uint64_t& GetCount() { return count_; }
|
||||
|
||||
inline void SetTime(uint64_t t) { time_ = t; }
|
||||
inline uint64_t& GetTime() { return time_; }
|
||||
|
||||
inline void SetParent(ProfilerNode *p) { parent_ = p; }
|
||||
inline ProfilerNode* GetParent() { return parent_; }
|
||||
|
||||
inline std::unordered_map<std::string, ProfilerNode*>& GetNodes() { return nodes_; }
|
||||
|
||||
void Dump(std::ostream &stream, const std::string &func, uint64_t total_time, int node_level);
|
||||
private:
|
||||
uint64_t count_;
|
||||
uint64_t time_;
|
||||
ProfilerNode *parent_;
|
||||
std::unordered_map<std::string, ProfilerNode*> nodes_;
|
||||
};
|
||||
|
||||
struct ProfilerNodeDump
|
||||
{
|
||||
std::string name;
|
||||
ProfilerNode *node;
|
||||
};
|
||||
} // CPU
|
||||
} // EQP
|
||||
|
||||
Reference in New Issue
Block a user