0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-26 16:11:02 +08:00

(Core) Monitoring threads, removing threads without blocks, events and context switches;

* (Core) Context switch events also register threads for current process;
* (Core) Writing process id into output file (old files still could be opened);
This commit is contained in:
Victor Zarubkin 2016-12-12 22:26:32 +03:00
parent 039e0a6efa
commit 29f81d1244
7 changed files with 151 additions and 70 deletions

View File

@ -1,4 +1,4 @@
# easy_profiler [![version](https://img.shields.io/badge/version-1.0.0%20beta-009688.svg)](https://github.com/yse/easy_profiler/releases)
# easy_profiler [![version](https://img.shields.io/badge/version-1.0.1%20beta-009688.svg)](https://github.com/yse/easy_profiler/releases)
[![Build Status](https://travis-ci.org/yse/easy_profiler.svg?branch=develop)](https://travis-ci.org/yse/easy_profiler)

View File

@ -129,6 +129,7 @@ namespace profiler {
if (time > TRACING_END_TIME.load(::std::memory_order_acquire))
return;
processid_t pid = 0;
const char* process_name = "";
// Trying to get target process name and id
@ -138,7 +139,7 @@ namespace profiler {
auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _contextSwitchEvent->NewThreadId);
if (hThread != nullptr)
{
auto pid = GetProcessIdOfThread(hThread);
pid = GetProcessIdOfThread(hThread);
auto pinfo = &PROCESS_INFO_TABLE[pid];
if (pinfo->valid == 0)
@ -204,7 +205,7 @@ namespace profiler {
}
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, _contextSwitchEvent->NewThreadId, process_name);
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, pid, time);
}
//////////////////////////////////////////////////////////////////////////

View File

@ -190,15 +190,16 @@ namespace profiler {
public:
BlocksTree::children_t children; ///< List of children indexes
BlocksTree::children_t sync; ///< List of context-switch events
BlocksTree::children_t events; ///< List of events indexes
std::string thread_name; ///< Name of this thread
::profiler::timestamp_t active_time; ///< Active time of this thread (sum of all children duration)
::profiler::thread_id_t thread_id; ///< System Id of this thread
uint16_t depth; ///< Maximum stack depth (number of levels)
BlocksTree::children_t children; ///< List of children indexes
BlocksTree::children_t sync; ///< List of context-switch events
BlocksTree::children_t events; ///< List of events indexes
std::string thread_name; ///< Name of this thread
::profiler::timestamp_t active_time; ///< Active time of this thread (sum of all children duration)
::profiler::thread_id_t thread_id; ///< System Id of this thread
::profiler::block_index_t blocks_number; ///< Total blocks number including their children
uint16_t depth; ///< Maximum stack depth (number of levels)
BlocksTreeRoot() : active_time(0), thread_id(0), depth(0)
BlocksTreeRoot() : active_time(0), thread_id(0), blocks_number(0), depth(0)
{
}
@ -209,6 +210,7 @@ namespace profiler {
, thread_name(::std::move(that.thread_name))
, active_time(that.active_time)
, thread_id(that.thread_id)
, blocks_number(that.blocks_number)
, depth(that.depth)
{
}
@ -221,6 +223,7 @@ namespace profiler {
thread_name = ::std::move(that.thread_name);
active_time = that.active_time;
thread_id = that.thread_id;
blocks_number = that.blocks_number;
depth = that.depth;
return *this;
}

View File

@ -47,9 +47,8 @@
#include "event_trace_win.h"
#include "current_time.h"
#ifndef _WIN32
#include <signal.h>
#ifdef min
#undef min
#endif
//////////////////////////////////////////////////////////////////////////
@ -93,20 +92,34 @@ EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
//////////////////////////////////////////////////////////////////////////
#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);
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);
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
//////////////////////////////////////////////////////////////////////////
@ -314,14 +327,13 @@ public:
//////////////////////////////////////////////////////////////////////////
ThreadStorage::ThreadStorage() : id(getCurrentThreadId()), allowChildren(true), named(false)
ThreadStorage::ThreadStorage() : id(getCurrentThreadId()), allowChildren(true), named(false), guarded(false)
#ifndef _WIN32
, pthread_id(pthread_self())
#endif
{
expired = ATOMIC_VAR_INIT(false);
expired = ATOMIC_VAR_INIT(0);
}
void ThreadStorage::storeBlock(const profiler::Block& block)
@ -388,8 +400,9 @@ ThreadGuard::~ThreadGuard()
#ifndef EASY_PROFILER_API_DISABLED
if (m_id != 0 && THREAD_STORAGE != nullptr && THREAD_STORAGE->id == m_id)
{
EASY_EVENT("ThreadFinished", profiler::colors::Dark);
THREAD_STORAGE->expired.store(true, std::memory_order_release);
bool isMarked = false;
EASY_EVENT_RES(isMarked, "ThreadFinished", profiler::colors::Dark, ::profiler::FORCE_ON);
THREAD_STORAGE->expired.store(isMarked ? 2 : 1, std::memory_order_release);
THREAD_STORAGE = nullptr;
}
#endif
@ -397,7 +410,15 @@ ThreadGuard::~ThreadGuard()
//////////////////////////////////////////////////////////////////////////
ProfileManager::ProfileManager()
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_isEnabled = ATOMIC_VAR_INIT(false);
m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_EVENT_TRACING_ENABLED);
@ -422,6 +443,8 @@ class ProfileManagerInstance {
} PROFILE_MANAGER;
#endif
//////////////////////////////////////////////////////////////////////////
ProfileManager& ProfileManager::instance()
{
#ifndef EASY_MAGIC_STATIC_CPP11
@ -434,9 +457,10 @@ ProfileManager& ProfileManager::instance()
#endif
}
ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id)
//////////////////////////////////////////////////////////////////////////
ThreadStorage& ProfileManager::_threadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
return m_threads[_thread_id];
}
@ -446,6 +470,8 @@ ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_
return it != m_threads.end() ? &it->second : nullptr;
}
//////////////////////////////////////////////////////////////////////////
const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _defaultStatus,
const char* _autogenUniqueId,
const char* _name,
@ -469,17 +495,19 @@ const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _d
return desc;
}
void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
//////////////////////////////////////////////////////////////////////////
bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
{
if (!m_isEnabled.load(std::memory_order_acquire) || !(_desc->m_status & profiler::ON))
return;
return false;
if (THREAD_STORAGE == nullptr)
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
#if EASY_ENABLE_BLOCK_STATUS != 0
if (!THREAD_STORAGE->allowChildren)
return;
if (!THREAD_STORAGE->allowChildren && !(_desc->m_status & FORCE_ON_FLAG))
return false;
#endif
profiler::Block b(_desc, _runtimeName);
@ -487,8 +515,12 @@ void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, cons
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))
@ -498,7 +530,7 @@ void ProfileManager::storeBlockForce(const profiler::BaseBlockDescriptor* _desc,
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
#if EASY_ENABLE_BLOCK_STATUS != 0
if (!THREAD_STORAGE->allowChildren)
if (!THREAD_STORAGE->allowChildren && !(_desc->m_status & FORCE_ON_FLAG))
return;
#endif
@ -519,7 +551,7 @@ void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
#if EASY_ENABLE_BLOCK_STATUS != 0
if (!THREAD_STORAGE->allowChildren)
if (!THREAD_STORAGE->allowChildren && !(_desc->m_status & FORCE_ON_FLAG))
return;
#endif
@ -529,6 +561,15 @@ void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc
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 (!m_isEnabled.load(std::memory_order_acquire))
@ -569,16 +610,7 @@ void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profil
ts->sync.openedList.emplace(_time, _target_thread_id, _target_process);
}
void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin)
{
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
if (ts != nullptr)
{
profiler::Block b(_time, _target_thread_id, "");
b.finish(_time);
ts->storeCSwitch(b);
}
}
//////////////////////////////////////////////////////////////////////////
void ProfileManager::endBlock()
{
@ -607,9 +639,16 @@ void ProfileManager::endBlock()
#endif
}
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin)
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, processid_t _process_id, profiler::timestamp_t _endtime, bool _lockSpin)
{
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
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;
@ -620,6 +659,8 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler
ts->sync.openedList.pop();
}
//////////////////////////////////////////////////////////////////////////
void ProfileManager::setEnabled(bool isEnable, bool _setTime)
{
auto time = getCurrentTime();
@ -661,10 +702,14 @@ void ProfileManager::setEventTracingEnabled(bool _isEnable)
//////////////////////////////////////////////////////////////////////////
bool ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
uint8_t ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
{
if (_registeredThread.expired.load(std::memory_order_acquire))
return true;
const uint8_t val = _registeredThread.expired.load(std::memory_order_acquire);
if (val != 0)
return val;
if (_registeredThread.guarded)
return 0;
#ifdef _WIN32
@ -678,19 +723,20 @@ bool ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
_registeredThread.expired.store(true, std::memory_order_release);
if (hThread != nullptr)
CloseHandle(hThread);
return true;
return 1;
}
if (hThread != nullptr)
CloseHandle(hThread);
return false;
return 0;
#else
return false;//pthread_kill(_registeredThread.pthread_id, 0) != 0;
return 0;//pthread_kill(_registeredThread.pthread_id, 0) != 0;
#endif
}
//////////////////////////////////////////////////////////////////////////
@ -719,6 +765,8 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
std::this_thread::sleep_for(std::chrono::milliseconds(20));
// 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)
@ -731,9 +779,10 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
std::ifstream infile(m_csInfoFilename.c_str());
if(infile.is_open()) {
std::string next_task_name;
while (infile >> timestamp >> thread_from >> thread_to >> 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, timestamp, false);
endContextSwitch(thread_to, (processid_t)process_to, timestamp, false);
}
}
}
@ -745,16 +794,20 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
for (auto it = m_threads.begin(), end = m_threads.end(); it != end;)
{
auto& t = it->second;
const uint32_t num = static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
uint32_t num = static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
const bool expired = checkThreadExpired(t) || t.expired.load(std::memory_order_acquire);
if (expired && num == 0) {
// Thread has been finished and contains no profiled information.
// Remove it now.
const uint8_t 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;
@ -763,6 +816,7 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
// 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
@ -847,7 +901,7 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
t.blocks.openedList.clear();
t.sync.openedList.clear();
if (t.expired.load(std::memory_order_acquire))
if (t.expired.load(std::memory_order_acquire) != 0)
m_threads.erase(it++); // Remove expired thread after writing all profiled information
else
++it;
@ -875,6 +929,7 @@ const char* ProfileManager::registerThread(const char* name, ThreadGuard& thread
if (THREAD_STORAGE == nullptr)
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
THREAD_STORAGE->guarded = true;
if (!THREAD_STORAGE->named) {
THREAD_STORAGE->named = true;
THREAD_STORAGE->name = name;

View File

@ -320,9 +320,10 @@ struct ThreadStorage
#endif
const profiler::thread_id_t id;
std::atomic_bool expired;
std::atomic<uint8_t> expired;
bool allowChildren;
bool named;
bool guarded;
void storeBlock(const profiler::Block& _block);
void storeCSwitch(const profiler::Block& _block);
@ -333,6 +334,8 @@ struct ThreadStorage
//////////////////////////////////////////////////////////////////////////
typedef uint32_t processid_t;
class BlockDescriptor;
class ProfileManager
@ -355,12 +358,14 @@ class ProfileManager
typedef std::unordered_map<profiler::hashed_stdstring, profiler::block_id_t> descriptors_map_t;
#endif
const processid_t m_processId;
map_of_threads_stacks m_threads;
block_descriptors_t m_descriptors;
descriptors_map_t m_descriptorsMap;
uint64_t m_usedMemorySize = 0;
profiler::timestamp_t m_beginTime = 0;
profiler::timestamp_t m_endTime = 0;
uint64_t m_usedMemorySize;
profiler::timestamp_t m_beginTime;
profiler::timestamp_t m_endTime;
profiler::spin_lock m_spin;
profiler::spin_lock m_storedSpin;
std::atomic_bool m_isEnabled;
@ -392,7 +397,7 @@ public:
profiler::block_type_t _block_type,
profiler::color_t _color);
void storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName);
bool storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName);
void beginBlock(profiler::Block& _block);
void endBlock();
void setEnabled(bool isEnable, bool _setTime = true);
@ -412,22 +417,28 @@ public:
}
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin = true);
void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin = true);
void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin = true);
void endContextSwitch(profiler::thread_id_t _thread_id, processid_t _process_id, profiler::timestamp_t _endtime, bool _lockSpin = true);
void startListen(uint16_t _port);
void stopListen();
private:
bool checkThreadExpired(ThreadStorage& _registeredThread);
uint8_t checkThreadExpired(ThreadStorage& _registeredThread);
void storeBlockForce(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t& _timestamp);
void storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp);
void storeBlockForce2(ThreadStorage& _registeredThread, const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp);
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id);
ThreadStorage& _threadStorage(profiler::thread_id_t _thread_id);
ThreadStorage* _findThreadStorage(profiler::thread_id_t _thread_id);
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
inline ThreadStorage& threadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
return _threadStorage(_thread_id);
}
inline ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
return _findThreadStorage(_thread_id);

View File

@ -67,14 +67,18 @@
//////////////////////////////////////////////////////////////////////////
typedef uint32_t processid_t;
extern const uint32_t PROFILER_SIGNATURE;
extern const uint32_t EASY_CURRENT_VERSION;
# 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))
const uint32_t EASY_V_100 = EASY_VERSION_INT(1, 0, 0);
const uint32_t COMPATIBLE_VERSIONS[] = {
EASY_CURRENT_VERSION,
EASY_V_100,
EASY_VERSION_INT(0, 1, 0)
};
// WARNING: Modify isCompatibleVersion(uint32_t _version) if COMPATIBLE_VERSIONS_NUM == 0
const uint16_t COMPATIBLE_VERSIONS_NUM = sizeof(COMPATIBLE_VERSIONS) / sizeof(uint32_t);
# undef EASY_VERSION_INT
@ -112,7 +116,9 @@ bool isCompatibleVersion(uint32_t _version)
{
if (_version == EASY_CURRENT_VERSION)
return true;
return COMPATIBLE_VERSIONS_NUM > 1 && ::std::binary_search(COMPATIBLE_VERSIONS + 1, COMPATIBLE_VERSIONS + COMPATIBLE_VERSIONS_NUM, _version);
return ::std::binary_search(COMPATIBLE_VERSIONS, COMPATIBLE_VERSIONS + COMPATIBLE_VERSIONS_NUM,
_version, [](uint32_t _a, uint32_t _b){ return _a > _b; });
}
inline void write(::std::stringstream& _stream, const char* _value, size_t _size)
@ -369,6 +375,10 @@ extern "C" {
return 0;
}
processid_t pid = 0;
if (version > EASY_V_100)
inFile.read((char*)&pid, sizeof(processid_t));
int64_t file_cpu_frequency = 0LL;
inFile.read((char*)&file_cpu_frequency, sizeof(int64_t));
uint64_t cpu_frequency = file_cpu_frequency;
@ -669,6 +679,7 @@ extern "C" {
}
}
++root.blocks_number;
root.children.emplace_back(block_index);// ::std::move(tree));
if (desc->type() == ::profiler::BLOCK_TYPE_EVENT)
root.events.emplace_back(block_index);

View File

@ -1 +1 @@
1.0.0
1.0.1