/** Lightweight profiler library for c++ Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin This program is free software : you can redistribute it and / or modify it 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 . **/ #ifndef ___PROFILER____MANAGER____H______ #define ___PROFILER____MANAGER____H______ #include "profiler/profiler.h" #include "spin_lock.h" #include #include #include #include #include #include ////////////////////////////////////////////////////////////////////////// #ifdef _WIN32 #include #else #include #include #include #include #endif inline uint32_t getCurrentThreadId() { #ifdef _WIN32 return (uint32_t)::GetCurrentThreadId(); #else EASY_THREAD_LOCAL static const pid_t x = syscall(__NR_gettid); EASY_THREAD_LOCAL static const uint32_t _id = (uint32_t)x;//std::hash()(std::this_thread::get_id()); return _id; #endif } namespace profiler { class SerializedBlock; } ////////////////////////////////////////////////////////////////////////// template class chunk_allocator final { struct chunk { T data[N]; }; std::list m_chunks; uint16_t m_size; public: chunk_allocator() : m_size(0) { m_chunks.emplace_back(); } T* allocate(uint16_t n) { if (m_size + n <= N) { T* data = m_chunks.back().data + m_size; m_size += n; return data; } m_size = n; m_chunks.emplace_back(); return m_chunks.back().data; } void clear() { m_size = 0; m_chunks.clear(); m_chunks.emplace_back(); } }; ////////////////////////////////////////////////////////////////////////// const uint16_t SIZEOF_CSWITCH = sizeof(profiler::BaseBlockData) + 1; typedef std::vector serialized_list_t; template struct BlocksList final { BlocksList() = default; class Stack final { //std::stack m_stack; std::vector m_stack; public: inline void clear() { m_stack.clear(); } inline bool empty() const { return m_stack.empty(); } inline void emplace(profiler::Block& _block) { //m_stack.emplace(_block); m_stack.emplace_back(_block); } inline void emplace(profiler::Block&& _block) { //m_stack.emplace(_block); m_stack.emplace_back(std::forward(_block)); } template inline void emplace(TArgs ... _args) { //m_stack.emplace(_args); m_stack.emplace_back(_args...); } inline T& top() { //return m_stack.top(); return m_stack.back(); } inline void pop() { //m_stack.pop(); m_stack.pop_back(); } }; chunk_allocator alloc; Stack openedList; serialized_list_t closedList; uint64_t usedMemorySize = 0; void clearClosed() { serialized_list_t().swap(closedList); alloc.clear(); usedMemorySize = 0; } }; class ThreadStorage final { public: BlocksList, SIZEOF_CSWITCH * (uint16_t)1024U> blocks; BlocksList sync; std::string name; bool named = false; void storeBlock(const profiler::Block& _block); void storeCSwitch(const profiler::Block& _block); void clearClosed(); ThreadStorage() = default; }; ////////////////////////////////////////////////////////////////////////// class ProfileManager final { ProfileManager(); ProfileManager(const ProfileManager& p) = delete; ProfileManager& operator=(const ProfileManager&) = delete; typedef profiler::guard_lock guard_lock_t; typedef std::map map_of_threads_stacks; typedef std::vector block_descriptors_t; map_of_threads_stacks m_threads; block_descriptors_t m_descriptors; uint64_t m_usedMemorySize = 0; profiler::spin_lock m_spin; profiler::spin_lock m_storedSpin; bool m_isEnabled = false; std::string m_csInfoFilename = "/tmp/cs_profiling_info.log"; public: static ProfileManager& instance(); ~ProfileManager(); template const profiler::BaseBlockDescriptor& addBlockDescriptor(TArgs ... _args) { guard_lock_t lock(m_storedSpin); const auto id = static_cast(m_descriptors.size()); m_descriptors.emplace_back(new profiler::BlockDescriptor(m_usedMemorySize, id, _args...)); return *m_descriptors.back(); } void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName); void beginBlock(profiler::Block& _block); void endBlock(); void setEnabled(bool isEnable); uint32_t dumpBlocksToFile(const char* filename); const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line); void setContextSwitchLogFilename(const char* name) { m_csInfoFilename = name; } const char* getContextSwitchLogFilename() const { return m_csInfoFilename.c_str(); } void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id); void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id); void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime); private: ThreadStorage& threadStorage(profiler::thread_id_t _thread_id) { guard_lock_t lock(m_spin); return m_threads[_thread_id]; } ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id) { guard_lock_t lock(m_spin); auto it = m_threads.find(_thread_id); return it != m_threads.end() ? &it->second : nullptr; } }; #endif