mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-27 00:31:02 +08:00
(profiler_core) !! Non-blocking API using Thread-Local-Storage (threal_local or __declspec(thread) or __thread)
This commit is contained in:
parent
31705d5daf
commit
10bb3da45b
@ -20,7 +20,23 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
#define EASY_PROFILER____H_______
|
||||
|
||||
#ifdef _WIN32
|
||||
#define __func__ __FUNCTION__
|
||||
# define __func__ __FUNCTION__
|
||||
# if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
// There is no support for C++11 thread_local keyword prior to Visual Studio 2015. Use __declspec(thread) instead.
|
||||
# define EASY_THREAD_LOCAL __declspec(thread)
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
# if (__GNUC__ == 4 && __GNUC_MINOR__ < 8) || (__GNUC__ < 4)
|
||||
// There is no support for C++11 thread_local keyword prior to gcc 4.8. Use __thread instead.
|
||||
# define EASY_THREAD_LOCAL __thread
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// TODO: Check thread_local support for Clang!
|
||||
|
||||
#ifndef EASY_THREAD_LOCAL
|
||||
# define EASY_THREAD_LOCAL thread_local
|
||||
# define EASY_THREAD_LOCAL_CPP11
|
||||
#endif
|
||||
|
||||
#if defined ( __clang__ )
|
||||
@ -80,9 +96,9 @@ Block will be automatically completed by destructor.
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_BLOCK(name, ...)\
|
||||
static const ::profiler::StaticBlockDescriptor EASY_UNIQUE_DESC(__LINE__)(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__).id(), EASY_RUNTIME_NAME(name));\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
|
||||
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
|
||||
|
||||
/** Macro of beginning of block with function name and custom color.
|
||||
@ -105,9 +121,9 @@ Name of the block automatically created with function name.
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_FUNCTION(...)\
|
||||
static const ::profiler::StaticBlockDescriptor EASY_UNIQUE_DESC(__LINE__)(__func__, __FILE__, __LINE__,\
|
||||
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__).id(), "");\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__), "");\
|
||||
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
|
||||
|
||||
/** Macro of completion of last nearest open block.
|
||||
@ -145,9 +161,9 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_EVENT(name, ...)\
|
||||
static const ::profiler::StaticBlockDescriptor EASY_UNIQUE_DESC(__LINE__)(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_EVENT, EASY_UNIQUE_DESC(__LINE__).id(), EASY_RUNTIME_NAME(name));\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_EVENT, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
|
||||
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
|
||||
|
||||
/** Macro enabling profiler
|
||||
@ -166,10 +182,15 @@ If this thread has been already named then nothing changes.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
#define EASY_THREAD(name) ::profiler::setThreadName(name, __FILE__, __func__, __LINE__);
|
||||
#ifdef EASY_THREAD_LOCAL_CPP11
|
||||
#define EASY_THREAD(name)\
|
||||
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = \
|
||||
::profiler::setThreadName(name, __FILE__, __func__, __LINE__);
|
||||
#else
|
||||
#define EASY_THREAD(name) thread_local static const ::profiler::ThreadNameSetter EASY_TOKEN_CONCATENATE(unique_profiler_thread_name_setter_, __LINE__)(name, __FILE__, __func__, __LINE__);
|
||||
#define EASY_THREAD(name)\
|
||||
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\
|
||||
if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\
|
||||
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__);
|
||||
#endif
|
||||
|
||||
/** Macro of naming main thread.
|
||||
@ -211,20 +232,9 @@ class ThreadStorage;
|
||||
namespace profiler {
|
||||
|
||||
class Block;
|
||||
class StaticBlockDescriptor;
|
||||
|
||||
extern "C"{
|
||||
void PROFILER_API beginBlock(Block& _block);
|
||||
void PROFILER_API endBlock();
|
||||
void PROFILER_API setEnabled(bool isEnable);
|
||||
uint32_t PROFILER_API dumpBlocksToFile(const char* filename);
|
||||
void PROFILER_API setThreadName(const char* name, const char* filename, const char* _funcname, int line);
|
||||
void PROFILER_API setContextSwitchLogFilename(const char* name);
|
||||
PROFILER_API const char* getContextSwitchLogFilename();
|
||||
}
|
||||
|
||||
typedef uint64_t timestamp_t;
|
||||
typedef uint32_t thread_id_t;
|
||||
typedef uint64_t timestamp_t;
|
||||
typedef uint32_t thread_id_t;
|
||||
typedef uint32_t block_id_t;
|
||||
|
||||
enum BlockType : uint8_t
|
||||
@ -237,6 +247,17 @@ namespace profiler {
|
||||
BLOCK_TYPES_NUMBER
|
||||
};
|
||||
typedef BlockType block_type_t;
|
||||
|
||||
extern "C" {
|
||||
PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = DefaultBlockColor);
|
||||
PROFILER_API void beginBlock(Block& _block);
|
||||
PROFILER_API void endBlock();
|
||||
PROFILER_API void setEnabled(bool isEnable);
|
||||
PROFILER_API uint32_t dumpBlocksToFile(const char* filename);
|
||||
PROFILER_API const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line);
|
||||
PROFILER_API void setContextSwitchLogFilename(const char* name);
|
||||
PROFILER_API const char* getContextSwitchLogFilename();
|
||||
}
|
||||
|
||||
#pragma pack(push,1)
|
||||
class PROFILER_API BaseBlockDescriptor
|
||||
@ -322,28 +343,6 @@ namespace profiler {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
class PROFILER_API StaticBlockDescriptor final
|
||||
{
|
||||
block_id_t m_id;
|
||||
|
||||
public:
|
||||
|
||||
StaticBlockDescriptor(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = DefaultBlockColor);
|
||||
inline block_id_t id() const { return m_id; }
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
struct PROFILER_API ThreadNameSetter final
|
||||
{
|
||||
ThreadNameSetter(const char* _name, const char* _filename, const char* _funcname, int _line)
|
||||
{
|
||||
setThreadName(_name, _filename, _funcname, _line);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
#if defined ( __clang__ )
|
||||
|
@ -48,9 +48,9 @@ namespace profiler {
|
||||
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
|
||||
const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
|
||||
|
||||
static const ::profiler::StaticBlockDescriptor desc("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
MANAGER._cswitchBeginBlock(time, desc.id(), _contextSwitchEvent->OldThreadId);
|
||||
MANAGER._cswitchEndBlock(_contextSwitchEvent->NewThreadId, time);
|
||||
static const auto desc = MANAGER.addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc);
|
||||
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -10,16 +10,6 @@
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define EASY_INTERNAL_BLOCK(name, easyType, CODE) CODE
|
||||
//#define EASY_INTERNAL_BLOCK(name, easyType, CODE)\
|
||||
// static const profiler::StaticBlockDescriptor EASY_UNIQUE_DESC(__LINE__)(name, __FILE__, __LINE__, easyType, profiler::colors::White);\
|
||||
// ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(easyType, EASY_UNIQUE_DESC(__LINE__).id(), "");\
|
||||
// CODE\
|
||||
// EASY_UNIQUE_BLOCK(__LINE__).finish();\
|
||||
// thread_storage.storeBlock(EASY_UNIQUE_BLOCK(__LINE__))
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using namespace profiler;
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -29,14 +19,19 @@ extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
|
||||
//auto& MANAGER = ProfileManager::instance();
|
||||
#define MANAGER ProfileManager::instance()
|
||||
|
||||
extern "C"{
|
||||
extern "C" {
|
||||
|
||||
void PROFILER_API endBlock()
|
||||
PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
||||
{
|
||||
return MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color);
|
||||
}
|
||||
|
||||
PROFILER_API void endBlock()
|
||||
{
|
||||
MANAGER.endBlock();
|
||||
}
|
||||
|
||||
void PROFILER_API setEnabled(bool isEnable)
|
||||
PROFILER_API void setEnabled(bool isEnable)
|
||||
{
|
||||
MANAGER.setEnabled(isEnable);
|
||||
#ifdef _WIN32
|
||||
@ -47,22 +42,22 @@ extern "C"{
|
||||
#endif
|
||||
}
|
||||
|
||||
void PROFILER_API beginBlock(Block& _block)
|
||||
PROFILER_API void beginBlock(Block& _block)
|
||||
{
|
||||
MANAGER.beginBlock(_block);
|
||||
}
|
||||
|
||||
uint32_t PROFILER_API dumpBlocksToFile(const char* filename)
|
||||
PROFILER_API uint32_t dumpBlocksToFile(const char* filename)
|
||||
{
|
||||
return MANAGER.dumpBlocksToFile(filename);
|
||||
}
|
||||
|
||||
void PROFILER_API setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
||||
PROFILER_API const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
||||
{
|
||||
return MANAGER.setThreadName(name, filename, _funcname, line);
|
||||
}
|
||||
|
||||
void PROFILER_API setContextSwitchLogFilename(const char* name)
|
||||
PROFILER_API void setContextSwitchLogFilename(const char* name)
|
||||
{
|
||||
return MANAGER.setContextSwitchLogFilename(name);
|
||||
}
|
||||
@ -84,12 +79,6 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
StaticBlockDescriptor::StaticBlockDescriptor(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
||||
: m_id(MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color))
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BaseBlockDescriptor::BaseBlockDescriptor(int _line, block_type_t _block_type, color_t _color)
|
||||
: m_line(_line)
|
||||
, m_type(_block_type)
|
||||
@ -136,6 +125,8 @@ void ThreadStorage::clearClosed()
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
|
||||
|
||||
// #ifdef _WIN32
|
||||
// LPTOP_LEVEL_EXCEPTION_FILTER PREVIOUS_FILTER = NULL;
|
||||
// LONG WINAPI easyTopLevelExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo)
|
||||
@ -175,32 +166,30 @@ void ProfileManager::beginBlock(Block& _block)
|
||||
if (!m_isEnabled)
|
||||
return;
|
||||
|
||||
EASY_INTERNAL_BLOCK("Easy.Spin", profiler::BLOCK_TYPE_BLOCK,\
|
||||
auto& thread_storage = threadStorage(getCurrentThreadId());\
|
||||
);
|
||||
if (THREAD_STORAGE == nullptr)
|
||||
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
||||
|
||||
EASY_INTERNAL_BLOCK("Easy.Insert", profiler::BLOCK_TYPE_BLOCK,\
|
||||
if (!_block.isFinished())
|
||||
thread_storage.blocks.openedList.emplace(_block);
|
||||
else
|
||||
thread_storage.storeBlock(_block);\
|
||||
);
|
||||
if (!_block.isFinished())
|
||||
THREAD_STORAGE->blocks.openedList.emplace(_block);
|
||||
else
|
||||
THREAD_STORAGE->storeBlock(_block);
|
||||
}
|
||||
|
||||
void ProfileManager::_cswitchBeginBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id)
|
||||
void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id)
|
||||
{
|
||||
auto thread_storage = _threadStorage(_thread_id);
|
||||
if (thread_storage != nullptr)
|
||||
thread_storage->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
|
||||
auto ts = findThreadStorage(_thread_id);
|
||||
if (ts != nullptr)
|
||||
ts->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
|
||||
}
|
||||
|
||||
void ProfileManager::_cswitchStoreBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id)
|
||||
void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id)
|
||||
{
|
||||
auto thread_storage = _threadStorage(_thread_id);
|
||||
if (thread_storage != nullptr){
|
||||
auto ts = findThreadStorage(_thread_id);
|
||||
if (ts != nullptr)
|
||||
{
|
||||
profiler::Block lastBlock(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
|
||||
lastBlock.finish(_time);
|
||||
thread_storage->storeCSwitch(lastBlock);
|
||||
ts->storeCSwitch(lastBlock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,29 +198,28 @@ void ProfileManager::endBlock()
|
||||
if (!m_isEnabled)
|
||||
return;
|
||||
|
||||
auto& thread_storage = threadStorage(getCurrentThreadId());
|
||||
if (thread_storage.blocks.openedList.empty())
|
||||
if (THREAD_STORAGE->blocks.openedList.empty())
|
||||
return;
|
||||
|
||||
Block& lastBlock = thread_storage.blocks.openedList.top();
|
||||
Block& lastBlock = THREAD_STORAGE->blocks.openedList.top();
|
||||
if (!lastBlock.isFinished())
|
||||
lastBlock.finish();
|
||||
|
||||
thread_storage.storeBlock(lastBlock);
|
||||
thread_storage.blocks.openedList.pop();
|
||||
THREAD_STORAGE->storeBlock(lastBlock);
|
||||
THREAD_STORAGE->blocks.openedList.pop();
|
||||
}
|
||||
|
||||
void ProfileManager::_cswitchEndBlock(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime)
|
||||
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime)
|
||||
{
|
||||
auto thread_storage = _threadStorage(_thread_id);
|
||||
if (thread_storage == nullptr || thread_storage->sync.openedList.empty())
|
||||
auto ts = findThreadStorage(_thread_id);
|
||||
if (ts == nullptr || ts->sync.openedList.empty())
|
||||
return;
|
||||
|
||||
Block& lastBlock = thread_storage->sync.openedList.top();
|
||||
Block& lastBlock = ts->sync.openedList.top();
|
||||
lastBlock.finish(_endtime);
|
||||
|
||||
thread_storage->storeCSwitch(lastBlock);
|
||||
thread_storage->sync.openedList.pop();
|
||||
ts->storeCSwitch(lastBlock);
|
||||
ts->sync.openedList.pop();
|
||||
}
|
||||
|
||||
void ProfileManager::setEnabled(bool isEnable)
|
||||
@ -283,11 +271,11 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
|
||||
|
||||
if(infile.is_open())
|
||||
{
|
||||
while (infile >> timestamp >> thread_from >> thread_to )
|
||||
static const auto desc = addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
while (infile >> timestamp >> thread_from >> thread_to)
|
||||
{
|
||||
static const ::profiler::StaticBlockDescriptor desc("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
_cswitchBeginBlock(timestamp, desc.id(), thread_from);
|
||||
_cswitchEndBlock(thread_to, timestamp);
|
||||
beginContextSwitch(thread_from, timestamp, desc);
|
||||
endContextSwitch(thread_to, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,15 +373,22 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
|
||||
return blocks_number;
|
||||
}
|
||||
|
||||
void ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
||||
const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
||||
{
|
||||
auto& thread_storage = threadStorage(getCurrentThreadId());
|
||||
if (thread_storage.named)
|
||||
return;
|
||||
if (THREAD_STORAGE == nullptr)
|
||||
{
|
||||
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
||||
}
|
||||
|
||||
const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Random);
|
||||
thread_storage.storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name));
|
||||
thread_storage.named = true;
|
||||
if (!THREAD_STORAGE->named)
|
||||
{
|
||||
const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Random);
|
||||
THREAD_STORAGE->storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name));
|
||||
THREAD_STORAGE->name = name;
|
||||
THREAD_STORAGE->named = true;
|
||||
}
|
||||
|
||||
return THREAD_STORAGE->name.c_str();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -26,8 +26,8 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
#include <stack>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -44,10 +44,10 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
inline uint32_t getCurrentThreadId()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (uint32_t)::GetCurrentThreadId();
|
||||
return (uint32_t)::GetCurrentThreadId();
|
||||
#else
|
||||
thread_local static pid_t x = syscall(__NR_gettid);
|
||||
thread_local static uint32_t _id = (uint32_t)x;//std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
EASY_THREAD_LOCAL static const pid_t x = syscall(__NR_gettid);
|
||||
EASY_THREAD_LOCAL static const uint32_t _id = (uint32_t)x;//std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
return _id;
|
||||
#endif
|
||||
}
|
||||
@ -150,6 +150,7 @@ public:
|
||||
|
||||
BlocksList<std::reference_wrapper<profiler::Block>, SIZEOF_CSWITCH * (uint16_t)1024U> blocks;
|
||||
BlocksList<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync;
|
||||
std::string name;
|
||||
bool named = false;
|
||||
|
||||
void storeBlock(const profiler::Block& _block);
|
||||
@ -161,8 +162,6 @@ public:
|
||||
|
||||
class ProfileManager final
|
||||
{
|
||||
friend profiler::StaticBlockDescriptor;
|
||||
|
||||
ProfileManager();
|
||||
ProfileManager(const ProfileManager& p) = delete;
|
||||
ProfileManager& operator=(const ProfileManager&) = delete;
|
||||
@ -185,11 +184,20 @@ public:
|
||||
static ProfileManager& instance();
|
||||
~ProfileManager();
|
||||
|
||||
template <class ... TArgs>
|
||||
uint32_t addBlockDescriptor(TArgs ... _args)
|
||||
{
|
||||
guard_lock_t lock(m_storedSpin);
|
||||
const auto id = static_cast<uint32_t>(m_descriptors.size());
|
||||
m_descriptors.emplace_back(m_usedMemorySize, _args...);
|
||||
return id;
|
||||
}
|
||||
|
||||
void beginBlock(profiler::Block& _block);
|
||||
void endBlock();
|
||||
void setEnabled(bool isEnable);
|
||||
uint32_t dumpBlocksToFile(const char* filename);
|
||||
void setThreadName(const char* name, const char* filename, const char* _funcname, int line);
|
||||
const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line);
|
||||
|
||||
void setContextSwitchLogFilename(const char* name)
|
||||
{
|
||||
@ -201,28 +209,19 @@ public:
|
||||
return m_csInfoFilename.c_str();
|
||||
}
|
||||
|
||||
void _cswitchBeginBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id);
|
||||
void _cswitchStoreBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id);
|
||||
void _cswitchEndBlock(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime);
|
||||
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id);
|
||||
void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id);
|
||||
void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime);
|
||||
|
||||
private:
|
||||
|
||||
template <class ... TArgs>
|
||||
uint32_t addBlockDescriptor(TArgs ... _args)
|
||||
{
|
||||
guard_lock_t lock(m_storedSpin);
|
||||
const auto id = static_cast<uint32_t>(m_descriptors.size());
|
||||
m_descriptors.emplace_back(m_usedMemorySize, _args...);
|
||||
return id;
|
||||
}
|
||||
|
||||
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
return m_threads[_thread_id];
|
||||
}
|
||||
|
||||
ThreadStorage* _threadStorage(profiler::thread_id_t _thread_id)
|
||||
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
auto it = m_threads.find(_thread_id);
|
||||
|
128
src/spin_lock.h
128
src/spin_lock.h
@ -16,53 +16,87 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
#ifndef ________SPIN_LOCK__________H______
|
||||
#define ________SPIN_LOCK__________H______
|
||||
#ifndef EASY_PROFILER__SPIN_LOCK__________H______
|
||||
#define EASY_PROFILER__SPIN_LOCK__________H______
|
||||
|
||||
#define EASY_USE_CRITICAL_SECTION // Use CRITICAL_SECTION instead of std::atomic_flag
|
||||
|
||||
#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION)
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <atomic>
|
||||
|
||||
namespace profiler{
|
||||
|
||||
class spin_lock
|
||||
{
|
||||
std::atomic_flag m_lock;
|
||||
public:
|
||||
void lock()
|
||||
{
|
||||
while (m_lock.test_and_set(std::memory_order_acquire)){}
|
||||
}
|
||||
|
||||
void unlock()
|
||||
{
|
||||
m_lock.clear(std::memory_order_release);
|
||||
}
|
||||
|
||||
spin_lock(){
|
||||
m_lock.clear();
|
||||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
class guard_lock
|
||||
{
|
||||
T& m_mutex;
|
||||
bool m_isLocked = false;
|
||||
public:
|
||||
explicit guard_lock(T& m) :m_mutex(m){
|
||||
m_mutex.lock();
|
||||
m_isLocked = true;
|
||||
}
|
||||
~guard_lock(){
|
||||
this->release();
|
||||
}
|
||||
inline void release(){
|
||||
if (m_isLocked){
|
||||
m_mutex.unlock();
|
||||
m_isLocked = false;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace profiler {
|
||||
|
||||
#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION)
|
||||
// std::atomic_flag on Windows works slower than critical section, so we will use it instead of std::atomic_flag...
|
||||
// By the way, Windows critical sections are slower than std::atomic_flag on Unix.
|
||||
class spin_lock final { CRITICAL_SECTION m_lock; public:
|
||||
|
||||
void lock() {
|
||||
EnterCriticalSection(&m_lock);
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
LeaveCriticalSection(&m_lock);
|
||||
}
|
||||
|
||||
spin_lock() {
|
||||
InitializeCriticalSection(&m_lock);
|
||||
}
|
||||
|
||||
~spin_lock() {
|
||||
DeleteCriticalSection(&m_lock);
|
||||
}
|
||||
};
|
||||
#else
|
||||
// std::atomic_flag on Unix works fine and very fast (almost instant!)
|
||||
class spin_lock final { ::std::atomic_flag m_lock; public:
|
||||
|
||||
void lock() {
|
||||
while (m_lock.test_and_set(::std::memory_order_acquire));
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
m_lock.clear(::std::memory_order_release);
|
||||
}
|
||||
|
||||
spin_lock() {
|
||||
m_lock.clear();
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <class T>
|
||||
class guard_lock final
|
||||
{
|
||||
T& m_lock;
|
||||
bool m_isLocked = false;
|
||||
|
||||
public:
|
||||
|
||||
explicit guard_lock(T& m) : m_lock(m) {
|
||||
m_lock.lock();
|
||||
m_isLocked = true;
|
||||
}
|
||||
|
||||
~guard_lock() {
|
||||
unlock();
|
||||
}
|
||||
|
||||
inline void unlock() {
|
||||
if (m_isLocked) {
|
||||
m_lock.unlock();
|
||||
m_isLocked = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
#ifdef EASY_USE_CRITICAL_SECTION
|
||||
# undef EASY_USE_CRITICAL_SECTION
|
||||
#endif
|
||||
|
||||
#endif // EASY_PROFILER__SPIN_LOCK__________H______
|
||||
|
Loading…
x
Reference in New Issue
Block a user