/************************************************************************ * 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 * : * : 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 . ************************************************************************/ #include "profile_manager.h" #include "profiler/serialized_block.h" #include "event_trace_win.h" #include "profiler/easy_net.h" #include "easy_socket.h" #include #include #include ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// using namespace profiler; #ifdef _WIN32 extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY; #endif //auto& MANAGER = ProfileManager::instance(); #define MANAGER ProfileManager::instance() extern "C" { PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) { return MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color); } PROFILER_API void endBlock() { MANAGER.endBlock(); } PROFILER_API void setEnabled(bool isEnable) { MANAGER.setEnabled(isEnable); #ifdef _WIN32 if (isEnable) EasyEventTracer::instance().enable(true); else EasyEventTracer::instance().disable(); #endif } 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* setThreadName(const char* name, const char* filename, const char* _funcname, int line) { return MANAGER.setThreadName(name, filename, _funcname, line); } PROFILER_API void setContextSwitchLogFilename(const char* name) { return MANAGER.setContextSwitchLogFilename(name); } PROFILER_API const char* getContextSwitchLogFilename() { return MANAGER.getContextSwitchLogFilename(); } PROFILER_API void startListenSignalToCapture() { return MANAGER.startListenSignalToCapture(); } PROFILER_API void stopListenSignalToCapture() { return MANAGER.stopListenSignalToCapture(); } } 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(int _line, block_type_t _block_type, color_t _color) : m_line(_line) , m_type(_block_type) , m_color(_color) { } BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) : BaseBlockDescriptor(_line, _block_type, _color) , m_name(_name) , m_filename(_filename) { _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; } ////////////////////////////////////////////////////////////////////////// void ThreadStorage::storeBlock(const profiler::Block& block) { auto name_length = static_cast(strlen(block.name())); auto size = static_cast(sizeof(BaseBlockData) + name_length + 1); auto data = blocks.alloc.allocate(size); ::new (static_cast(data)) SerializedBlock(block, name_length); blocks.usedMemorySize += size; blocks.closedList.emplace_back(reinterpret_cast(data)); } 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.alloc.allocate(size); ::new (static_cast(data)) SerializedBlock(block, name_length); sync.usedMemorySize += size; sync.closedList.emplace_back(reinterpret_cast(data)); } void ThreadStorage::clearClosed() { blocks.clearClosed(); sync.clearClosed(); } ////////////////////////////////////////////////////////////////////////// EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; // #ifdef _WIN32 // LPTOP_LEVEL_EXCEPTION_FILTER PREVIOUS_FILTER = NULL; // LONG WINAPI easyTopLevelExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo) // { // std::ofstream testexp("TEST_EXP.txt", std::fstream::binary); // testexp.write("APPLICATION CRASHED!", strlen("APPLICATION CRASHED!")); // // EasyEventTracer::instance().disable(); // if (PREVIOUS_FILTER) // return PREVIOUS_FILTER(ExceptionInfo); // return EXCEPTION_CONTINUE_SEARCH; // } // #endif ProfileManager::ProfileManager() { // #ifdef _WIN32 // PREVIOUS_FILTER = SetUnhandledExceptionFilter(easyTopLevelExceptionFilter); // #endif m_stopListen = ATOMIC_VAR_INIT(false); } ProfileManager::~ProfileManager() { stopListenSignalToCapture(); if(m_listenThread.joinable()){ m_listenThread.join(); } } ProfileManager& ProfileManager::instance() { ///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 m_profileManager; return m_profileManager; } void ProfileManager::beginBlock(Block& _block) { if (!m_isEnabled) return; if (THREAD_STORAGE == nullptr) THREAD_STORAGE = &threadStorage(getCurrentThreadId()); if (!_block.isFinished()) THREAD_STORAGE->blocks.openedList.emplace(_block); else THREAD_STORAGE->storeBlock(_block); } void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id) { auto ts = findThreadStorage(_thread_id); if (ts != nullptr) ts->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, ""); } void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id) { auto ts = findThreadStorage(_thread_id); if (ts != nullptr) { profiler::Block lastBlock(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, ""); lastBlock.finish(_time); ts->storeCSwitch(lastBlock); } } void ProfileManager::endBlock() { if (!m_isEnabled) return; if (THREAD_STORAGE->blocks.openedList.empty()) return; Block& lastBlock = THREAD_STORAGE->blocks.openedList.top(); if (!lastBlock.isFinished()) lastBlock.finish(); THREAD_STORAGE->storeBlock(lastBlock); THREAD_STORAGE->blocks.openedList.pop(); } void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime) { auto ts = 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::startListenSignalToCapture() { if(!m_isAlreadyListened) { m_stopListen.store(false); m_listenThread = std::thread(&ProfileManager::startListen, this); m_isAlreadyListened = true; } } void ProfileManager::stopListenSignalToCapture() { m_stopListen.store(true); m_isAlreadyListened = false; } void ProfileManager::setEnabled(bool isEnable) { m_isEnabled = isEnable; } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// #define STORE_CSWITCHES_SEPARATELY uint32_t ProfileManager::dumpBlocksToStream(StreamWriter& of) { const bool wasEnabled = m_isEnabled; if (wasEnabled) ::profiler::setEnabled(false); #ifndef _WIN32 uint64_t timestamp; uint32_t thread_from, thread_to; std::ifstream infile(m_csInfoFilename.c_str()); if(infile.is_open()) { static const auto desc = addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); while (infile >> timestamp >> thread_from >> thread_to) { beginContextSwitch(thread_from, timestamp, desc); endContextSwitch(thread_to, timestamp); } } #endif uint64_t usedMemorySize = 0; uint32_t blocks_number = 0; for (const auto& thread_storage : m_threads) { const auto& t = thread_storage.second; usedMemorySize += t.blocks.usedMemorySize + t.sync.usedMemorySize; blocks_number += static_cast(t.blocks.closedList.size()) + static_cast(t.sync.closedList.size()); } #ifdef _WIN32 of.write(CPU_FREQUENCY); #else of.write(0LL); #endif of.write(blocks_number); of.write(usedMemorySize); of.write(static_cast(m_descriptors.size())); of.write(m_usedMemorySize); for (const auto& descriptor : m_descriptors) { const auto name_size = static_cast(strlen(descriptor.name()) + 1); const auto filename_size = static_cast(strlen(descriptor.file()) + 1); const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); of.write(size); of.write(descriptor); of.write(name_size); of.write(descriptor.name(), name_size); of.write(descriptor.file(), filename_size); } for (auto& thread_storage : m_threads) { auto& t = thread_storage.second; of.write(thread_storage.first); #ifdef STORE_CSWITCHES_SEPARATELY of.write(static_cast(t.blocks.closedList.size())); #else of.write(static_cast(t.blocks.closedList.size()) + static_cast(t.sync.closedList.size())); uint32_t i = 0; #endif for (auto b : t.blocks.closedList) { #ifndef STORE_CSWITCHES_SEPARATELY if (i < t.sync.closedList.size()) { auto s = t.sync.closedList[i]; if (s->end() <= b->end())// || s->begin() >= b->begin()) //if (((s->end() <= b->end() && s->end() >= b->begin()) || (s->begin() >= b->begin() && s->begin() <= b->end()))) { if (s->m_begin < b->m_begin) s->m_begin = b->m_begin; if (s->m_end > b->m_end) s->m_end = b->m_end; of.writeBlock(s); ++i; } } #endif of.writeBlock(b); } #ifdef STORE_CSWITCHES_SEPARATELY of.write(static_cast(t.sync.closedList.size())); for (auto b : t.sync.closedList) { #else for (; i < t.sync.closedList.size(); ++i) { auto b = t.sync.closedList[i]; #endif of.writeBlock(b); } t.clearClosed(); } // if (wasEnabled) // ::profiler::setEnabled(true); return blocks_number; } uint32_t ProfileManager::dumpBlocksToFile(const char* filename) { StreamWriter os; auto res = dumpBlocksToStream(os); std::ofstream of(filename, std::fstream::binary); of << os.stream().str(); return res; } const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line) { if (THREAD_STORAGE == nullptr) { THREAD_STORAGE = &threadStorage(getCurrentThreadId()); } if (!THREAD_STORAGE->named) { const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black); THREAD_STORAGE->storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name)); THREAD_STORAGE->name = name; THREAD_STORAGE->named = true; } return THREAD_STORAGE->name.c_str(); } ////////////////////////////////////////////////////////////////////////// /* #include #include #include #include #include #include */ void ProfileManager::startListen() { EasySocket socket; socket.setAddress("127.0.0.1",28077); while(!m_stopListen.load() && (socket.connect() < 0 ) ) {} profiler::net::Message replyMessage(profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING); char buffer[256] = {}; //bzero(buffer,256); while(!m_stopListen.load()) { int bytes = socket.read(buffer,255); 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_REQUEST_START_CAPTURE: { //printf ("RECEIVED MESSAGE_TYPE_REQUEST_START_CAPTURE\n"); profiler::setEnabled(true); replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING; socket.write(&replyMessage,sizeof(replyMessage)); } break; case profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE: { //printf ("RECEIVED MESSAGE_TYPE_REQUEST_STOP_CAPTURE\n"); profiler::setEnabled(false); replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_PREPARE_BLOCKS; int send_bytes = socket.write(&replyMessage,sizeof(replyMessage)); profiler::net::DataMessage dm; StreamWriter os; dumpBlocksToStream(os); dm.size = 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); send_bytes = socket.write(sendbuf,packet_size); delete [] sendbuf; replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_END_SEND_BLOCKS; send_bytes = socket.write(&replyMessage,sizeof(replyMessage)); } break; default: break; } //nn_freemsg (buf); } } }