0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-26 16:11:02 +08:00
easy_profiler/easy_profiler_core/profile_manager.cpp
Holger Rapp 7641951247 Fixes for clang 3.8.
- Disables warnings for no-braced-scalar-init which shows up for ATOMIC_VAR_INIT.
- Fixes compile warnings, mostly "moving a temporary objects prevents
  copy elision" (-Wpessimizing-move) and unused variables.
- Adds some necessary fields to make the sample compile after having
  build the core library.
2017-02-07 17:19:58 +01:00

1327 lines
45 KiB
C++

/************************************************************************
* 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 <http://www.gnu.org/licenses/>.
************************************************************************/
#include <algorithm>
#include <fstream>
#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"
#ifdef min
#undef min
#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_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<uint32_t>(v_major) << 24) | (static_cast<uint32_t>(v_minor) << 16) | static_cast<uint32_t>(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
//////////////////////////////////////////////////////////////////////////
# 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;
#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;
EASY_THREAD_LOCAL static int32_t THREAD_STACK_SIZE = 0;
//////////////////////////////////////////////////////////////////////////
#ifdef BUILD_WITH_EASY_PROFILER
# define EASY_EVENT_RES(res, name, ...)\
EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), MANAGER.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__)));\
res = MANAGER.storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name))
# 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)
# define EASY_FORCE_EVENT3(ts, 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(ts, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp)
#else
# define EASY_EVENT_RES(res, name, ...)
# define EASY_FORCE_EVENT(timestamp, name, ...)
# define EASY_FORCE_EVENT2(timestamp, name, ...)
# define EASY_FORCE_EVENT3(ts, 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, bool _copyName)
{
return MANAGER.addBlockDescriptor(_status, _autogenUniqueId, _name, _filename, _line, _block_type, _color, _copyName);
}
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.startListen(_port);
}
PROFILER_API void stopListen()
{
return MANAGER.stopListen();
}
#else
PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus, const char*, const char*, const char*, int, block_type_t, color_t, bool) { return reinterpret_cast<const BaseBlockDescriptor*>(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 startListen(uint16_t) { }
PROFILER_API void stopListen() { }
#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<char*>(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<uint16_t>(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<uint16_t>(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_filename; ///< Source file name where this block is declared
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_filename(_filename)
, m_name(_name)
{
}
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), guarded(false)
#ifndef _WIN32
, pthread_id(pthread_self())
#endif
{
expired = ATOMIC_VAR_INIT(0);
frame = ATOMIC_VAR_INIT(false);
}
void ThreadStorage::storeBlock(const profiler::Block& block)
{
#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0
EASY_LOCAL_STATIC_PTR(const BaseBlockDescriptor*, desc,\
MANAGER.addBlockDescriptor(EASY_OPTION_STORAGE_EXPAND_BLOCKS_ON ? 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<uint16_t>(strlen(block.name()));
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
#if EASY_OPTION_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_OPTION_MEASURE_STORAGE_EXPAND != 0
if (expanded) endTime = getCurrentTime();
#endif
::new (data) SerializedBlock(block, name_length);
blocks.usedMemorySize += size;
#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0
if (expanded)
{
profiler::Block b(beginTime, desc->id(), "");
b.finish(endTime);
size = static_cast<uint16_t>(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<uint16_t>(strlen(block.name()));
auto size = static_cast<uint16_t>(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)
{
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;
}
#endif
}
//////////////////////////////////////////////////////////////////////////
ProfileManager::ProfileManager() :
#ifdef _WIN32
m_processId(GetProcessId(GetCurrentProcess()))
#else
m_processId((processid_t)getpid())
#endif
, m_usedMemorySize(0)
, m_beginTime(0)
, m_endTime(0)
{
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);
#if !defined(EASY_PROFILER_API_DISABLED) && EASY_OPTION_START_LISTEN_ON_STARTUP != 0
startListen(profiler::DEFAULT_PORT);
#endif
}
ProfileManager::~ProfileManager()
{
#ifndef EASY_PROFILER_API_DISABLED
stopListen();
#endif
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
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)
{
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,
bool _copyName)
{
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];
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<char*>(data) + sizeof(BlockDescriptor);
strncpy(name, _name, nameLen);
desc = ::new (data)BlockDescriptor(static_cast<block_id_t>(m_descriptors.size()), _defaultStatus, name, _filename, _line, _block_type, _color);
}
else
{
void* data = malloc(sizeof(BlockDescriptor));
desc = ::new (data)BlockDescriptor(static_cast<block_id_t>(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color);
}
#else
auto desc = new BlockDescriptor(static_cast<block_id_t>(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color);
#endif
m_descriptors.emplace_back(desc);
m_descriptorsMap.emplace(key, desc->id());
return desc;
}
//////////////////////////////////////////////////////////////////////////
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))
return false;
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))
return false;
#endif
profiler::Block b(_desc, _runtimeName);
b.start();
b.m_end = b.m_begin;
THREAD_STORAGE->storeBlock(b);
return true;
}
//////////////////////////////////////////////////////////////////////////
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 && !(_desc->m_status & FORCE_ON_FLAG))
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 && !(_desc->m_status & FORCE_ON_FLAG))
return;
#endif
profiler::Block b(_desc, _runtimeName);
b.m_end = b.m_begin = _timestamp;
THREAD_STORAGE->storeBlock(b);
}
void ProfileManager::storeBlockForce2(ThreadStorage& _registeredThread, const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp)
{
profiler::Block b(_desc, _runtimeName);
b.m_end = b.m_begin = _timestamp;
_registeredThread.storeBlock(b);
}
//////////////////////////////////////////////////////////////////////////
void ProfileManager::beginBlock(Block& _block)
{
if (++THREAD_STACK_SIZE > 1)
return;
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)
{
#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
if (empty)
THREAD_STORAGE->frame.store(true, std::memory_order_release);
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::endBlock()
{
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;
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();
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 = empty || !(THREAD_STORAGE->blocks.openedList.top().get().m_status & profiler::OFF_RECURSIVE);
#endif
}
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, processid_t _process_id, profiler::timestamp_t _endtime, bool _lockSpin)
{
ThreadStorage* ts = nullptr;
if (_process_id == m_processId)
// If thread owned by current process then create new ThreadStorage if there is no one
ts = _lockSpin ? &threadStorage(_thread_id) : &_threadStorage(_thread_id);
else
// If thread owned by another process OR _process_id IS UNKNOWN then do not create ThreadStorage for this
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::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 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)
{
enableEventTracer();
m_beginTime = time;
}
else
{
disableEventTracer();
m_endTime = time;
}
}
void ProfileManager::setEventTracingEnabled(bool _isEnable)
{
m_isEventTracingEnabled.store(_isEnable, std::memory_order_release);
}
//////////////////////////////////////////////////////////////////////////
char ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
{
const char val = _registeredThread.expired.load(std::memory_order_acquire);
if (val != 0)
return val;
if (_registeredThread.guarded)
return 0;
#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(1, std::memory_order_release);
if (hThread != nullptr)
CloseHandle(hThread);
return 1;
}
if (hThread != nullptr)
CloseHandle(hThread);
return 0;
#else
return 0;//pthread_kill(_registeredThread.pthread_id, 0) != 0;
#endif
}
//////////////////////////////////////////////////////////////////////////
uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bool _lockSpin)
{
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);
disableEventTracer();
m_endTime = getCurrentTime();
}
// This is to make sure that no new descriptors or new threads will be
// added until we finish sending data.
//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();
const profiler::timestamp_t endtime = m_endTime == 0 ? now : std::min(now, m_endTime);
#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;
pid_t process_to = 0;
while (infile >> timestamp >> thread_from >> thread_to >> next_task_name >> process_to) {
beginContextSwitch(thread_from, timestamp, thread_to, next_task_name.c_str(), false);
endContextSwitch(thread_to, (processid_t)process_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;
uint32_t num = static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
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++);
continue;
}
if (expired == 1) {
EASY_FORCE_EVENT3(t, endtime, "ThreadExpired", profiler::colors::Dark);
++num;
}
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);
_outputStream.write(m_processId);
// 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<uint32_t>(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<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size);
_outputStream.write(size);
_outputStream.write<profiler::BaseBlockDescriptor>(*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<uint16_t>(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) != 0)
m_threads.erase(it++); // Remove expired thread after writing all profiled information
else
++it;
}
m_storedSpin.unlock();
m_spin.unlock();
if (_lockSpin)
m_dumpSpin.unlock();
return blocks_number;
}
uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
{
profiler::OStream outputStream;
const auto blocksNumber = dumpBlocksToStream(outputStream, true);
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());
THREAD_STORAGE->guarded = true;
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_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);
if (_id < m_descriptors.size())
{
auto desc = m_descriptors[_id];
lock.unlock();
desc->m_status = _status;
}
}
void ProfileManager::startListen(uint16_t _port)
{
if (!m_isAlreadyListening.exchange(true, std::memory_order_release))
{
m_stopListen.store(false, std::memory_order_release);
m_listenThread = std::thread(&ProfileManager::listen, this, _port);
}
}
void ProfileManager::stopListen()
{
m_stopListen.store(true, std::memory_order_release);
if (m_listenThread.joinable())
m_listenThread.join();
m_isAlreadyListening.store(false, std::memory_order_release);
}
//////////////////////////////////////////////////////////////////////////
//#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_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;
}
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);
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));
hasConnect = bytes > 0;
break;
}
case profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE:
{
#ifdef EASY_DEBUG_NET_PRINT
printf("receive REQUEST_STOP_CAPTURE\n");
#endif
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::OStream 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);
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<uint32_t>(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<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size);
os.write(size);
os.write<profiler::BaseBlockDescriptor>(*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<const profiler::net::BlockStatusMessage*>(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<const profiler::net::BoolMessage*>(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<const profiler::net::BoolMessage*>(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);
}
}
}
}
//////////////////////////////////////////////////////////////////////////