diff --git a/easy_profiler_core/chunk_allocator.h b/easy_profiler_core/chunk_allocator.h index 82e1c39..47e0788 100644 --- a/easy_profiler_core/chunk_allocator.h +++ b/easy_profiler_core/chunk_allocator.h @@ -312,6 +312,9 @@ class chunk_allocator { chunk* last; + chunk_list(const chunk_list&) = delete; + chunk_list(chunk_list&&) = delete; + chunk_list() : last(nullptr) { static_assert(sizeof(char) == 1, "easy_profiler logic error: sizeof(char) != 1 for this platform! Please, contact easy_profiler authors to resolve your problem."); @@ -358,9 +361,6 @@ class chunk_allocator private: - chunk_list(const chunk_list&) = delete; - chunk_list(chunk_list&&) = delete; - void free_last() { auto p = last; @@ -384,7 +384,7 @@ class chunk_allocator EASY_STATIC_CONSTEXPR uint16_t N_MINUS_ONE = N - 1; chunk_list m_chunks; ///< List of chunks. - const chunk* m_markedChunk; ///< Chunk marked by last closed frame + chunk* m_markedChunk; ///< Chunk marked by last closed frame uint32_t m_size; ///< Number of elements stored(# of times allocate() has been called.) uint32_t m_markedSize; ///< Number of elements to the moment when put_mark() has been called. uint16_t m_chunkOffset; ///< Number of bytes used in the current chunk. @@ -392,6 +392,9 @@ class chunk_allocator public: + chunk_allocator(const chunk_allocator&) = delete; + chunk_allocator(chunk_allocator&&) = delete; + chunk_allocator() : m_markedChunk(nullptr), m_size(0), m_markedSize(0), m_chunkOffset(0), m_markedChunkOffset(0) { } @@ -527,10 +530,66 @@ public: m_markedChunkOffset = m_chunkOffset; } -private: + void* marked_allocate(uint16_t n) + { + chunk* marked = m_markedChunk; + if (marked == nullptr || (marked == m_chunks.last && m_markedSize == m_size)) + { + auto data = allocate(n); + put_mark(); + return data; + } - chunk_allocator(const chunk_allocator&) = delete; - chunk_allocator(chunk_allocator&&) = delete; + ++m_markedSize; + + uint16_t chunkOffset = m_markedChunkOffset; + if ((chunkOffset + n + sizeof(uint16_t)) <= N) + { + // Temp to avoid extra load due to this* aliasing. + char* data = marked->data + chunkOffset; + chunkOffset += n + sizeof(uint16_t); + m_markedChunkOffset = chunkOffset; + + unaligned_store16(data, n); + data += sizeof(uint16_t); + + // If there is enough space for at least another payload size, + // set it to zero. + if (chunkOffset < N_MINUS_ONE) + unaligned_zero16(data + n); + + if (marked == m_chunks.last && chunkOffset > m_chunkOffset) + m_chunkOffset = chunkOffset; + + return data; + } + + chunkOffset = n + sizeof(uint16_t); + m_markedChunkOffset = chunkOffset; + + chunk* last = m_chunks.last; + if (marked == last) + { + m_chunks.emplace_back(); + last = m_chunks.last; + m_chunkOffset = chunkOffset; + m_size = m_markedSize; + } + else + { + do last = last->prev; while (last->prev != m_markedChunk); + } + + m_markedChunk = last; + char* data = last->data; + unaligned_store16(data, n); + data += sizeof(uint16_t); + + // We assume here that it takes more than one element to fill a chunk. + unaligned_zero16(data + n); + + return data; + } }; // END of class chunk_allocator. diff --git a/easy_profiler_core/profile_manager.cpp b/easy_profiler_core/profile_manager.cpp index 5a364f9..2cef32c 100644 --- a/easy_profiler_core/profile_manager.cpp +++ b/easy_profiler_core/profile_manager.cpp @@ -160,12 +160,6 @@ extern const uint32_t EASY_CURRENT_VERSION = EASY_VERSION_INT(EASY_PROFILER_VERS ////////////////////////////////////////////////////////////////////////// -# define EASY_PROF_DISABLED false//0 -# define EASY_PROF_ENABLED true//1 -//# define EASY_PROF_DUMP 2 - -////////////////////////////////////////////////////////////////////////// - //auto& MANAGER = ProfileManager::instance(); # define MANAGER ProfileManager::instance() EASY_CONSTEXPR uint8_t FORCE_ON_FLAG = profiler::FORCE_ON & ~profiler::ON; @@ -181,7 +175,7 @@ const decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY = ([](){ LARGE_INTEGER fre # ifndef __APPLE__ # include # endif -int64_t calculate_cpu_frequency() +static int64_t calculate_cpu_frequency() { double g_TicksPerNanoSec; uint64_t begin = 0, end = 0; @@ -272,7 +266,7 @@ thread_local static profiler::ThreadGuard THIS_THREAD_GUARD; // thread guard for EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), addBlockDescriptor(\ ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\ __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\ - storeBlockForce2(ts, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp) + ts.storeBlockForce(profiler::Block(timestamp, timestamp, EASY_UNIQUE_DESC(__LINE__)->id(), EASY_RUNTIME_NAME(name))) #else # ifndef EASY_PROFILER_API_DISABLED # define EASY_PROFILER_API_DISABLED @@ -644,7 +638,8 @@ ThreadGuard::~ThreadGuard() { bool isMarked = false; EASY_EVENT_RES(isMarked, "ThreadFinished", EASY_COLOR_THREAD_END, ::profiler::FORCE_ON); - THIS_THREAD->markProfilingFrameEnded(); + //THIS_THREAD->markProfilingFrameEnded(); + THIS_THREAD->putMark(); THIS_THREAD->expired.store(isMarked ? 2 : 1, std::memory_order_release); THIS_THREAD = nullptr; } @@ -663,7 +658,7 @@ ProfileManager::ProfileManager() : , m_beginTime(0) , m_endTime(0) { - m_profilerStatus = ATOMIC_VAR_INIT(EASY_PROF_DISABLED); + m_profilerStatus = ATOMIC_VAR_INIT(false); m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_OPTION_EVENT_TRACING_ENABLED); m_isAlreadyListening = ATOMIC_VAR_INIT(false); m_stopDumping = ATOMIC_VAR_INIT(false); @@ -788,15 +783,18 @@ const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _d void ProfileManager::storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, size_t _size, bool _isArray, ValueId _vin) { - const auto state = m_profilerStatus.load(std::memory_order_acquire); - if (state != EASY_PROF_ENABLED || (_desc->m_status & profiler::ON) == 0) + if (!isEnabled() || (_desc->m_status & profiler::ON) == 0) return; if (THIS_THREAD == nullptr) registerThread(); #if EASY_ENABLE_BLOCK_STATUS != 0 - if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0) + if (THIS_THREAD->stackSize > 0 || (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0)) + return; +#else + if (THIS_THREAD->stackSize > 0) + // Prevent from store values until frame, which has been opened when profiler was disabled, finish return; #endif @@ -807,51 +805,42 @@ void ProfileManager::storeValue(const BaseBlockDescriptor* _desc, DataType _type bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName) { - const auto state = m_profilerStatus.load(std::memory_order_acquire); - if (state == EASY_PROF_DISABLED || (_desc->m_status & profiler::ON) == 0) + if (!isEnabled() || (_desc->m_status & profiler::ON) == 0) return false; - /*if (state == EASY_PROF_DUMP) - { - if (THIS_THREAD == nullptr || THIS_THREAD->halt) - return false; - } - else*/ if (THIS_THREAD == nullptr) - { registerThread(); - } #if EASY_ENABLE_BLOCK_STATUS != 0 - if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0) + if (THIS_THREAD->stackSize > 0 || (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0)) + return false; +#else + if (THIS_THREAD->stackSize > 0) + // Prevent from store block until frame, which has been opened when profiler was disabled, finish return false; #endif const auto time = getCurrentTime(); THIS_THREAD->storeBlock(profiler::Block(time, time, _desc->id(), _runtimeName)); + THIS_THREAD->putMarkIfEmpty(); return true; } bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime) { - const auto state = m_profilerStatus.load(std::memory_order_acquire); - if (state == EASY_PROF_DISABLED || (_desc->m_status & profiler::ON) == 0) + if (!isEnabled() || (_desc->m_status & profiler::ON) == 0) return false; - /*if (state == EASY_PROF_DUMP) - { - if (THIS_THREAD == nullptr || THIS_THREAD->halt) - return false; - } - else*/ if (THIS_THREAD == nullptr) - { registerThread(); - } #if EASY_ENABLE_BLOCK_STATUS != 0 - if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0) + if (THIS_THREAD->stackSize > 0 || (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0)) + return false; +#else + if (THIS_THREAD->stackSize > 0) + // Prevent from store block until frame, which has been opened when profiler was disabled, finish return false; #endif @@ -859,6 +848,8 @@ bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, cons THIS_THREAD->storeBlock(b); b.m_end = b.m_begin; + THIS_THREAD->putMarkIfEmpty(); + return true; } @@ -879,6 +870,7 @@ void ProfileManager::storeBlockForce(const profiler::BaseBlockDescriptor* _desc, _timestamp = getCurrentTime(); THIS_THREAD->storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName)); + THIS_THREAD->putMark(); } void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp) @@ -895,11 +887,7 @@ void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc #endif THIS_THREAD->storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName)); -} - -void ProfileManager::storeBlockForce2(ThreadStorage& _registeredThread, const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp) -{ - _registeredThread.storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName)); + THIS_THREAD->putMark(); } ////////////////////////////////////////////////////////////////////////// @@ -911,58 +899,28 @@ void ProfileManager::beginBlock(Block& _block) if (++THIS_THREAD->stackSize > 1) { + // _block is a sibling of current opened frame and this frame has been opened + // before profiler was enabled. This _block should be ignored. _block.m_status = profiler::OFF; THIS_THREAD->blocks.openedList.emplace_back(_block); return; } - bool empty = true; - const auto state = m_profilerStatus.load(std::memory_order_acquire); - if (state == - //switch (state) - //{ - EASY_PROF_DISABLED) - { - _block.m_status = profiler::OFF; - //THIS_THREAD->halt = false; - THIS_THREAD->blocks.openedList.emplace_back(_block); - beginFrame(); - return; - } + if (!isEnabled()) + { + // _block is a top-level block (a.k.a. frame). + // It should be ignored because profiler is disabled. + _block.m_status = profiler::OFF; + THIS_THREAD->blocks.openedList.emplace_back(_block); + beginFrame(); // FPS counter + return; + } - /*case EASY_PROF_DUMP: - { - const bool halt = THIS_THREAD->halt; - if (halt || THIS_THREAD->blocks.openedList.empty()) - { - _block.m_status = profiler::OFF; - THIS_THREAD->blocks.openedList.emplace_back(_block); - - if (!halt) - { - THIS_THREAD->halt = true; - beginFrame(); - } - - return; - } - - empty = false; - break; - }*/ - - //default: - else - { - empty = THIS_THREAD->blocks.openedList.empty(); - //break; - } - //} + // Profiler is enabled. Begin block. THIS_THREAD->stackSize = 0; - //THIS_THREAD->halt = false; - auto blockStatus = _block.m_status; + const auto blockStatus = _block.m_status; #if EASY_ENABLE_BLOCK_STATUS != 0 if (THIS_THREAD->allowChildren) { @@ -983,11 +941,8 @@ void ProfileManager::beginBlock(Block& _block) } #endif - if (empty) - { - beginFrame(); - THIS_THREAD->markProfilingFrameStarted(); - } + if (THIS_THREAD->blocks.openedList.empty()) + beginFrame(); // FPS counter THIS_THREAD->blocks.openedList.emplace_back(_block); } @@ -1017,22 +972,25 @@ void ProfileManager::endBlock() { if (--THIS_THREAD->stackSize > 0) { + // Just pop child blocks from stack until frame, which + // has been opened before profiler was enabled, finish. THIS_THREAD->popSilent(); return; } THIS_THREAD->stackSize = 0; - if (/*THIS_THREAD->halt ||*/ m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_DISABLED) + if (!isEnabled()) { THIS_THREAD->popSilent(); - endFrame(); + endFrame(); // FPS counter return; } - if (THIS_THREAD->blocks.openedList.empty()) + auto& currentThreadStack = THIS_THREAD->blocks.openedList; + if (currentThreadStack.empty()) return; - Block& top = THIS_THREAD->blocks.openedList.back(); + Block& top = currentThreadStack.back(); if (top.m_status & profiler::ON) { if (!top.finished()) @@ -1041,29 +999,27 @@ void ProfileManager::endBlock() } else { - top.m_end = top.m_begin; // this is to restrict endBlock() call inside ~Block() + // This is to restrict endBlock() call inside ~Block() + top.m_end = top.m_begin; } if (!top.m_isScoped) THIS_THREAD->nonscopedBlocks.pop(); - THIS_THREAD->blocks.openedList.pop_back(); - const bool empty = THIS_THREAD->blocks.openedList.empty(); - if (empty) + currentThreadStack.pop_back(); + if (currentThreadStack.empty()) { - THIS_THREAD->markProfilingFrameEnded(); - endFrame(); + THIS_THREAD->putMark(); + endFrame(); // FPS counter #if EASY_ENABLE_BLOCK_STATUS != 0 THIS_THREAD->allowChildren = true; } else { THIS_THREAD->allowChildren = - ((THIS_THREAD->blocks.openedList.back().get().m_status & profiler::OFF_RECURSIVE) == 0); - } -#else - } + ((currentThreadStack.back().get().m_status & profiler::OFF_RECURSIVE) == 0); #endif + } } void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, processid_t _process_id, profiler::timestamp_t _endtime, bool _lockSpin) @@ -1114,7 +1070,8 @@ void ProfileManager::endFrame() const profiler::timestamp_t duration = THIS_THREAD->endFrame(); - if (THIS_THREAD_FRAME_T_RESET_MAX) THIS_THREAD_FRAME_T_MAX = 0; + if (THIS_THREAD_FRAME_T_RESET_MAX) + THIS_THREAD_FRAME_T_MAX = 0; THIS_THREAD_FRAME_T_RESET_MAX = false; THIS_THREAD_FRAME_T_CUR = duration; @@ -1125,7 +1082,7 @@ void ProfileManager::endFrame() if (THIS_THREAD_IS_MAIN) { - if (m_frameAvgReset.exchange(false, std::memory_order_release) || THIS_THREAD_FRAME_T_RESET_AVG) + if (m_frameAvgReset.exchange(false, std::memory_order_acq_rel) || THIS_THREAD_FRAME_T_RESET_AVG) { if (THIS_THREAD_N_FRAMES > 0) m_frameAvg.store(THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES, std::memory_order_release); @@ -1141,7 +1098,7 @@ void ProfileManager::endFrame() } const auto maxDuration = m_frameMax.load(std::memory_order_acquire); - if (m_frameMaxReset.exchange(false, std::memory_order_release) || duration > maxDuration) + if (m_frameMaxReset.exchange(false, std::memory_order_acq_rel) || duration > maxDuration) m_frameMax.store(duration, std::memory_order_release); m_frameCur.store(duration, std::memory_order_release); @@ -1196,9 +1153,7 @@ void ProfileManager::setEnabled(bool isEnable) guard_lock_t lock(m_dumpSpin); auto time = getCurrentTime(); - const auto status = isEnable ? EASY_PROF_ENABLED : EASY_PROF_DISABLED; - const auto prev = m_profilerStatus.exchange(status, std::memory_order_acq_rel); - if (prev == status) + if (m_profilerStatus.exchange(isEnable, std::memory_order_acq_rel) == isEnable) return; if (isEnable) @@ -1215,11 +1170,6 @@ void ProfileManager::setEnabled(bool isEnable) } } -bool ProfileManager::isEnabled() const -{ - return m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_ENABLED; -} - void ProfileManager::setEventTracingEnabled(bool _isEnable) { m_isEventTracingEnabled.store(_isEnable, std::memory_order_release); @@ -1284,15 +1234,13 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bo 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 (state == EASY_PROF_ENABLED) { - //m_profilerStatus.store(EASY_PROF_DUMP, std::memory_order_release); - m_profilerStatus.store(EASY_PROF_DISABLED, std::memory_order_release); + if (isEnabled()) + { + m_profilerStatus.store(false, std::memory_order_release); disableEventTracer(); m_endTime = getCurrentTime(); } @@ -1303,50 +1251,13 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bo m_dumpSpin.unlock(); return 0; } -/* + // 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. + // This is much better than inserting spin-lock or atomic variable store/load into each storeBlock operation. + // + // Note: this means - wait for all ThreadStorage::storeBlock() to finish. std::this_thread::sleep_for(std::chrono::milliseconds(20)); - // wait for all threads finish opened frames - EASY_LOG_ONLY(bool logged = false); - for (auto thread_it = m_threads.begin(), end = m_threads.end(); thread_it != end;) - { - if (_async && m_stopDumping.load(std::memory_order_acquire)) - { - if (_lockSpin) - m_dumpSpin.unlock(); - return 0; - } - - if (!thread_it->second.profiledFrameOpened.load(std::memory_order_acquire)) - { - ++thread_it; - EASY_LOG_ONLY(logged = false); - } - else - { - EASY_LOG_ONLY( - if (!logged) - { - logged = true; - if (thread_it->second.named) - EASY_WARNING("Waiting for thread \"" << thread_it->second.name << "\" finish opened frame (which is top EASY_BLOCK for this thread)...\n"); - else - EASY_WARNING("Waiting for thread " << thread_it->first << " finish opened frame (which is top EASY_BLOCK for this thread)...\n"); - } - ); - - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - } - - m_profilerStatus.store(EASY_PROF_DISABLED, std::memory_order_release); - - EASY_LOGMSG("All threads have closed frames\n"); - EASY_LOGMSG("Disabled profiling\n"); -*/ - // This is to make sure that no new descriptors or new threads will be // added until we finish sending data. m_spin.lock(); @@ -1657,7 +1568,7 @@ const char* ProfileManager::registerThread(const char* name) void ProfileManager::setBlockStatus(block_id_t _id, EasyBlockStatus _status) { - if (m_profilerStatus.load(std::memory_order_acquire) != EASY_PROF_DISABLED) + if (isEnabled()) return; // Changing blocks statuses is restricted while profile session is active guard_lock_t lock(m_storedSpin); @@ -1671,7 +1582,7 @@ void ProfileManager::setBlockStatus(block_id_t _id, EasyBlockStatus _status) void ProfileManager::startListen(uint16_t _port) { - if (!m_isAlreadyListening.exchange(true, std::memory_order_release)) + if (!m_isAlreadyListening.exchange(true, std::memory_order_acq_rel)) { m_stopListen.store(false, std::memory_order_release); m_listenThread = std::thread(&ProfileManager::listen, this, _port); @@ -1741,9 +1652,7 @@ void ProfileManager::listen(uint16_t _port) #else false; #endif - const profiler::net::EasyProfilerStatus connectionReply( - m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_ENABLED, - m_isEventTracingEnabled.load(std::memory_order_acquire), wasLowPriorityET); + const profiler::net::EasyProfilerStatus connectionReply(isEnabled(), isEventTracingEnabled(), wasLowPriorityET); bytes = socket.send(&connectionReply, sizeof(profiler::net::EasyProfilerStatus)); hasConnect = bytes > 0; @@ -1852,8 +1761,8 @@ void ProfileManager::listen(uint16_t _port) EASY_FORCE_EVENT(t, "StartCapture", EASY_COLOR_START, profiler::OFF); m_dumpSpin.lock(); - const auto prev = m_profilerStatus.exchange(EASY_PROF_ENABLED, std::memory_order_release); - if (prev != EASY_PROF_ENABLED) { + if (!m_profilerStatus.exchange(true, std::memory_order_acq_rel)) + { enableEventTracer(); m_beginTime = t; } @@ -1875,9 +1784,8 @@ void ProfileManager::listen(uint16_t _port) m_dumpSpin.lock(); auto time = getCurrentTime(); - //const auto prev = m_profilerStatus.exchange(EASY_PROF_DUMP, std::memory_order_release); - const auto prev = m_profilerStatus.exchange(EASY_PROF_DISABLED, std::memory_order_acq_rel); - if (prev == EASY_PROF_ENABLED) { + if (m_profilerStatus.exchange(false, std::memory_order_acq_rel)) + { disableEventTracer(); m_endTime = time; } @@ -1958,8 +1866,7 @@ void ProfileManager::listen(uint16_t _port) EASY_ERROR("Can not send block descriptions. Bad std::stringstream.tellp() == -1"); } - replyMessage.type = profiler::net::MessageType::Reply_Blocks_Description_End - ; + replyMessage.type = profiler::net::MessageType::Reply_Blocks_Description_End; bytes = socket.send(&replyMessage, sizeof(replyMessage)); hasConnect = bytes > 0; @@ -1969,21 +1876,16 @@ void ProfileManager::listen(uint16_t _port) case profiler::net::MessageType::Change_Block_Status: { auto data = reinterpret_cast(message); - EASY_LOGMSG("receive MessageType::ChangeBLock_Status id=" << data->id << " status=" << data->status << std::endl); - setBlockStatus(data->id, static_cast<::profiler::EasyBlockStatus>(data->status)); - break; } case profiler::net::MessageType::Change_Event_Tracing_Status: { auto data = reinterpret_cast(message); - EASY_LOGMSG("receive MessageType::Change_Event_Tracing_Status on=" << data->flag << std::endl); - - m_isEventTracingEnabled.store(data->flag, std::memory_order_release); + setEventTracingEnabled(data->flag); break; } diff --git a/easy_profiler_core/profile_manager.h b/easy_profiler_core/profile_manager.h index ebed050..f42b0a2 100644 --- a/easy_profiler_core/profile_manager.h +++ b/easy_profiler_core/profile_manager.h @@ -149,8 +149,12 @@ public: profiler::timestamp_t maxFrameDuration(); profiler::timestamp_t avgFrameDuration(); profiler::timestamp_t curFrameDuration() const; + void setEnabled(bool isEnable); - bool isEnabled() const; + EASY_FORCE_INLINE bool isEnabled() const { + return m_profilerStatus.load(std::memory_order_acquire); + } + void setEventTracingEnabled(bool _isEnable); bool isEventTracingEnabled() const; uint32_t dumpBlocksToFile(const char* filename); @@ -187,7 +191,6 @@ private: 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); - void storeBlockForce2(ThreadStorage& _registeredThread, const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp); ThreadStorage& _threadStorage(profiler::thread_id_t _thread_id); ThreadStorage* _findThreadStorage(profiler::thread_id_t _thread_id); diff --git a/easy_profiler_core/thread_storage.cpp b/easy_profiler_core/thread_storage.cpp index 4acb832..f716a53 100644 --- a/easy_profiler_core/thread_storage.cpp +++ b/easy_profiler_core/thread_storage.cpp @@ -53,10 +53,8 @@ ThreadStorage::ThreadStorage() , named(false) , guarded(false) , frameOpened(false) - //, halt(false) { expired = ATOMIC_VAR_INIT(0); - profiledFrameOpened = ATOMIC_VAR_INIT(false); } void ThreadStorage::storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, size_t _size, bool _isArray, profiler::ValueId _vin) @@ -70,6 +68,8 @@ void ThreadStorage::storeValue(profiler::timestamp_t _timestamp, profiler::block memcpy(cdata + sizeof(profiler::ArbitraryValue), _data, _size); blocks.frameMemorySize += serializedDataSize; + + putMarkIfEmpty(); } void ThreadStorage::storeBlock(const profiler::Block& block) @@ -83,7 +83,10 @@ void ThreadStorage::storeBlock(const profiler::Block& block) EASY_THREAD_LOCAL static profiler::timestamp_t endTime = 0ULL; #endif - uint16_t nameLength = static_cast(strlen(block.name())); + const uint16_t nameLength = static_cast(strlen(block.name())); +#if EASY_OPTION_MEASURE_STORAGE_EXPAND == 0 + const +#endif uint16_t serializedDataSize = static_cast(sizeof(profiler::BaseBlockData) + nameLength + 1); #if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0 @@ -114,10 +117,21 @@ void ThreadStorage::storeBlock(const profiler::Block& block) #endif } +void ThreadStorage::storeBlockForce(const profiler::Block& block) +{ + const uint16_t nameLength = static_cast(strlen(block.name())); + const uint16_t serializedDataSize = static_cast(sizeof(profiler::BaseBlockData) + nameLength + 1); + + void* data = blocks.closedList.marked_allocate(serializedDataSize); + ::new (data) profiler::SerializedBlock(block, nameLength); + blocks.usedMemorySize += serializedDataSize; +} + void ThreadStorage::storeCSwitch(const CSwitchBlock& block) { - uint16_t nameLength = static_cast(strlen(block.name())); - uint16_t serializedDataSize = static_cast(sizeof(profiler::CSwitchEvent) + nameLength + 1); + const uint16_t nameLength = static_cast(strlen(block.name())); + const uint16_t serializedDataSize = static_cast(sizeof(profiler::CSwitchEvent) + nameLength + 1); + void* data = sync.closedList.allocate(serializedDataSize); ::new (data) profiler::SerializedCSwitch(block, nameLength); sync.usedMemorySize += serializedDataSize; @@ -156,15 +170,15 @@ profiler::timestamp_t ThreadStorage::endFrame() return getCurrentTime() - frameStartTime; } -void ThreadStorage::markProfilingFrameStarted() +void ThreadStorage::putMark() { - profiledFrameOpened.store(true, std::memory_order_release); -} - -void ThreadStorage::markProfilingFrameEnded() -{ - profiledFrameOpened.store(false, std::memory_order_release); blocks.closedList.put_mark(); blocks.usedMemorySize += blocks.frameMemorySize; blocks.frameMemorySize = 0; } + +void ThreadStorage::putMarkIfEmpty() +{ + if (!frameOpened) + putMark(); +} diff --git a/easy_profiler_core/thread_storage.h b/easy_profiler_core/thread_storage.h index 1c63832..4635855 100644 --- a/easy_profiler_core/thread_storage.h +++ b/easy_profiler_core/thread_storage.h @@ -105,29 +105,25 @@ struct ThreadStorage EASY_FINAL profiler::timestamp_t frameStartTime; ///< Current frame start time. Used to calculate FPS. const profiler::thread_id_t id; ///< Thread ID std::atomic expired; ///< Is thread expired - std::atomic_bool profiledFrameOpened; ///< Is new profiled frame opened (this is true when profiling is enabled and there is an opened frame) \sa frameOpened int32_t stackSize; ///< Current thread stack depth. Used when switching profiler state to begin collecting blocks only when new frame would be opened. bool allowChildren; ///< False if one of previously opened blocks has OFF_RECURSIVE or ON_WITHOUT_CHILDREN status bool named; ///< True if thread name was set bool guarded; ///< True if thread has been registered using ThreadGuard bool frameOpened; ///< Is new frame opened (this does not depend on profiling status) \sa profiledFrameOpened - //bool halt; ///< This is set to true when new frame started while dumping blocks. Used to restrict collecting blocks during dumping process. void storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, size_t _size, bool _isArray, profiler::ValueId _vin); void storeBlock(const profiler::Block& _block); + void storeBlockForce(const profiler::Block& _block); void storeCSwitch(const CSwitchBlock& _block); void clearClosed(); void popSilent(); void beginFrame(); profiler::timestamp_t endFrame(); - void markProfilingFrameStarted(); - void markProfilingFrameEnded(); + void putMark(); + void putMarkIfEmpty(); ThreadStorage(); - -private: - ThreadStorage(const ThreadStorage&) = delete; ThreadStorage(ThreadStorage&&) = delete; diff --git a/profiler_gui/common_functions.cpp b/profiler_gui/common_functions.cpp index 9e337d9..9d89867 100644 --- a/profiler_gui/common_functions.cpp +++ b/profiler_gui/common_functions.cpp @@ -54,6 +54,21 @@ #include "common_functions.h" +template +static QString toString(const profiler::ArbitraryValue& _serializedValue) { + return QString::number(_serializedValue.toValue()->value()); +} + +template +static double toReal(const profiler::ArbitraryValue& _serializedValue, int _index) { + return static_cast(_serializedValue.toArray()->at(_index)); +} + +template +static double toReal(const profiler::ArbitraryValue& _serializedValue) { + return static_cast(_serializedValue.toValue()->value()); +} + namespace profiler_gui { ////////////////////////////////////////////////////////////////////////// @@ -272,17 +287,17 @@ namespace profiler_gui { switch (_serializedValue.type()) { case ::profiler::DataType::Bool: return _serializedValue.toValue()->value() ? QStringLiteral("true") : QStringLiteral("false"); - case ::profiler::DataType::Char: return QChar(_serializedValue.toValue()->value()); + case ::profiler::DataType::Char: return QChar(_serializedValue.toValue ()->value()); case ::profiler::DataType::Int8: return QChar(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint8: return QString::number(_serializedValue.toValue()->value()); - case ::profiler::DataType::Int16: return QString::number(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint16: return QString::number(_serializedValue.toValue()->value()); - case ::profiler::DataType::Int32: return QString::number(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint32: return QString::number(_serializedValue.toValue()->value()); - case ::profiler::DataType::Int64: return QString::number(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint64: return QString::number(_serializedValue.toValue()->value()); - case ::profiler::DataType::Float: return QString::number(_serializedValue.toValue()->value()); - case ::profiler::DataType::Double: return QString::number(_serializedValue.toValue()->value()); + case ::profiler::DataType::Uint8: return toString (_serializedValue); + case ::profiler::DataType::Int16: return toString (_serializedValue); + case ::profiler::DataType::Uint16: return toString(_serializedValue); + case ::profiler::DataType::Int32: return toString (_serializedValue); + case ::profiler::DataType::Uint32: return toString(_serializedValue); + case ::profiler::DataType::Int64: return toString (_serializedValue); + case ::profiler::DataType::Uint64: return toString(_serializedValue); + case ::profiler::DataType::Float: return toString (_serializedValue); + case ::profiler::DataType::Double: return toString (_serializedValue); case ::profiler::DataType::String: return _serializedValue.data(); default: return QStringLiteral("Unknown"); } @@ -300,16 +315,16 @@ namespace profiler_gui { return value ? 1 : 0; } - case ::profiler::DataType::Char: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Int8: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Uint8: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Int16: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Uint16: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Int32: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Uint32: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Int64: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Uint64: return static_cast(_serializedValue.toArray()->at(_index)); - case ::profiler::DataType::Float: return static_cast(_serializedValue.toArray()->at(_index)); + case ::profiler::DataType::Char: return toReal (_serializedValue, _index); + case ::profiler::DataType::Int8: return toReal (_serializedValue, _index); + case ::profiler::DataType::Uint8: return toReal (_serializedValue, _index); + case ::profiler::DataType::Int16: return toReal (_serializedValue, _index); + case ::profiler::DataType::Uint16: return toReal(_serializedValue, _index); + case ::profiler::DataType::Int32: return toReal (_serializedValue, _index); + case ::profiler::DataType::Uint32: return toReal(_serializedValue, _index); + case ::profiler::DataType::Int64: return toReal (_serializedValue, _index); + case ::profiler::DataType::Uint64: return toReal(_serializedValue, _index); + case ::profiler::DataType::Float: return toReal (_serializedValue, _index); case ::profiler::DataType::Double: return _serializedValue.toArray()->at(_index); case ::profiler::DataType::String: return static_cast(_serializedValue.data()[_index]); default: return 0; @@ -324,16 +339,16 @@ namespace profiler_gui { return value ? 1 : 0; } - case ::profiler::DataType::Char: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Int8: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint8: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Int16: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint16: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Int32: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint32: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Int64: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Uint64: return static_cast(_serializedValue.toValue()->value()); - case ::profiler::DataType::Float: return static_cast(_serializedValue.toValue()->value()); + case ::profiler::DataType::Char: return toReal (_serializedValue); + case ::profiler::DataType::Int8: return toReal (_serializedValue); + case ::profiler::DataType::Uint8: return toReal (_serializedValue); + case ::profiler::DataType::Int16: return toReal (_serializedValue); + case ::profiler::DataType::Uint16: return toReal(_serializedValue); + case ::profiler::DataType::Int32: return toReal (_serializedValue); + case ::profiler::DataType::Uint32: return toReal(_serializedValue); + case ::profiler::DataType::Int64: return toReal (_serializedValue); + case ::profiler::DataType::Uint64: return toReal(_serializedValue); + case ::profiler::DataType::Float: return toReal (_serializedValue); case ::profiler::DataType::Double: return _serializedValue.toValue()->value(); case ::profiler::DataType::String: return static_cast(_serializedValue.data()[_index]); default: return 0;