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);