From e9bed4c839b27a13827b3703fb07a51818615e12 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Wed, 21 Dec 2016 21:59:40 +0300 Subject: [PATCH] (Core) Major update (file format NOT affected). Description: 1) Added macro EASY_CONST_NAME(name) to make possible to force EasyProfiler think that given block name is const and not changed during application execution (this rely to compile-time strings saved into a run-time variable); 2) Starting from now, EasyProfiler will not store partially finished frames - first, this means that profiler enabled during frame execution will skip all blocks from this frame, and second, this means that when dumping profiled information profiler will wait until all frames finish before dumping blocks. --- easy_profiler_core/include/easy/profiler.h | 12 +- .../include/easy/profiler_aux.h | 33 ++- easy_profiler_core/profile_manager.cpp | 235 +++++++++++++----- easy_profiler_core/profile_manager.h | 20 +- 4 files changed, 217 insertions(+), 83 deletions(-) diff --git a/easy_profiler_core/include/easy/profiler.h b/easy_profiler_core/include/easy/profiler.h index add7d10..86c43d8 100644 --- a/easy_profiler_core/include/easy/profiler.h +++ b/easy_profiler_core/include/easy/profiler.h @@ -91,7 +91,8 @@ Block will be automatically completed by destructor. */ # define EASY_BLOCK(name, ...)\ EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ - EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ + EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__),\ + ::std::is_base_of<::profiler::ForceConstStr, decltype(name)>::value));\ ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable @@ -121,7 +122,7 @@ Name of the block automatically created with function name. */ # define EASY_FUNCTION(...)\ EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ - EASY_UNIQUE_LINE_ID, __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ + EASY_UNIQUE_LINE_ID, __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__), false));\ ::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\ ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable @@ -162,7 +163,8 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION. # define EASY_EVENT(name, ...)\ EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(\ ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ - __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\ + __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__),\ + ::std::is_base_of<::profiler::ForceConstStr, decltype(name)>::value));\ ::profiler::storeEvent(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); /** Macro for enabling profiler. @@ -515,7 +517,7 @@ namespace profiler { \ingroup profiler */ - PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, const char* _autogenUniqueId, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color); + PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, const char* _autogenUniqueId, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color, bool _copyName = false); /** Stores event in the blocks list. @@ -639,7 +641,7 @@ namespace profiler { } #else - inline const BaseBlockDescriptor* registerDescription(EasyBlockStatus, const char*, const char*, const char*, int, block_type_t, color_t) + inline const BaseBlockDescriptor* registerDescription(EasyBlockStatus, const char*, const char*, const char*, int, block_type_t, color_t, bool = false) { return reinterpret_cast(0xbad); } inline void endBlock() { } inline void setEnabled(bool) { } diff --git a/easy_profiler_core/include/easy/profiler_aux.h b/easy_profiler_core/include/easy/profiler_aux.h index 7605f57..d195d08 100644 --- a/easy_profiler_core/include/easy/profiler_aux.h +++ b/easy_profiler_core/include/easy/profiler_aux.h @@ -86,22 +86,46 @@ namespace profiler { namespace profiler { + template struct NameSwitch; + + class ForceConstStr EASY_FINAL { + friend NameSwitch; + friend NameSwitch; + + const char* c_str; + + ForceConstStr() = delete; + ForceConstStr(const ForceConstStr&) = delete; + ForceConstStr(ForceConstStr&&) = delete; + ForceConstStr& operator = (const ForceConstStr&) = delete; + ForceConstStr& operator = (ForceConstStr&&) = delete; + + public: + + ForceConstStr(const char* _str) : c_str(_str) {} + ForceConstStr(const ::std::string& _str) : c_str(_str.c_str()) {} + }; + template struct NameSwitch EASY_FINAL { static const char* runtime_name(const char* name) { return name; } - static const char* runtime_name(const std::string& name) { return name.c_str(); } + static const char* runtime_name(const ::std::string& name) { return name.c_str(); } + static const char* runtime_name(const ForceConstStr&) { return ""; } template static const char* compiletime_name(const T&, const char* autoGeneratedName) { return autoGeneratedName; } static const char* compiletime_name(const char*, const char* autoGeneratedName) { return autoGeneratedName; } + static const char* compiletime_name(const ForceConstStr& name, const char*) { return name.c_str; } }; template <> struct NameSwitch EASY_FINAL { static const char* runtime_name(const char*) { return ""; } - static const char* runtime_name(const std::string& name) { return name.c_str(); } + static const char* runtime_name(const ::std::string& name) { return name.c_str(); } + static const char* runtime_name(const ForceConstStr&) { return ""; } template static const char* compiletime_name(const T&, const char* autoGeneratedName) { return autoGeneratedName; } static const char* compiletime_name(const char* name, const char*) { return name; } + static const char* compiletime_name(const ForceConstStr& name, const char*) { return name.c_str; } }; //*********************************************** @@ -160,6 +184,11 @@ namespace profiler { # define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFICATION(__LINE__) # define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::compiletime_name(name, EASY_UNIQUE_LINE_ID) # define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::runtime_name(name) +# define EASY_CONST_NAME(name) ::profiler::ForceConstStr(name) + +#else + +# define EASY_CONST_NAME(name) #endif // BUILD_WITH_EASY_PROFILER diff --git a/easy_profiler_core/profile_manager.cpp b/easy_profiler_core/profile_manager.cpp index a85b1e0..932897c 100644 --- a/easy_profiler_core/profile_manager.cpp +++ b/easy_profiler_core/profile_manager.cpp @@ -66,9 +66,9 @@ using namespace profiler; # endif #endif -#define EASY_PROFILER_PRODUCT_VERSION "v" EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MAJOR) "." \ - EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MINOR) "." \ - EASY_STRINGIFICATION(EASY_PROFILER_VERSION_PATCH) +# define EASY_PROFILER_PRODUCT_VERSION "v" EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MAJOR) "." \ + EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MINOR) "." \ + EASY_STRINGIFICATION(EASY_PROFILER_VERSION_PATCH) # define EASY_VERSION_INT(v_major, v_minor, v_patch) ((static_cast(v_major) << 24) | (static_cast(v_minor) << 16) | static_cast(v_patch)) extern const uint32_t PROFILER_SIGNATURE = ('E' << 24) | ('a' << 16) | ('s' << 8) | 'y'; @@ -77,6 +77,12 @@ extern const uint32_t EASY_CURRENT_VERSION = EASY_VERSION_INT(EASY_PROFILER_VERS ////////////////////////////////////////////////////////////////////////// +# define EASY_PROF_DISABLED 0 +# define EASY_PROF_ENABLED 1 +# define EASY_PROF_DUMP 2 + +////////////////////////////////////////////////////////////////////////// + //auto& MANAGER = ProfileManager::instance(); # define MANAGER ProfileManager::instance() const uint8_t FORCE_ON_FLAG = profiler::FORCE_ON & ~profiler::ON; @@ -88,6 +94,7 @@ decltype(LARGE_INTEGER::QuadPart) const CPU_FREQUENCY = ([](){ LARGE_INTEGER fre ////////////////////////////////////////////////////////////////////////// EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; +EASY_THREAD_LOCAL static int32_t THREAD_STACK_SIZE = 0; ////////////////////////////////////////////////////////////////////////// @@ -127,9 +134,9 @@ EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; extern "C" { #if !defined(EASY_PROFILER_API_DISABLED) - PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color, bool _copyName) { - return MANAGER.addBlockDescriptor(_status, _autogenUniqueId, _name, _filename, _line, _block_type, _color); + return MANAGER.addBlockDescriptor(_status, _autogenUniqueId, _name, _filename, _line, _block_type, _color, _copyName); } PROFILER_API void endBlock() @@ -201,7 +208,7 @@ extern "C" { return MANAGER.stopListen(); } #else - PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus, const char*, const char*, const char*, int, block_type_t, color_t) { return reinterpret_cast(0xbad); } + PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus, const char*, const char*, const char*, int, block_type_t, color_t, bool) { return reinterpret_cast(0xbad); } PROFILER_API void endBlock() { } PROFILER_API void setEnabled(bool) { } PROFILER_API void storeEvent(const BaseBlockDescriptor*, const char*) { } @@ -293,17 +300,15 @@ class BlockDescriptor : public BaseBlockDescriptor { friend ProfileManager; - EASY_BLOCK_DESC_STRING m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier EASY_BLOCK_DESC_STRING m_filename; ///< Source file name where this block is declared - uint16_t m_size; ///< Used memory size + EASY_BLOCK_DESC_STRING m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier public: BlockDescriptor(block_id_t _id, EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) : BaseBlockDescriptor(_id, _status, _line, _block_type, _color) - , m_name(_name) , m_filename(_filename) - , m_size(static_cast(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2)) + , m_name(_name) { } @@ -334,6 +339,7 @@ ThreadStorage::ThreadStorage() : id(getCurrentThreadId()), allowChildren(true), { expired = ATOMIC_VAR_INIT(0); + frame = ATOMIC_VAR_INIT(false); } void ThreadStorage::storeBlock(const profiler::Block& block) @@ -402,6 +408,7 @@ ThreadGuard::~ThreadGuard() { bool isMarked = false; EASY_EVENT_RES(isMarked, "ThreadFinished", profiler::colors::Dark, ::profiler::FORCE_ON); + THREAD_STORAGE->frame.store(false, std::memory_order_release); THREAD_STORAGE->expired.store(isMarked ? 2 : 1, std::memory_order_release); THREAD_STORAGE = nullptr; } @@ -420,7 +427,7 @@ ProfileManager::ProfileManager() : , m_beginTime(0) , m_endTime(0) { - m_isEnabled = ATOMIC_VAR_INIT(false); + m_profilerStatus = ATOMIC_VAR_INIT(EASY_PROF_DISABLED); m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_OPTION_EVENT_TRACING_ENABLED); m_isAlreadyListening = ATOMIC_VAR_INIT(false); m_stopListen = ATOMIC_VAR_INIT(false); @@ -436,8 +443,15 @@ ProfileManager::~ProfileManager() stopListen(); #endif - for (auto desc : m_descriptors) + for (auto desc : m_descriptors) { +#if EASY_BLOCK_DESC_FULL_COPY == 0 + if (desc) + desc->~BlockDescriptor(); + free(desc); +#else delete desc; +#endif + } } #ifndef EASY_MAGIC_STATIC_CPP11 @@ -482,7 +496,8 @@ const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _d const char* _filename, int _line, block_type_t _block_type, - color_t _color) + color_t _color, + bool _copyName) { guard_lock_t lock(m_storedSpin); @@ -491,8 +506,28 @@ const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _d if (it != m_descriptorsMap.end()) return m_descriptors[it->second]; + const auto nameLen = strlen(_name); + m_usedMemorySize += sizeof(profiler::SerializedBlockDescriptor) + nameLen + strlen(_filename) + 2; + +#if EASY_BLOCK_DESC_FULL_COPY == 0 + BlockDescriptor* desc = nullptr; + + if (_copyName) + { + void* data = malloc(sizeof(BlockDescriptor) + nameLen + 1); + char* name = reinterpret_cast(data) + sizeof(BlockDescriptor); + strncpy(name, _name, nameLen); + desc = ::new (data)BlockDescriptor(static_cast(m_descriptors.size()), _defaultStatus, name, _filename, _line, _block_type, _color); + } + else + { + void* data = malloc(sizeof(BlockDescriptor)); + desc = ::new (data)BlockDescriptor(static_cast(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color); + } +#else auto desc = new BlockDescriptor(static_cast(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color); - m_usedMemorySize += desc->m_size; +#endif + m_descriptors.emplace_back(desc); m_descriptorsMap.emplace(key, desc->id()); @@ -503,11 +538,19 @@ const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _d bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName) { - if (!m_isEnabled.load(std::memory_order_acquire) || !(_desc->m_status & profiler::ON)) + const auto state = m_profilerStatus.load(std::memory_order_acquire); + if (state == EASY_PROF_DISABLED || !(_desc->m_status & profiler::ON)) return false; - if (THREAD_STORAGE == nullptr) + if (state == EASY_PROF_DUMP) + { + if (THREAD_STORAGE == nullptr || THREAD_STORAGE->blocks.openedList.empty()) + return false; + } + else if (THREAD_STORAGE == nullptr) + { THREAD_STORAGE = &threadStorage(getCurrentThreadId()); + } #if EASY_ENABLE_BLOCK_STATUS != 0 if (!THREAD_STORAGE->allowChildren && !(_desc->m_status & FORCE_ON_FLAG)) @@ -576,11 +619,29 @@ void ProfileManager::storeBlockForce2(ThreadStorage& _registeredThread, const pr void ProfileManager::beginBlock(Block& _block) { - if (!m_isEnabled.load(std::memory_order_acquire)) + if (++THREAD_STACK_SIZE > 1) return; - if (THREAD_STORAGE == nullptr) + const auto state = m_profilerStatus.load(std::memory_order_acquire); + if (state == EASY_PROF_DISABLED) + return; + + THREAD_STACK_SIZE = 0; + bool empty = true; + if (state == EASY_PROF_DUMP) + { + if (THREAD_STORAGE == nullptr || THREAD_STORAGE->blocks.openedList.empty()) + return; + empty = false; + } + else if (THREAD_STORAGE == nullptr) + { THREAD_STORAGE = &threadStorage(getCurrentThreadId()); + } + else + { + empty = THREAD_STORAGE->blocks.openedList.empty(); + } #if EASY_ENABLE_BLOCK_STATUS != 0 if (THREAD_STORAGE->allowChildren) @@ -602,6 +663,8 @@ void ProfileManager::beginBlock(Block& _block) } #endif + if (empty) + THREAD_STORAGE->frame.store(true, std::memory_order_release); THREAD_STORAGE->blocks.openedList.emplace(_block); } @@ -618,9 +681,10 @@ void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profil void ProfileManager::endBlock() { - if (!m_isEnabled.load(std::memory_order_acquire)) + if (--THREAD_STACK_SIZE > 0 || m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_DISABLED) return; + THREAD_STACK_SIZE = 0; if (THREAD_STORAGE == nullptr || THREAD_STORAGE->blocks.openedList.empty()) return; @@ -637,9 +701,12 @@ void ProfileManager::endBlock() } THREAD_STORAGE->blocks.openedList.pop(); + const bool empty = THREAD_STORAGE->blocks.openedList.empty(); + if (empty) + THREAD_STORAGE->frame.store(false, std::memory_order_release); #if EASY_ENABLE_BLOCK_STATUS != 0 - THREAD_STORAGE->allowChildren = THREAD_STORAGE->blocks.openedList.empty() || !(THREAD_STORAGE->blocks.openedList.top().get().m_status & profiler::OFF_RECURSIVE); + THREAD_STORAGE->allowChildren = empty || !(THREAD_STORAGE->blocks.openedList.top().get().m_status & profiler::OFF_RECURSIVE); #endif } @@ -665,37 +732,40 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, processi ////////////////////////////////////////////////////////////////////////// -void ProfileManager::setEnabled(bool isEnable, bool _setTime) +void ProfileManager::enableEventTracer() { +#ifdef _WIN32 + if (m_isEventTracingEnabled.load(std::memory_order_acquire)) + EasyEventTracer::instance().enable(true); +#endif +} + +void ProfileManager::disableEventTracer() +{ +#ifdef _WIN32 + EasyEventTracer::instance().disable(); +#endif +} + +void ProfileManager::setEnabled(bool isEnable) +{ + guard_lock_t lock(m_dumpSpin); + auto time = getCurrentTime(); - const bool prev = m_isEnabled.exchange(isEnable, std::memory_order_release); - if (prev == isEnable) + const auto status = isEnable ? EASY_PROF_ENABLED : EASY_PROF_DISABLED; + const auto prev = m_profilerStatus.exchange(status, std::memory_order_release); + if (prev == status) return; if (isEnable) { -#ifdef _WIN32 - if (m_isEventTracingEnabled.load(std::memory_order_acquire)) - EasyEventTracer::instance().enable(true); -#endif - - if (_setTime) - { - guard_lock_t lk(m_spin); - m_beginTime = time; - } + enableEventTracer(); + m_beginTime = time; } else { -#ifdef _WIN32 - EasyEventTracer::instance().disable(); -#endif - - if (_setTime) - { - guard_lock_t lk(m_spin); - m_endTime = time; - } + disableEventTracer(); + m_endTime = time; } } @@ -706,9 +776,9 @@ void ProfileManager::setEventTracingEnabled(bool _isEnable) ////////////////////////////////////////////////////////////////////////// -uint8_t ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread) +char ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread) { - const uint8_t val = _registeredThread.expired.load(std::memory_order_acquire); + const char val = _registeredThread.expired.load(std::memory_order_acquire); if (val != 0) return val; @@ -724,7 +794,7 @@ uint8_t ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread) if (hThread == nullptr || GetExitCodeThread(hThread, &exitCode) == FALSE || exitCode != STILL_ACTIVE) { // Thread has been expired - _registeredThread.expired.store(true, std::memory_order_release); + _registeredThread.expired.store(1, std::memory_order_release); if (hThread != nullptr) CloseHandle(hThread); return 1; @@ -745,28 +815,45 @@ uint8_t ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread) ////////////////////////////////////////////////////////////////////////// -uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) +uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bool _lockSpin) { - const bool wasEnabled = m_isEnabled.load(std::memory_order_acquire); + if (_lockSpin) + m_dumpSpin.lock(); + + const auto state = m_profilerStatus.load(std::memory_order_acquire); #ifndef _WIN32 const bool eventTracingEnabled = m_isEventTracingEnabled.load(std::memory_order_acquire); #endif - if (wasEnabled) - ::profiler::setEnabled(false); + if (state == EASY_PROF_ENABLED) { + m_profilerStatus.store(EASY_PROF_DUMP, std::memory_order_release); + disableEventTracer(); + m_endTime = getCurrentTime(); + } // This is to make sure that no new descriptors or new threads will be // added until we finish sending data. - guard_lock_t lock1(m_storedSpin); - guard_lock_t lock2(m_spin); + //m_spin.lock(); // This is the only place using both spins, so no dead-lock will occur // Wait for some time to be sure that all operations which began before setEnabled(false) will be finished. // This is much better than inserting spin-lock or atomic variable check into each store operation. std::this_thread::sleep_for(std::chrono::milliseconds(20)); + + // wait for all threads finish opened frames + for (auto it = m_threads.begin(), end = m_threads.end(); it != end;) + { + if (!it->second.frame.load(std::memory_order_acquire)) + ++it; + else + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + } + m_profilerStatus.store(EASY_PROF_DISABLED, std::memory_order_release); + m_spin.lock(); + m_storedSpin.lock(); // TODO: think about better solution because this one is not 100% safe... const profiler::timestamp_t now = getCurrentTime(); @@ -800,7 +887,7 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) auto& t = it->second; uint32_t num = static_cast(t.blocks.closedList.size()) + static_cast(t.sync.closedList.size()); - const uint8_t expired = checkThreadExpired(t); + const char expired = checkThreadExpired(t); if (num == 0 && (expired != 0 || !t.guarded)) { // Remove thread if it contains no profiled information and has been finished or is not guarded. m_threads.erase(it++); @@ -911,8 +998,11 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) ++it; } - //if (wasEnabled) - // ::profiler::setEnabled(true); + m_storedSpin.unlock(); + m_spin.unlock(); + + if (_lockSpin) + m_dumpSpin.unlock(); return blocks_number; } @@ -920,7 +1010,7 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) uint32_t ProfileManager::dumpBlocksToFile(const char* _filename) { profiler::OStream outputStream; - const auto blocksNumber = dumpBlocksToStream(outputStream); + const auto blocksNumber = dumpBlocksToStream(outputStream, true); std::ofstream of(_filename, std::fstream::binary); of << outputStream.stream().str(); @@ -959,7 +1049,7 @@ const char* ProfileManager::registerThread(const char* name) void ProfileManager::setBlockStatus(block_id_t _id, EasyBlockStatus _status) { - if (m_isEnabled.load(std::memory_order_acquire)) + if (m_profilerStatus.load(std::memory_order_acquire) != EASY_PROF_ENABLED) return; // Changing blocks statuses is restricted while profile session is active guard_lock_t lock(m_storedSpin); @@ -1023,7 +1113,7 @@ void ProfileManager::listen(uint16_t _port) #else false; #endif - profiler::net::EasyProfilerStatus connectionReply(m_isEnabled.load(std::memory_order_acquire), m_isEventTracingEnabled.load(std::memory_order_acquire), wasLowPriorityET); + profiler::net::EasyProfilerStatus connectionReply(m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_ENABLED, m_isEventTracingEnabled.load(std::memory_order_acquire), wasLowPriorityET); bytes = socket.send(&connectionReply, sizeof(connectionReply)); hasConnect = bytes > 0; } @@ -1061,11 +1151,14 @@ void ProfileManager::listen(uint16_t _port) #endif ::profiler::timestamp_t t = 0; EASY_FORCE_EVENT(t, "StartCapture", profiler::colors::Green, profiler::OFF); - ProfileManager::setEnabled(true, false); - m_spin.lock(); - m_beginTime = t; - m_spin.unlock(); + m_dumpSpin.lock(); + const auto prev = m_profilerStatus.exchange(EASY_PROF_ENABLED, std::memory_order_release); + if (prev != EASY_PROF_ENABLED) { + enableEventTracer(); + m_beginTime = t; + } + m_dumpSpin.unlock(); replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING; bytes = socket.send(&replyMessage, sizeof(replyMessage)); @@ -1079,19 +1172,23 @@ void ProfileManager::listen(uint16_t _port) #ifdef EASY_DEBUG_NET_PRINT printf("receive REQUEST_STOP_CAPTURE\n"); #endif - ProfileManager::setEnabled(false); - - m_spin.lock(); - ::profiler::timestamp_t t = m_endTime; - m_spin.unlock(); - EASY_FORCE_EVENT2(t, "StopCapture", profiler::colors::Red, profiler::OFF); + m_dumpSpin.lock(); + auto time = getCurrentTime(); + const auto prev = m_profilerStatus.exchange(EASY_PROF_DUMP, std::memory_order_release); + if (prev == EASY_PROF_ENABLED) { + disableEventTracer(); + m_endTime = time; + } + EASY_FORCE_EVENT2(m_endTime, "StopCapture", profiler::colors::Red, profiler::OFF); //TODO //if connection aborted - ignore this part - profiler::net::DataMessage dm; profiler::OStream os; - dumpBlocksToStream(os); + dumpBlocksToStream(os, false); + m_dumpSpin.unlock(); + + profiler::net::DataMessage dm; dm.size = (uint32_t)os.stream().str().length(); int packet_size = int(sizeof(dm)) + int(dm.size); diff --git a/easy_profiler_core/profile_manager.h b/easy_profiler_core/profile_manager.h index e4344b5..e147ed0 100644 --- a/easy_profiler_core/profile_manager.h +++ b/easy_profiler_core/profile_manager.h @@ -320,7 +320,8 @@ struct ThreadStorage #endif const profiler::thread_id_t id; - std::atomic expired; + std::atomic expired; + std::atomic_bool frame; ///< is new frame working bool allowChildren; bool named; bool guarded; @@ -368,13 +369,14 @@ class ProfileManager profiler::timestamp_t m_endTime; profiler::spin_lock m_spin; profiler::spin_lock m_storedSpin; - std::atomic_bool m_isEnabled; + profiler::spin_lock m_dumpSpin; + std::atomic m_profilerStatus; std::atomic_bool m_isEventTracingEnabled; - std::atomic_bool m_isAlreadyListening; + std::atomic_bool m_isAlreadyListening; std::string m_csInfoFilename = "/tmp/cs_profiling_info.log"; - uint32_t dumpBlocksToStream(profiler::OStream& _outputStream); + uint32_t dumpBlocksToStream(profiler::OStream& _outputStream, bool _lockSpin); void setBlockStatus(profiler::block_id_t _id, profiler::EasyBlockStatus _status); std::thread m_listenThread; @@ -395,12 +397,13 @@ public: const char* _filename, int _line, profiler::block_type_t _block_type, - profiler::color_t _color); + profiler::color_t _color, + bool _copyName = false); bool storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName); void beginBlock(profiler::Block& _block); void endBlock(); - void setEnabled(bool isEnable, bool _setTime = true); + void setEnabled(bool isEnable); void setEventTracingEnabled(bool _isEnable); uint32_t dumpBlocksToFile(const char* filename); const char* registerThread(const char* name, profiler::ThreadGuard& threadGuard); @@ -423,7 +426,10 @@ public: private: - uint8_t checkThreadExpired(ThreadStorage& _registeredThread); + void enableEventTracer(); + void disableEventTracer(); + + char checkThreadExpired(ThreadStorage& _registeredThread); void storeBlockForce(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t& _timestamp); void storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp);