/************************************************************************ * file name : profile_manager.cpp * ----------------- : * creation time : 2016/02/16 * authors : Sergey Yagovtsev, Victor Zarubkin * emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com * ----------------- : * description : The file contains implementation of Profile manager and implement access c-function * : * license : Lightweight profiler library for c++ * : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin * : * : * : Licensed under the Apache License, Version 2.0 (the "License"); * : you may not use this file except in compliance with the License. * : You may obtain a copy of the License at * : * : http://www.apache.org/licenses/LICENSE-2.0 * : * : Unless required by applicable law or agreed to in writing, software * : distributed under the License is distributed on an "AS IS" BASIS, * : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * : See the License for the specific language governing permissions and * : limitations under the License. * : * : * : GNU General Public License Usage * : Alternatively, this file may be used under the terms of the GNU * : General Public License as published by the Free Software Foundation, * : either version 3 of the License, or (at your option) any later version. * : * : This program is distributed in the hope that it will be useful, * : but WITHOUT ANY WARRANTY; without even the implied warranty of * : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the * : GNU General Public License for more details. * : * : You should have received a copy of the GNU General Public License * : along with this program.If not, see . ************************************************************************/ #include #include #include "profile_manager.h" #include "easy/serialized_block.h" #include "easy/easy_net.h" #include "easy/easy_socket.h" #include "event_trace_win.h" #include "current_time.h" #ifndef _WIN32 #include #endif ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// using namespace profiler; ////////////////////////////////////////////////////////////////////////// #if !defined(EASY_PROFILER_VERSION_MAJOR) || !defined(EASY_PROFILER_VERSION_MINOR) || !defined(EASY_PROFILER_VERSION_PATCH) # ifdef _WIN32 # error EASY_PROFILER_VERSION_MAJOR and EASY_PROFILER_VERSION_MINOR and EASY_PROFILER_VERSION_PATCH macros must be defined # else # error "EASY_PROFILER_VERSION_MAJOR and EASY_PROFILER_VERSION_MINOR and EASY_PROFILER_VERSION_PATCH macros must be defined" # endif #endif # 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'; extern const uint32_t EASY_CURRENT_VERSION = EASY_VERSION_INT(EASY_PROFILER_VERSION_MAJOR, EASY_PROFILER_VERSION_MINOR, EASY_PROFILER_VERSION_PATCH); # undef EASY_VERSION_INT ////////////////////////////////////////////////////////////////////////// //auto& MANAGER = ProfileManager::instance(); # define MANAGER ProfileManager::instance() const uint8_t FORCE_ON_FLAG = profiler::FORCE_ON & ~profiler::ON; #ifdef _WIN32 decltype(LARGE_INTEGER::QuadPart) const CPU_FREQUENCY = ([](){ LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return freq.QuadPart; })(); #endif ////////////////////////////////////////////////////////////////////////// EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; ////////////////////////////////////////////////////////////////////////// #ifdef BUILD_WITH_EASY_PROFILER # define EASY_FORCE_EVENT(timestamp, name, ...)\ 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::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\ storeBlockForce(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp); # define EASY_FORCE_EVENT2(timestamp, name, ...)\ 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::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\ storeBlockForce2(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp); #else # define EASY_FORCE_EVENT(timestamp, name, ...) # define EASY_FORCE_EVENT2(timestamp, name, ...) #endif ////////////////////////////////////////////////////////////////////////// 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) { return MANAGER.addBlockDescriptor(_status, _autogenUniqueId, _name, _filename, _line, _block_type, _color); } PROFILER_API void endBlock() { MANAGER.endBlock(); } PROFILER_API void setEnabled(bool isEnable) { MANAGER.setEnabled(isEnable); } PROFILER_API void storeEvent(const BaseBlockDescriptor* _desc, const char* _runtimeName) { MANAGER.storeBlock(_desc, _runtimeName); } PROFILER_API void beginBlock(Block& _block) { MANAGER.beginBlock(_block); } PROFILER_API uint32_t dumpBlocksToFile(const char* filename) { return MANAGER.dumpBlocksToFile(filename); } PROFILER_API const char* registerThreadScoped(const char* name, ThreadGuard& threadGuard) { return MANAGER.registerThread(name, threadGuard); } PROFILER_API const char* registerThread(const char* name) { return MANAGER.registerThread(name); } PROFILER_API void setEventTracingEnabled(bool _isEnable) { MANAGER.setEventTracingEnabled(_isEnable); } # ifdef _WIN32 PROFILER_API void setLowPriorityEventTracing(bool _isLowPriority) { EasyEventTracer::instance().setLowPriority(_isLowPriority); } # else PROFILER_API void setLowPriorityEventTracing(bool) { } # endif PROFILER_API void setContextSwitchLogFilename(const char* name) { return MANAGER.setContextSwitchLogFilename(name); } PROFILER_API const char* getContextSwitchLogFilename() { return MANAGER.getContextSwitchLogFilename(); } PROFILER_API void startListen(uint16_t _port) { return MANAGER.startListenSignalToCapture(_port); } PROFILER_API void stopListen() { return MANAGER.stopListenSignalToCapture(); } #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 void endBlock() { } PROFILER_API void setEnabled(bool) { } PROFILER_API void storeEvent(const BaseBlockDescriptor*, const char*) { } PROFILER_API void beginBlock(Block&) { } PROFILER_API uint32_t dumpBlocksToFile(const char*) { return 0; } PROFILER_API const char* registerThreadScoped(const char*, ThreadGuard&) { return ""; } PROFILER_API const char* registerThread(const char*) { return ""; } PROFILER_API void setEventTracingEnabled(bool) { } PROFILER_API void setLowPriorityEventTracing(bool) { } PROFILER_API void setContextSwitchLogFilename(const char*) { } PROFILER_API const char* getContextSwitchLogFilename() { return ""; } PROFILER_API void startListenSignalToCapture() { } PROFILER_API void stopListenSignalToCapture() { } #endif PROFILER_API uint8_t versionMajor() { static_assert(0 <= EASY_PROFILER_VERSION_MAJOR && EASY_PROFILER_VERSION_MAJOR <= 255, "EASY_PROFILER_VERSION_MAJOR must be defined in range [0, 255]"); return EASY_PROFILER_VERSION_MAJOR; } PROFILER_API uint8_t versionMinor() { static_assert(0 <= EASY_PROFILER_VERSION_MINOR && EASY_PROFILER_VERSION_MINOR <= 255, "EASY_PROFILER_VERSION_MINOR must be defined in range [0, 255]"); return EASY_PROFILER_VERSION_MINOR; } PROFILER_API uint16_t versionPatch() { static_assert(0 <= EASY_PROFILER_VERSION_PATCH && EASY_PROFILER_VERSION_PATCH <= 65535, "EASY_PROFILER_VERSION_PATCH must be defined in range [0, 65535]"); return EASY_PROFILER_VERSION_PATCH; } PROFILER_API uint32_t version() { return EASY_CURRENT_VERSION; } PROFILER_API const char* versionName() { return EASY_PROFILER_PRODUCT_VERSION #ifdef EASY_PROFILER_API_DISABLED "_disabled" #endif ; } } ////////////////////////////////////////////////////////////////////////// SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length) : BaseBlockData(block) { auto pName = const_cast(name()); if (name_length) strncpy(pName, block.name(), name_length); pName[name_length] = 0; } ////////////////////////////////////////////////////////////////////////// BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, EasyBlockStatus _status, int _line, block_type_t _block_type, color_t _color) : m_id(_id) , m_line(_line) , m_type(_block_type) , m_color(_color) , m_status(_status) { } ////////////////////////////////////////////////////////////////////////// #ifndef EASY_BLOCK_DESC_FULL_COPY # define EASY_BLOCK_DESC_FULL_COPY 0 #endif #if EASY_BLOCK_DESC_FULL_COPY == 0 # define EASY_BLOCK_DESC_STRING const char* # define EASY_BLOCK_DESC_STRING_LEN(s) static_cast(strlen(s) + 1) # define EASY_BLOCK_DESC_STRING_VAL(s) s #else # define EASY_BLOCK_DESC_STRING std::string # define EASY_BLOCK_DESC_STRING_LEN(s) static_cast(s.size() + 1) # define EASY_BLOCK_DESC_STRING_VAL(s) s.c_str() #endif 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 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)) { } const char* name() const { return EASY_BLOCK_DESC_STRING_VAL(m_name); } const char* filename() const { return EASY_BLOCK_DESC_STRING_VAL(m_filename); } uint16_t nameSize() const { return EASY_BLOCK_DESC_STRING_LEN(m_name); } uint16_t filenameSize() const { return EASY_BLOCK_DESC_STRING_LEN(m_filename); } }; // END of class BlockDescriptor. ////////////////////////////////////////////////////////////////////////// ThreadStorage::ThreadStorage() : id(getCurrentThreadId()), allowChildren(true), named(false) #ifndef _WIN32 , pthread_id(pthread_self()) #endif { expired = ATOMIC_VAR_INIT(false); } void ThreadStorage::storeBlock(const profiler::Block& block) { #if EASY_MEASURE_STORAGE_EXPAND != 0 EASY_LOCAL_STATIC_PTR(const BaseBlockDescriptor*, desc,\ MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED ? profiler::ON : profiler::OFF, EASY_UNIQUE_LINE_ID, "EasyProfiler.ExpandStorage",\ __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White)); EASY_THREAD_LOCAL static profiler::timestamp_t beginTime = 0ULL; EASY_THREAD_LOCAL static profiler::timestamp_t endTime = 0ULL; #endif auto name_length = static_cast(strlen(block.name())); auto size = static_cast(sizeof(BaseBlockData) + name_length + 1); #if EASY_MEASURE_STORAGE_EXPAND != 0 const bool expanded = (desc->m_status & profiler::ON) && blocks.closedList.need_expand(size); if (expanded) beginTime = getCurrentTime(); #endif auto data = blocks.closedList.allocate(size); #if EASY_MEASURE_STORAGE_EXPAND != 0 if (expanded) endTime = getCurrentTime(); #endif ::new (data) SerializedBlock(block, name_length); blocks.usedMemorySize += size; #if EASY_MEASURE_STORAGE_EXPAND != 0 if (expanded) { profiler::Block b(beginTime, desc->id(), ""); b.finish(endTime); size = static_cast(sizeof(BaseBlockData) + 1); data = blocks.closedList.allocate(size); ::new (data) SerializedBlock(b, 0); blocks.usedMemorySize += size; } #endif } void ThreadStorage::storeCSwitch(const profiler::Block& block) { auto name_length = static_cast(strlen(block.name())); auto size = static_cast(sizeof(BaseBlockData) + name_length + 1); auto data = sync.closedList.allocate(size); ::new (data) SerializedBlock(block, name_length); sync.usedMemorySize += size; } void ThreadStorage::clearClosed() { blocks.clearClosed(); sync.clearClosed(); } ////////////////////////////////////////////////////////////////////////// ThreadGuard::~ThreadGuard() { #ifndef EASY_PROFILER_API_DISABLED if (m_id != 0 && THREAD_STORAGE != nullptr && THREAD_STORAGE->id == m_id) { EASY_EVENT("ThreadFinished", profiler::colors::Dark); THREAD_STORAGE->expired.store(true, std::memory_order_release); THREAD_STORAGE = nullptr; } #endif } ////////////////////////////////////////////////////////////////////////// ProfileManager::ProfileManager() { m_isEnabled = ATOMIC_VAR_INIT(false); m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_EVENT_TRACING_ENABLED); m_stopListen = ATOMIC_VAR_INIT(false); } ProfileManager::~ProfileManager() { #ifndef EASY_PROFILER_API_DISABLED stopListenSignalToCapture(); #endif for (auto desc : m_descriptors) { if (desc != nullptr) delete desc; } } #ifndef EASY_MAGIC_STATIC_CPP11 class ProfileManagerInstance { friend ProfileManager; ProfileManager instance; } PROFILE_MANAGER; #endif ProfileManager& ProfileManager::instance() { #ifndef EASY_MAGIC_STATIC_CPP11 return PROFILE_MANAGER.instance; #else ///C++11 makes possible to create Singleton without any warry about thread-safeness ///http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/ static ProfileManager profileManager; return profileManager; #endif } ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id) { guard_lock_t lock(m_spin); return m_threads[_thread_id]; } ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_id) { auto it = m_threads.find(_thread_id); return it != m_threads.end() ? &it->second : nullptr; } const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _defaultStatus, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) { guard_lock_t lock(m_storedSpin); descriptors_map_t::key_type key(_autogenUniqueId); auto it = m_descriptorsMap.find(key); if (it != m_descriptorsMap.end()) return m_descriptors[it->second]; auto desc = new BlockDescriptor(static_cast(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color); m_usedMemorySize += desc->m_size; m_descriptors.emplace_back(desc); m_descriptorsMap.emplace(key, desc->id()); return desc; } void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName) { if (!m_isEnabled.load(std::memory_order_acquire) || !(_desc->m_status & profiler::ON)) return; if (THREAD_STORAGE == nullptr) THREAD_STORAGE = &threadStorage(getCurrentThreadId()); #if EASY_ENABLE_BLOCK_STATUS != 0 if (!THREAD_STORAGE->allowChildren) return; #endif profiler::Block b(_desc, _runtimeName); b.start(); b.m_end = b.m_begin; THREAD_STORAGE->storeBlock(b); } void ProfileManager::storeBlockForce(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t& _timestamp) { if (!(_desc->m_status & profiler::ON)) return; if (THREAD_STORAGE == nullptr) THREAD_STORAGE = &threadStorage(getCurrentThreadId()); #if EASY_ENABLE_BLOCK_STATUS != 0 if (!THREAD_STORAGE->allowChildren) return; #endif profiler::Block b(_desc, _runtimeName); b.start(); b.m_end = b.m_begin; _timestamp = b.m_begin; THREAD_STORAGE->storeBlock(b); } void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp) { if (!(_desc->m_status & profiler::ON)) return; if (THREAD_STORAGE == nullptr) THREAD_STORAGE = &threadStorage(getCurrentThreadId()); #if EASY_ENABLE_BLOCK_STATUS != 0 if (!THREAD_STORAGE->allowChildren) return; #endif profiler::Block b(_desc, _runtimeName); b.m_end = b.m_begin = _timestamp; THREAD_STORAGE->storeBlock(b); } void ProfileManager::beginBlock(Block& _block) { if (!m_isEnabled.load(std::memory_order_acquire)) return; if (THREAD_STORAGE == nullptr) THREAD_STORAGE = &threadStorage(getCurrentThreadId()); #if EASY_ENABLE_BLOCK_STATUS != 0 if (THREAD_STORAGE->allowChildren) { #endif if (_block.m_status & profiler::ON) _block.start(); #if EASY_ENABLE_BLOCK_STATUS != 0 THREAD_STORAGE->allowChildren = !(_block.m_status & profiler::OFF_RECURSIVE); } else if (_block.m_status & FORCE_ON_FLAG) { _block.start(); _block.m_status = profiler::FORCE_ON_WITHOUT_CHILDREN; } else { _block.m_status = profiler::OFF_RECURSIVE; } #endif THREAD_STORAGE->blocks.openedList.emplace(_block); } void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin) { auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id); if (ts != nullptr) // Dirty hack: _target_thread_id will be written to the field "block_id_t m_id" // and will be available calling method id(). ts->sync.openedList.emplace(_time, _target_thread_id, _target_process); } void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin) { auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id); if (ts != nullptr) { profiler::Block b(_time, _target_thread_id, ""); b.finish(_time); ts->storeCSwitch(b); } } void ProfileManager::endBlock() { if (!m_isEnabled.load(std::memory_order_acquire)) return; if (THREAD_STORAGE == nullptr || THREAD_STORAGE->blocks.openedList.empty()) return; Block& lastBlock = THREAD_STORAGE->blocks.openedList.top(); if (lastBlock.m_status & profiler::ON) { if (!lastBlock.finished()) lastBlock.finish(); THREAD_STORAGE->storeBlock(lastBlock); } else { lastBlock.m_end = lastBlock.m_begin; // this is to restrict endBlock() call inside ~Block() } THREAD_STORAGE->blocks.openedList.pop(); #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); #endif } void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin) { auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id); if (ts == nullptr || ts->sync.openedList.empty()) return; Block& lastBlock = ts->sync.openedList.top(); lastBlock.finish(_endtime); ts->storeCSwitch(lastBlock); ts->sync.openedList.pop(); } void ProfileManager::setEnabled(bool isEnable, bool _setTime) { auto time = getCurrentTime(); const bool prev = m_isEnabled.exchange(isEnable, std::memory_order_release); if (prev == isEnable) 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; } } else { #ifdef _WIN32 EasyEventTracer::instance().disable(); #endif if (_setTime) { guard_lock_t lk(m_spin); m_endTime = time; } } } void ProfileManager::setEventTracingEnabled(bool _isEnable) { m_isEventTracingEnabled.store(_isEnable, std::memory_order_release); } ////////////////////////////////////////////////////////////////////////// bool ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread) { if (_registeredThread.expired.load(std::memory_order_acquire)) return true; #ifdef _WIN32 // Check thread for Windows DWORD exitCode = 0; auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _registeredThread.id); if (hThread == nullptr || GetExitCodeThread(hThread, &exitCode) == FALSE || exitCode != STILL_ACTIVE) { // Thread has been expired _registeredThread.expired.store(true, std::memory_order_release); if (hThread != nullptr) CloseHandle(hThread); return true; } if (hThread != nullptr) CloseHandle(hThread); return false; #else return pthread_kill(_registeredThread.pthread_id, 0) != 0; #endif } ////////////////////////////////////////////////////////////////////////// uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) { const bool wasEnabled = m_isEnabled.load(std::memory_order_acquire); #ifndef _WIN32 const bool eventTracingEnabled = m_isEventTracingEnabled.load(std::memory_order_acquire); #endif if (wasEnabled) ::profiler::setEnabled(false); // 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); // 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)); // TODO: think about better solution because this one is not 100% safe... #ifndef _WIN32 if (eventTracingEnabled) { // Read thread context switch events from temporary file uint64_t timestamp = 0; uint32_t thread_from = 0, thread_to = 0; std::ifstream infile(m_csInfoFilename.c_str()); if(infile.is_open()) { std::string next_task_name; while (infile >> timestamp >> thread_from >> thread_to >> next_task_name) { beginContextSwitch(thread_from, timestamp, thread_to, next_task_name.c_str(), false); endContextSwitch(thread_to, timestamp, false); } } } #endif // Calculate used memory total size and total blocks number uint64_t usedMemorySize = 0; uint32_t blocks_number = 0; for (auto it = m_threads.begin(), end = m_threads.end(); it != end;) { auto& t = it->second; const uint32_t num = static_cast(t.blocks.closedList.size()) + static_cast(t.sync.closedList.size()); const bool expired = checkThreadExpired(t) || t.expired.load(std::memory_order_acquire); if (expired && num == 0) { // Thread has been finished and contains no profiled information. // Remove it now. m_threads.erase(it++); continue; } usedMemorySize += t.blocks.usedMemorySize + t.sync.usedMemorySize; blocks_number += num; ++it; } // Write profiler signature and version _outputStream.write(PROFILER_SIGNATURE); _outputStream.write(EASY_CURRENT_VERSION); // Write CPU frequency to let GUI calculate real time value from CPU clocks #ifdef _WIN32 _outputStream.write(CPU_FREQUENCY); #else #if !defined(USE_STD_CHRONO) double g_TicksPerNanoSec; struct timespec begints, endts; uint64_t begin = 0, end = 0; clock_gettime(CLOCK_MONOTONIC, &begints); begin = getCurrentTime(); volatile uint64_t i; for (i = 0; i < 100000000; i++); /* must be CPU intensive */ end = getCurrentTime(); clock_gettime(CLOCK_MONOTONIC, &endts); struct timespec tmpts; const int NANO_SECONDS_IN_SEC = 1000000000; tmpts.tv_sec = endts.tv_sec - begints.tv_sec; tmpts.tv_nsec = endts.tv_nsec - begints.tv_nsec; if (tmpts.tv_nsec < 0) { tmpts.tv_sec--; tmpts.tv_nsec += NANO_SECONDS_IN_SEC; } uint64_t nsecElapsed = tmpts.tv_sec * 1000000000LL + tmpts.tv_nsec; g_TicksPerNanoSec = (double)(end - begin)/(double)nsecElapsed; int64_t cpu_frequency = int(g_TicksPerNanoSec*1000000); _outputStream.write(cpu_frequency*1000LL); #else _outputStream.write(0LL); #endif #endif // Write begin and end time _outputStream.write(m_beginTime); _outputStream.write(m_endTime); // Write blocks number and used memory size _outputStream.write(blocks_number); _outputStream.write(usedMemorySize); _outputStream.write(static_cast(m_descriptors.size())); _outputStream.write(m_usedMemorySize); // Write block descriptors for (const auto descriptor : m_descriptors) { const auto name_size = descriptor->nameSize(); const auto filename_size = descriptor->filenameSize(); const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); _outputStream.write(size); _outputStream.write(*descriptor); _outputStream.write(name_size); _outputStream.write(descriptor->name(), name_size); _outputStream.write(descriptor->filename(), filename_size); } // Write blocks and context switch events for each thread for (auto it = m_threads.begin(), end = m_threads.end(); it != end;) { auto& t = it->second; _outputStream.write(it->first); const auto name_size = static_cast(t.name.size() + 1); _outputStream.write(name_size); _outputStream.write(name_size > 1 ? t.name.c_str() : "", name_size); _outputStream.write(t.sync.closedList.size()); if (!t.sync.closedList.empty()) t.sync.closedList.serialize(_outputStream); _outputStream.write(t.blocks.closedList.size()); if (!t.blocks.closedList.empty()) t.blocks.closedList.serialize(_outputStream); t.clearClosed(); t.blocks.openedList.clear(); t.sync.openedList.clear(); if (t.expired.load(std::memory_order_acquire)) m_threads.erase(it++); // Remove expired thread after writing all profiled information else ++it; } //if (wasEnabled) // ::profiler::setEnabled(true); return blocks_number; } uint32_t ProfileManager::dumpBlocksToFile(const char* _filename) { profiler::OStream outputStream; const auto blocksNumber = dumpBlocksToStream(outputStream); std::ofstream of(_filename, std::fstream::binary); of << outputStream.stream().str(); return blocksNumber; } const char* ProfileManager::registerThread(const char* name, ThreadGuard& threadGuard) { if (THREAD_STORAGE == nullptr) THREAD_STORAGE = &threadStorage(getCurrentThreadId()); if (!THREAD_STORAGE->named) { THREAD_STORAGE->named = true; THREAD_STORAGE->name = name; } threadGuard.m_id = THREAD_STORAGE->id; return THREAD_STORAGE->name.c_str(); } const char* ProfileManager::registerThread(const char* name) { if (THREAD_STORAGE == nullptr) THREAD_STORAGE = &threadStorage(getCurrentThreadId()); if (!THREAD_STORAGE->named) { THREAD_STORAGE->named = true; THREAD_STORAGE->name = name; } return THREAD_STORAGE->name.c_str(); } void ProfileManager::setBlockStatus(block_id_t _id, EasyBlockStatus _status) { if (m_isEnabled.load(std::memory_order_acquire)) return; // Changing blocks statuses is restricted while profile session is active guard_lock_t lock(m_storedSpin); if (_id < m_descriptors.size()) { auto desc = m_descriptors[_id]; lock.unlock(); desc->m_status = _status; } } void ProfileManager::startListenSignalToCapture(uint16_t _port) { if (!m_isAlreadyListened) { m_stopListen.store(false, std::memory_order_release); m_listenThread = std::move(std::thread(&ProfileManager::listen, this, _port)); m_isAlreadyListened = true; } } void ProfileManager::stopListenSignalToCapture() { m_stopListen.store(true, std::memory_order_release); if (m_listenThread.joinable()) m_listenThread.join(); m_isAlreadyListened = false; } ////////////////////////////////////////////////////////////////////////// //#define EASY_DEBUG_NET_PRINT void ProfileManager::listen(uint16_t _port) { EASY_THREAD_SCOPE("EasyProfiler.Listen"); EasySocket socket; profiler::net::Message replyMessage(profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING); socket.bind(_port); int bytes = 0; while (!m_stopListen.load(std::memory_order_acquire)) { bool hasConnect = false; socket.listen(); socket.accept(); EASY_EVENT("ClientConnected", profiler::colors::White, profiler::OFF); hasConnect = true; #ifdef EASY_DEBUG_NET_PRINT printf("GUI-client connected\n"); #endif // Send reply { const bool wasLowPriorityET = #ifdef _WIN32 EasyEventTracer::instance().isLowPriority(); #else false; #endif profiler::net::EasyProfilerStatus connectionReply(m_isEnabled.load(std::memory_order_acquire), m_isEventTracingEnabled.load(std::memory_order_acquire), wasLowPriorityET); bytes = socket.send(&connectionReply, sizeof(connectionReply)); hasConnect = bytes > 0; } while (hasConnect && !m_stopListen.load(std::memory_order_acquire)) { char buffer[256] = {}; bytes = socket.receive(buffer, 255); hasConnect = bytes > 0; char *buf = &buffer[0]; if (bytes > 0) { profiler::net::Message* message = (profiler::net::Message*)buf; if (!message->isEasyNetMessage()){ continue; } switch (message->type) { case profiler::net::MESSAGE_TYPE_CHECK_CONNECTION: { #ifdef EASY_DEBUG_NET_PRINT printf("receive MESSAGE_TYPE_CHECK_CONNECTION\n"); #endif break; } case profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE: { #ifdef EASY_DEBUG_NET_PRINT printf("receive REQUEST_START_CAPTURE\n"); #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(); replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING; bytes = socket.send(&replyMessage, sizeof(replyMessage)); hasConnect = bytes > 0; break; } case profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE: { #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); //TODO //if connection aborted - ignore this part profiler::net::DataMessage dm; profiler::OStream os; dumpBlocksToStream(os); dm.size = (uint32_t)os.stream().str().length(); int packet_size = int(sizeof(dm)) + int(dm.size); char *sendbuf = new char[packet_size]; memset(sendbuf, 0, packet_size); memcpy(sendbuf, &dm, sizeof(dm)); memcpy(sendbuf + sizeof(dm), os.stream().str().c_str(), dm.size); bytes = socket.send(sendbuf, packet_size); hasConnect = bytes > 0; /*std::string tempfilename = "test_snd.prof"; std::ofstream of(tempfilename, std::fstream::binary); of.write((const char*)os.stream().str().c_str(), dm.size); of.close();*/ delete[] sendbuf; replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_END; bytes = socket.send(&replyMessage, sizeof(replyMessage)); hasConnect = bytes > 0; break; } case profiler::net::MESSAGE_TYPE_REQUEST_BLOCKS_DESCRIPTION: { #ifdef EASY_DEBUG_NET_PRINT printf("receive REQUEST_BLOCKS_DESCRIPTION\n"); #endif profiler::OStream os; // Write profiler signature and version os.write(PROFILER_SIGNATURE); os.write(EASY_CURRENT_VERSION); // Write block descriptors m_storedSpin.lock(); os.write(static_cast(m_descriptors.size())); os.write(m_usedMemorySize); for (const auto descriptor : m_descriptors) { const auto name_size = descriptor->nameSize(); const auto filename_size = descriptor->filenameSize(); const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); os.write(size); os.write(*descriptor); os.write(name_size); os.write(descriptor->name(), name_size); os.write(descriptor->filename(), filename_size); } m_storedSpin.unlock(); // END of Write block descriptors. profiler::net::DataMessage dm((uint32_t)os.stream().str().length(), profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION); int packet_size = int(sizeof(dm)) + int(dm.size); char *sendbuf = new char[packet_size]; memset(sendbuf, 0, packet_size); memcpy(sendbuf, &dm, sizeof(dm)); memcpy(sendbuf + sizeof(dm), os.stream().str().c_str(), dm.size); bytes = socket.send(sendbuf, packet_size); hasConnect = bytes > 0; delete[] sendbuf; replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END; bytes = socket.send(&replyMessage, sizeof(replyMessage)); hasConnect = bytes > 0; break; } case profiler::net::MESSAGE_TYPE_EDIT_BLOCK_STATUS: { auto data = reinterpret_cast(message); #ifdef EASY_DEBUG_NET_PRINT printf("receive EDIT_BLOCK_STATUS id=%u status=%u\n", data->id, data->status); #endif setBlockStatus(data->id, static_cast<::profiler::EasyBlockStatus>(data->status)); break; } case profiler::net::MESSAGE_TYPE_EVENT_TRACING_STATUS: { auto data = reinterpret_cast(message); #ifdef EASY_DEBUG_NET_PRINT printf("receive EVENT_TRACING_STATUS on=%d\n", data->flag ? 1 : 0); #endif m_isEventTracingEnabled.store(data->flag, std::memory_order_release); break; } case profiler::net::MESSAGE_TYPE_EVENT_TRACING_PRIORITY: { #if defined(_WIN32) || defined(EASY_DEBUG_NET_PRINT) auto data = reinterpret_cast(message); #endif #ifdef EASY_DEBUG_NET_PRINT printf("receive EVENT_TRACING_PRIORITY low=%d\n", data->flag ? 1 : 0); #endif #ifdef _WIN32 EasyEventTracer::instance().setLowPriority(data->flag); #endif break; } default: break; } //nn_freemsg (buf); } } } } //////////////////////////////////////////////////////////////////////////