2016-09-06 22:15:50 +03:00
|
|
|
/************************************************************************
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
************************************************************************/
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
#include <algorithm>
|
2016-08-28 21:06:23 +03:00
|
|
|
#include <thread>
|
|
|
|
#include <fstream>
|
2016-09-11 16:57:35 +03:00
|
|
|
#include "profiler/serialized_block.h"
|
|
|
|
#include "profile_manager.h"
|
|
|
|
#include "event_trace_win.h"
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-06 21:52:56 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
using namespace profiler;
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
|
|
|
|
#endif
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
extern timestamp_t getCurrentTime();
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
//auto& MANAGER = ProfileManager::instance();
|
|
|
|
#define MANAGER ProfileManager::instance()
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
extern "C" {
|
|
|
|
|
2016-09-14 23:34:56 +03:00
|
|
|
PROFILER_API const BaseBlockDescriptor* registerDescription(bool _enabled, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
2016-09-06 23:03:05 +03:00
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
return MANAGER.addBlockDescriptor(_enabled, _autogenUniqueId, _name, _filename, _line, _block_type, _color);
|
2016-09-06 23:03:05 +03:00
|
|
|
}
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
PROFILER_API void endBlock()
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
|
|
|
MANAGER.endBlock();
|
|
|
|
}
|
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
PROFILER_API void setEnabled(bool isEnable)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
|
|
|
MANAGER.setEnabled(isEnable);
|
|
|
|
}
|
2016-09-04 14:48:35 +03:00
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName)
|
2016-09-09 00:09:47 +03:00
|
|
|
{
|
|
|
|
MANAGER.storeBlock(_desc, _runtimeName);
|
|
|
|
}
|
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
PROFILER_API void beginBlock(Block& _block)
|
2016-09-07 21:32:14 +03:00
|
|
|
{
|
2016-08-28 21:06:23 +03:00
|
|
|
MANAGER.beginBlock(_block);
|
2016-09-07 21:32:14 +03:00
|
|
|
}
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-07 21:32:14 +03:00
|
|
|
PROFILER_API uint32_t dumpBlocksToFile(const char* filename)
|
|
|
|
{
|
2016-08-28 21:06:23 +03:00
|
|
|
return MANAGER.dumpBlocksToFile(filename);
|
2016-09-07 21:32:14 +03:00
|
|
|
}
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
PROFILER_API const char* registerThread(const char* name)//, const char* filename, const char* _funcname, int line)
|
2016-09-07 21:32:14 +03:00
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
return MANAGER.registerThread(name);// , filename, _funcname, line);
|
2016-09-07 21:32:14 +03:00
|
|
|
}
|
2016-09-04 14:48:35 +03:00
|
|
|
|
2016-09-15 23:15:07 +03:00
|
|
|
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
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
#ifndef _WIN32
|
2016-09-06 23:03:05 +03:00
|
|
|
PROFILER_API void setContextSwitchLogFilename(const char* name)
|
2016-09-05 22:11:03 +03:00
|
|
|
{
|
|
|
|
return MANAGER.setContextSwitchLogFilename(name);
|
|
|
|
}
|
|
|
|
|
2016-09-06 00:22:26 +03:00
|
|
|
PROFILER_API const char* getContextSwitchLogFilename()
|
2016-09-05 22:11:03 +03:00
|
|
|
{
|
|
|
|
return MANAGER.getContextSwitchLogFilename();
|
|
|
|
}
|
2016-09-13 23:03:01 +03:00
|
|
|
#endif
|
2016-09-05 22:11:03 +03:00
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, bool _enabled, int _line, block_type_t _block_type, color_t _color)
|
2016-09-09 00:09:47 +03:00
|
|
|
: m_id(_id)
|
|
|
|
, m_line(_line)
|
2016-08-28 21:06:23 +03:00
|
|
|
, m_type(_block_type)
|
|
|
|
, m_color(_color)
|
2016-09-11 16:57:35 +03:00
|
|
|
, m_enabled(_enabled)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-09-17 11:10:25 +03:00
|
|
|
BlockDescriptor::BlockDescriptor(block_id_t _id, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
2016-09-11 16:57:35 +03:00
|
|
|
: BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color)
|
2016-08-28 21:06:23 +03:00
|
|
|
, m_name(_name)
|
|
|
|
, m_filename(_filename)
|
2016-09-13 23:03:01 +03:00
|
|
|
, m_pEnable(nullptr)
|
2016-09-17 11:10:25 +03:00
|
|
|
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
|
2016-09-13 23:03:01 +03:00
|
|
|
, m_expired(false)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-09-17 11:10:25 +03:00
|
|
|
BlockDescriptor::BlockDescriptor(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
2016-09-11 16:57:35 +03:00
|
|
|
: BaseBlockDescriptor(0, _enabled, _line, _block_type, _color)
|
|
|
|
, m_name(_name)
|
|
|
|
, m_filename(_filename)
|
2016-09-13 23:03:01 +03:00
|
|
|
, m_pEnable(nullptr)
|
2016-09-17 11:10:25 +03:00
|
|
|
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
|
2016-09-13 23:03:01 +03:00
|
|
|
, m_expired(false)
|
2016-09-17 11:10:25 +03:00
|
|
|
{
|
2016-09-11 16:57:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
BlockDescRef::~BlockDescRef()
|
|
|
|
{
|
|
|
|
MANAGER.markExpired(m_desc.id());
|
|
|
|
}
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
void ThreadStorage::storeBlock(const profiler::Block& block)
|
|
|
|
{
|
2016-09-11 16:57:35 +03:00
|
|
|
#if EASY_MEASURE_STORAGE_EXPAND != 0
|
2016-09-13 23:03:01 +03:00
|
|
|
static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, EASY_UNIQUE_LINE_ID, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White);
|
2016-09-11 16:57:35 +03:00
|
|
|
#endif
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
auto name_length = static_cast<uint16_t>(strlen(block.name()));
|
|
|
|
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
|
2016-09-11 16:57:35 +03:00
|
|
|
|
|
|
|
#if EASY_MEASURE_STORAGE_EXPAND != 0
|
2016-09-14 23:34:56 +03:00
|
|
|
const bool expanded = desc->enabled() && blocks.closedList.need_expand(size);
|
|
|
|
profiler::Block b(0ULL, desc->id(), "");
|
2016-09-11 16:57:35 +03:00
|
|
|
if (expanded) b.start();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
auto data = blocks.closedList.allocate(size);
|
|
|
|
|
|
|
|
#if EASY_MEASURE_STORAGE_EXPAND != 0
|
|
|
|
if (expanded) b.finish();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
::new (data) SerializedBlock(block, name_length);
|
2016-09-04 14:48:35 +03:00
|
|
|
blocks.usedMemorySize += size;
|
2016-09-11 16:57:35 +03:00
|
|
|
|
|
|
|
#if EASY_MEASURE_STORAGE_EXPAND != 0
|
|
|
|
if (expanded)
|
|
|
|
{
|
|
|
|
name_length = 0;
|
|
|
|
size = static_cast<uint16_t>(sizeof(BaseBlockData) + 1);
|
|
|
|
data = blocks.closedList.allocate(size);
|
|
|
|
::new (data) SerializedBlock(b, name_length);
|
|
|
|
blocks.usedMemorySize += size;
|
|
|
|
}
|
|
|
|
#endif
|
2016-09-04 14:48:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ThreadStorage::storeCSwitch(const profiler::Block& block)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
|
|
|
auto name_length = static_cast<uint16_t>(strlen(block.name()));
|
|
|
|
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
|
2016-09-11 16:57:35 +03:00
|
|
|
auto data = sync.closedList.allocate(size);
|
|
|
|
::new (data) SerializedBlock(block, name_length);
|
2016-09-04 14:48:35 +03:00
|
|
|
sync.usedMemorySize += size;
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ThreadStorage::clearClosed()
|
|
|
|
{
|
2016-09-04 14:48:35 +03:00
|
|
|
blocks.clearClosed();
|
|
|
|
sync.clearClosed();
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
ProfileManager::ProfileManager()
|
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
m_isEnabled = ATOMIC_VAR_INIT(false);
|
|
|
|
m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_EVENT_TRACING_ENABLED);
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ProfileManager::~ProfileManager()
|
|
|
|
{
|
2016-09-07 21:32:14 +03:00
|
|
|
//dumpBlocksToFile("test.prof");
|
2016-09-09 00:09:47 +03:00
|
|
|
for (auto desc : m_descriptors)
|
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
if (desc != nullptr)
|
|
|
|
delete desc;
|
2016-09-09 00:09:47 +03:00
|
|
|
}
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
void ProfileManager::markExpired(profiler::block_id_t _id)
|
|
|
|
{
|
|
|
|
// Mark block descriptor as expired (descriptor may become expired if it's .dll/.so have been unloaded during application execution).
|
|
|
|
// We can not delete this descriptor now, because we need to send/write all collected data first.
|
|
|
|
|
|
|
|
guard_lock_t lock(m_storedSpin);
|
2016-09-13 23:03:01 +03:00
|
|
|
m_descriptors[_id]->m_expired = true;
|
2016-09-11 16:57:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id)
|
|
|
|
{
|
|
|
|
guard_lock_t lock(m_spin);
|
|
|
|
return m_threads[_thread_id];
|
|
|
|
}
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_id)
|
2016-09-11 16:57:35 +03:00
|
|
|
{
|
|
|
|
auto it = m_threads.find(_thread_id);
|
|
|
|
return it != m_threads.end() ? &it->second : nullptr;
|
|
|
|
}
|
|
|
|
|
2016-09-09 00:09:47 +03:00
|
|
|
void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName)
|
|
|
|
{
|
2016-09-15 23:15:07 +03:00
|
|
|
if (!m_isEnabled.load(std::memory_order_acquire) || !_desc.enabled())
|
2016-09-09 00:09:47 +03:00
|
|
|
return;
|
|
|
|
|
|
|
|
profiler::Block b(_desc, _runtimeName);
|
|
|
|
b.finish(b.begin());
|
|
|
|
|
|
|
|
if (THREAD_STORAGE == nullptr)
|
|
|
|
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
|
|
|
|
|
|
|
THREAD_STORAGE->storeBlock(b);
|
|
|
|
}
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
void ProfileManager::beginBlock(Block& _block)
|
|
|
|
{
|
2016-09-15 23:15:07 +03:00
|
|
|
if (!m_isEnabled.load(std::memory_order_acquire))
|
2016-08-28 21:06:23 +03:00
|
|
|
return;
|
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
if (THREAD_STORAGE == nullptr)
|
|
|
|
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-09 00:09:47 +03:00
|
|
|
THREAD_STORAGE->blocks.openedList.emplace(_block);
|
2016-09-04 14:48:35 +03:00
|
|
|
}
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
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)
|
2016-09-04 14:48:35 +03:00
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
|
2016-09-06 23:03:05 +03:00
|
|
|
if (ts != nullptr)
|
2016-09-13 23:03:01 +03:00
|
|
|
// 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);
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin)
|
2016-09-05 22:11:03 +03:00
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
|
2016-09-06 23:03:05 +03:00
|
|
|
if (ts != nullptr)
|
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
profiler::Block b(_time, _target_thread_id, "");
|
2016-09-09 00:09:47 +03:00
|
|
|
b.finish(_time);
|
|
|
|
ts->storeCSwitch(b);
|
2016-09-05 22:11:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
void ProfileManager::endBlock()
|
|
|
|
{
|
2016-09-15 23:15:07 +03:00
|
|
|
if (!m_isEnabled.load(std::memory_order_acquire))
|
2016-08-28 21:06:23 +03:00
|
|
|
return;
|
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
if (THREAD_STORAGE->blocks.openedList.empty())
|
2016-08-28 21:06:23 +03:00
|
|
|
return;
|
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
Block& lastBlock = THREAD_STORAGE->blocks.openedList.top();
|
2016-09-09 00:09:47 +03:00
|
|
|
if (lastBlock.enabled())
|
|
|
|
{
|
|
|
|
if (!lastBlock.finished())
|
|
|
|
lastBlock.finish();
|
|
|
|
THREAD_STORAGE->storeBlock(lastBlock);
|
|
|
|
}
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
THREAD_STORAGE->blocks.openedList.pop();
|
2016-09-04 14:48:35 +03:00
|
|
|
}
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin)
|
2016-09-04 14:48:35 +03:00
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
|
2016-09-06 23:03:05 +03:00
|
|
|
if (ts == nullptr || ts->sync.openedList.empty())
|
2016-09-04 14:48:35 +03:00
|
|
|
return;
|
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
Block& lastBlock = ts->sync.openedList.top();
|
2016-09-04 14:48:35 +03:00
|
|
|
lastBlock.finish(_endtime);
|
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
ts->storeCSwitch(lastBlock);
|
|
|
|
ts->sync.openedList.pop();
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileManager::setEnabled(bool isEnable)
|
|
|
|
{
|
2016-09-15 23:15:07 +03:00
|
|
|
m_isEnabled.store(isEnable, std::memory_order_release);
|
2016-09-13 23:03:01 +03:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
if (isEnable) {
|
2016-09-15 23:15:07 +03:00
|
|
|
if (m_isEventTracingEnabled.load(std::memory_order_acquire))
|
2016-09-13 23:03:01 +03:00
|
|
|
EasyEventTracer::instance().enable(true);
|
|
|
|
} else {
|
|
|
|
EasyEventTracer::instance().disable();
|
|
|
|
}
|
|
|
|
#endif
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
2016-09-15 23:15:07 +03:00
|
|
|
void ProfileManager::setEventTracingEnabled(bool _isEnable)
|
|
|
|
{
|
|
|
|
m_isEventTracingEnabled.store(_isEnable, std::memory_order_release);
|
|
|
|
}
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
2016-09-15 23:15:07 +03:00
|
|
|
const bool wasEnabled = m_isEnabled.load(std::memory_order_acquire);
|
2016-09-17 15:37:16 +03:00
|
|
|
|
|
|
|
#ifndef _WIN32
|
2016-09-15 23:15:07 +03:00
|
|
|
const bool eventTracingEnabled = m_isEventTracingEnabled.load(std::memory_order_acquire);
|
2016-09-17 15:37:16 +03:00
|
|
|
#endif
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
if (wasEnabled)
|
|
|
|
::profiler::setEnabled(false);
|
|
|
|
|
2016-09-15 23:30:36 +03:00
|
|
|
//TODO remove it
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
2016-09-13 23:03:01 +03:00
|
|
|
// 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
|
2016-09-05 22:11:03 +03:00
|
|
|
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
#ifndef _WIN32
|
|
|
|
if (eventTracingEnabled)
|
2016-09-05 22:11:03 +03:00
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
// 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()) {
|
|
|
|
while (infile >> timestamp >> thread_from >> thread_to) {
|
|
|
|
beginContextSwitch(thread_from, timestamp, thread_to, "", false);
|
|
|
|
endContextSwitch(thread_to, timestamp, false);
|
|
|
|
}
|
2016-09-05 22:11:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
// Calculate used memory total size and total blocks number
|
2016-08-28 21:06:23 +03:00
|
|
|
uint64_t usedMemorySize = 0;
|
|
|
|
uint32_t blocks_number = 0;
|
|
|
|
for (const auto& thread_storage : m_threads)
|
|
|
|
{
|
2016-09-04 14:48:35 +03:00
|
|
|
const auto& t = thread_storage.second;
|
|
|
|
usedMemorySize += t.blocks.usedMemorySize + t.sync.usedMemorySize;
|
|
|
|
blocks_number += static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
// Write CPU frequency to let GUI calculate real time value from CPU clocks
|
2016-09-04 14:48:35 +03:00
|
|
|
#ifdef _WIN32
|
2016-09-11 16:57:35 +03:00
|
|
|
_outputStream.write(CPU_FREQUENCY);
|
2016-09-04 14:48:35 +03:00
|
|
|
#else
|
2016-09-11 16:57:35 +03:00
|
|
|
_outputStream.write(0LL);
|
2016-09-04 14:48:35 +03:00
|
|
|
#endif
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
// 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);
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
// Write block descriptors
|
2016-09-09 00:09:47 +03:00
|
|
|
for (const auto descriptor : m_descriptors)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
if (descriptor == nullptr)
|
2016-09-17 11:10:25 +03:00
|
|
|
{
|
|
|
|
_outputStream.write<uint16_t>(0U);
|
2016-09-13 23:03:01 +03:00
|
|
|
continue;
|
2016-09-17 11:10:25 +03:00
|
|
|
}
|
2016-09-13 23:03:01 +03:00
|
|
|
|
2016-09-09 00:09:47 +03:00
|
|
|
const auto name_size = static_cast<uint16_t>(strlen(descriptor->name()) + 1);
|
|
|
|
const auto filename_size = static_cast<uint16_t>(strlen(descriptor->file()) + 1);
|
2016-08-28 23:40:23 +03:00
|
|
|
const auto size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size);
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
_outputStream.write(size);
|
|
|
|
_outputStream.write<profiler::BaseBlockDescriptor>(*descriptor);
|
|
|
|
_outputStream.write(name_size);
|
|
|
|
_outputStream.write(descriptor->name(), name_size);
|
|
|
|
_outputStream.write(descriptor->file(), filename_size);
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
// Write blocks and context switch events for each thread
|
2016-08-28 21:06:23 +03:00
|
|
|
for (auto& thread_storage : m_threads)
|
|
|
|
{
|
2016-09-04 14:48:35 +03:00
|
|
|
auto& t = thread_storage.second;
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
_outputStream.write(thread_storage.first);
|
2016-09-04 14:48:35 +03:00
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
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);
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
_outputStream.write(t.sync.closedList.size());
|
2016-09-15 23:30:36 +03:00
|
|
|
if (!t.sync.closedList.empty())
|
|
|
|
t.sync.closedList.serialize(_outputStream);
|
2016-09-04 14:48:35 +03:00
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
_outputStream.write(t.blocks.closedList.size());
|
2016-09-13 23:03:01 +03:00
|
|
|
if (!t.blocks.closedList.empty())
|
|
|
|
t.blocks.closedList.serialize(_outputStream);
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
t.clearClosed();
|
2016-09-13 23:03:01 +03:00
|
|
|
t.blocks.openedList.clear();
|
|
|
|
t.sync.openedList.clear();
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
// Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution)
|
|
|
|
for (auto& desc : m_descriptors)
|
2016-09-11 16:57:35 +03:00
|
|
|
{
|
2016-09-13 23:03:01 +03:00
|
|
|
if (desc == nullptr || !desc->m_expired)
|
|
|
|
continue;
|
|
|
|
|
2016-09-17 11:10:25 +03:00
|
|
|
m_usedMemorySize -= desc->m_size;
|
2016-09-13 23:03:01 +03:00
|
|
|
delete desc;
|
|
|
|
desc = nullptr;
|
2016-09-11 16:57:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//if (wasEnabled)
|
|
|
|
// ::profiler::setEnabled(true);
|
2016-09-04 14:48:35 +03:00
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
return blocks_number;
|
|
|
|
}
|
|
|
|
|
2016-09-11 16:57:35 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
const char* ProfileManager::registerThread(const char* name)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
2016-09-06 23:03:05 +03:00
|
|
|
if (THREAD_STORAGE == nullptr)
|
|
|
|
{
|
|
|
|
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!THREAD_STORAGE->named)
|
|
|
|
{
|
|
|
|
THREAD_STORAGE->named = true;
|
2016-09-13 23:03:01 +03:00
|
|
|
THREAD_STORAGE->name = name;
|
2016-09-06 23:03:05 +03:00
|
|
|
}
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-06 23:03:05 +03:00
|
|
|
return THREAD_STORAGE->name.c_str();
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
2016-09-13 23:03:01 +03:00
|
|
|
void ProfileManager::setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled)
|
|
|
|
{
|
|
|
|
guard_lock_t lock(m_storedSpin);
|
|
|
|
|
|
|
|
auto desc = m_descriptors[_id];
|
|
|
|
if (desc != nullptr)
|
|
|
|
{
|
|
|
|
lock.unlock();
|
|
|
|
|
|
|
|
*desc->m_pEnable = _enabled;
|
2016-09-17 15:37:16 +03:00
|
|
|
desc->m_enabled = _enabled; // TODO: possible concurrent access, atomic may be needed
|
2016-09-13 23:03:01 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-09-14 23:23:09 +03:00
|
|
|
#ifdef _WIN32
|
2016-09-13 23:03:01 +03:00
|
|
|
blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode());
|
|
|
|
m_blocksEnableStatus[key] = _enabled;
|
2016-09-14 23:23:09 +03:00
|
|
|
#else
|
|
|
|
m_blocksEnableStatus[_key] = _enabled;
|
|
|
|
#endif
|
2016-09-13 23:03:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|