2016-08-28 21:06:23 +03:00
|
|
|
#include "profile_manager.h"
|
|
|
|
#include "profiler/serialized_block.h"
|
2016-09-04 14:48:35 +03:00
|
|
|
#include "event_trace_win.h"
|
2016-08-28 21:06:23 +03:00
|
|
|
|
|
|
|
#include <thread>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
|
|
|
using namespace profiler;
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//auto& MANAGER = ProfileManager::instance();
|
|
|
|
#define MANAGER ProfileManager::instance()
|
2016-08-28 21:06:23 +03:00
|
|
|
|
|
|
|
extern "C"{
|
|
|
|
|
|
|
|
void PROFILER_API endBlock()
|
|
|
|
{
|
|
|
|
MANAGER.endBlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PROFILER_API setEnabled(bool isEnable)
|
|
|
|
{
|
|
|
|
MANAGER.setEnabled(isEnable);
|
2016-09-04 14:48:35 +03:00
|
|
|
if (isEnable)
|
|
|
|
EasyEventTracer::instance().enable(true);
|
|
|
|
else
|
|
|
|
EasyEventTracer::instance().disable();
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
2016-09-04 14:48:35 +03:00
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
void PROFILER_API beginBlock(Block& _block)
|
|
|
|
{
|
|
|
|
MANAGER.beginBlock(_block);
|
|
|
|
}
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
uint32_t PROFILER_API dumpBlocksToFile(const char* filename)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
|
|
|
return MANAGER.dumpBlocksToFile(filename);
|
|
|
|
}
|
|
|
|
|
2016-08-28 23:40:23 +03:00
|
|
|
void PROFILER_API setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
2016-08-28 23:40:23 +03:00
|
|
|
return MANAGER.setThreadName(name, filename, _funcname, line);
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
2016-09-04 14:48:35 +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;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
StaticBlockDescriptor::StaticBlockDescriptor(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
|
|
|
: m_id(MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color))
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2016-08-28 23:40:23 +03:00
|
|
|
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
void ThreadStorage::storeBlock(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 = blocks.alloc.allocate(size);
|
|
|
|
::new (static_cast<void*>(data)) SerializedBlock(block, name_length);
|
|
|
|
blocks.usedMemorySize += size;
|
|
|
|
blocks.closedList.emplace_back(reinterpret_cast<SerializedBlock*>(data));
|
|
|
|
}
|
|
|
|
|
|
|
|
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-04 14:48:35 +03:00
|
|
|
auto data = sync.alloc.allocate(size);
|
2016-08-28 21:06:23 +03:00
|
|
|
::new (static_cast<void*>(data)) SerializedBlock(block, name_length);
|
2016-09-04 14:48:35 +03:00
|
|
|
sync.usedMemorySize += size;
|
|
|
|
sync.closedList.emplace_back(reinterpret_cast<SerializedBlock*>(data));
|
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-04 14:48:35 +03:00
|
|
|
// #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
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
ProfileManager::ProfileManager()
|
|
|
|
{
|
2016-09-04 14:48:35 +03:00
|
|
|
// #ifdef _WIN32
|
|
|
|
// PREVIOUS_FILTER = SetUnhandledExceptionFilter(easyTopLevelExceptionFilter);
|
|
|
|
// #endif
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
ProfileManager::~ProfileManager()
|
|
|
|
{
|
|
|
|
//dumpBlocksToFile("test.prof");
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
auto& thread_storage = threadStorage(getCurrentThreadId());
|
|
|
|
|
|
|
|
if (!_block.isFinished())
|
2016-09-04 14:48:35 +03:00
|
|
|
thread_storage.blocks.openedList.emplace(_block);
|
2016-08-28 21:06:23 +03:00
|
|
|
else
|
2016-09-04 14:48:35 +03:00
|
|
|
thread_storage.storeBlock(_block);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileManager::_cswitchBeginBlock(profiler::timestamp_t _time, profiler::block_id_t _id, profiler::thread_id_t _thread_id)
|
|
|
|
{
|
|
|
|
auto thread_storage = _threadStorage(_thread_id);
|
|
|
|
if (thread_storage != nullptr)
|
|
|
|
thread_storage->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileManager::endBlock()
|
|
|
|
{
|
|
|
|
if (!m_isEnabled)
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto& thread_storage = threadStorage(getCurrentThreadId());
|
2016-09-04 14:48:35 +03:00
|
|
|
if (thread_storage.blocks.openedList.empty())
|
2016-08-28 21:06:23 +03:00
|
|
|
return;
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
Block& lastBlock = thread_storage.blocks.openedList.top();
|
2016-08-28 21:06:23 +03:00
|
|
|
if (!lastBlock.isFinished())
|
|
|
|
lastBlock.finish();
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
thread_storage.storeBlock(lastBlock);
|
|
|
|
thread_storage.blocks.openedList.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileManager::_cswitchEndBlock(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime)
|
|
|
|
{
|
|
|
|
auto thread_storage = _threadStorage(_thread_id);
|
|
|
|
if (thread_storage == nullptr || thread_storage->sync.openedList.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
Block& lastBlock = thread_storage->sync.openedList.top();
|
|
|
|
lastBlock.finish(_endtime);
|
|
|
|
|
|
|
|
thread_storage->storeCSwitch(lastBlock);
|
|
|
|
thread_storage->sync.openedList.pop();
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void ProfileManager::setEnabled(bool isEnable)
|
|
|
|
{
|
|
|
|
m_isEnabled = isEnable;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
class FileWriter final
|
|
|
|
{
|
|
|
|
std::ofstream m_file;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
FileWriter(const char* _filename) : m_file(_filename, std::fstream::binary) { }
|
|
|
|
|
|
|
|
template <typename T> void write(const char* _data, T _size) {
|
|
|
|
m_file.write(_data, _size);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T> void write(const T& _data) {
|
|
|
|
m_file.write((const char*)&_data, sizeof(T));
|
|
|
|
}
|
2016-09-04 14:48:35 +03:00
|
|
|
|
|
|
|
void writeBlock(const profiler::SerializedBlock* _block)
|
|
|
|
{
|
|
|
|
auto sz = static_cast<uint16_t>(sizeof(BaseBlockData) + strlen(_block->name()) + 1);
|
|
|
|
write(sz);
|
|
|
|
write(_block->data(), sz);
|
|
|
|
}
|
2016-08-28 21:06:23 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
#define STORE_CSWITCHES_SEPARATELY
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
|
|
|
|
{
|
2016-09-04 14:48:35 +03:00
|
|
|
const bool wasEnabled = m_isEnabled;
|
|
|
|
if (wasEnabled)
|
|
|
|
::profiler::setEnabled(false);
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
FileWriter of(filename);
|
|
|
|
|
|
|
|
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-04 14:48:35 +03:00
|
|
|
#ifdef _WIN32
|
|
|
|
of.write(CPU_FREQUENCY);
|
|
|
|
#else
|
|
|
|
of.write(0LL);
|
|
|
|
#endif
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
of.write(blocks_number);
|
|
|
|
of.write(usedMemorySize);
|
|
|
|
of.write(static_cast<uint32_t>(m_descriptors.size()));
|
|
|
|
of.write(m_usedMemorySize);
|
|
|
|
|
|
|
|
for (const auto& descriptor : m_descriptors)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
|
|
|
of.write(size);
|
|
|
|
of.write<profiler::BaseBlockDescriptor>(descriptor);
|
|
|
|
of.write(name_size);
|
|
|
|
of.write(descriptor.name(), name_size);
|
|
|
|
of.write(descriptor.file(), filename_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto& thread_storage : m_threads)
|
|
|
|
{
|
2016-09-04 14:48:35 +03:00
|
|
|
auto& t = thread_storage.second;
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
of.write(thread_storage.first);
|
2016-09-04 14:48:35 +03:00
|
|
|
#ifdef STORE_CSWITCHES_SEPARATELY
|
|
|
|
of.write(static_cast<uint32_t>(t.blocks.closedList.size()));
|
|
|
|
#else
|
|
|
|
of.write(static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(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);
|
|
|
|
}
|
2016-08-28 21:06:23 +03:00
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
#ifdef STORE_CSWITCHES_SEPARATELY
|
|
|
|
of.write(static_cast<uint32_t>(t.sync.closedList.size()));
|
|
|
|
for (auto b : t.sync.closedList)
|
|
|
|
{
|
|
|
|
#else
|
|
|
|
for (; i < t.sync.closedList.size(); ++i)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
2016-09-04 14:48:35 +03:00
|
|
|
auto b = t.sync.closedList[i];
|
|
|
|
#endif
|
|
|
|
|
|
|
|
of.writeBlock(b);
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
t.clearClosed();
|
2016-08-28 21:06:23 +03:00
|
|
|
}
|
|
|
|
|
2016-09-04 14:48:35 +03:00
|
|
|
// if (wasEnabled)
|
|
|
|
// ::profiler::setEnabled(true);
|
|
|
|
|
2016-08-28 21:06:23 +03:00
|
|
|
return blocks_number;
|
|
|
|
}
|
|
|
|
|
2016-08-28 23:40:23 +03:00
|
|
|
void ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
2016-08-28 21:06:23 +03:00
|
|
|
{
|
|
|
|
auto& thread_storage = threadStorage(getCurrentThreadId());
|
|
|
|
if (thread_storage.named)
|
|
|
|
return;
|
|
|
|
|
2016-08-28 23:40:23 +03:00
|
|
|
const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Random);
|
2016-09-04 14:48:35 +03:00
|
|
|
thread_storage.storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name));
|
2016-08-28 21:06:23 +03:00
|
|
|
thread_storage.named = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|