0
0
mirror of https://github.com/yse/easy_profiler.git synced 2025-01-14 00:27:55 +08:00

Trying to fix problem with c++11 magic statics (there is no support for visual studio 2013 and earlier, gcc < 4.3 and clang < 2.9);

Wrapped "final" keyword for different compilers support;
Block descriptors now stored in unordered_map to make it easy to control theirs visibility level and to make it safe to unload dll/so during application execution.
This commit is contained in:
Victor Zarubkin 2016-09-22 23:06:43 +03:00
parent 79e503983c
commit 30de452113
19 changed files with 485 additions and 365 deletions

View File

@ -0,0 +1,130 @@
/************************************************************************
* file name : easy_compiler_support.h
* ----------------- :
* creation time : 2016/09/22
* authors : Victor Zarubkin, Sergey Yagovtsev
* emails : v.s.zarubkin@gmail.com, yse.sey@gmail.com
* ----------------- :
* description : This file contains auxiliary profiler macros for different compiler support.
* ----------------- :
* 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/>.
************************************************************************/
#ifndef EASY_PROFILER__COMPILER_SUPPORT__H_______
#define EASY_PROFILER__COMPILER_SUPPORT__H_______
#include <cstddef>
//#define EASY_CODE_WRAP(Code) Code
#ifdef _WIN32
# define __func__ __FUNCTION__
# ifdef _BUILD_PROFILER
# define PROFILER_API __declspec(dllexport)
# else
# define PROFILER_API __declspec(dllimport)
# endif
#endif
#if defined (_MSC_VER)
//////////////////////////////////////////////////////////////////////////
// Visual Studio
# if _MSC_VER <= 1800
// There is no support for C++11 thread_local keyword prior to Visual Studio 2015. Use __declspec(thread) instead.
// There is also no support for C++11 magic statics feature :( So it becomes slightly harder to initialize static vars - additional "if" for each profiler block.
# define EASY_THREAD_LOCAL __declspec(thread)
# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer)\
__declspec(thread) static VarType VarName = 0;\
if (!VarName)\
VarName = VarInitializer
# endif
#elif defined (__clang__)
//////////////////////////////////////////////////////////////////////////
// Clang Compiler
# if (__clang_major__ == 3 && __clang_minor__ < 3) || (__clang_major__ < 3)
// There is no support for C++11 thread_local keyword prior to clang 3.3. Use __thread instead.
# define EASY_THREAD_LOCAL __thread
# endif
# if (__clang_major__ == 2 && __clang_minor__ < 9) || (__clang_major__ < 2)
// There is no support for C++11 magic statics feature prior to clang 2.9. It becomes slightly harder to initialize static vars - additional "if" for each profiler block.
# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer)\
EASY_THREAD_LOCAL static VarType VarName = 0;\
if (!VarName)\
VarName = VarInitializer
// There is no support for C++11 final keyword prior to clang 2.9
# define EASY_FINAL
# endif
#elif defined(__GNUC__)
//////////////////////////////////////////////////////////////////////////
// GNU Compiler
# if (__GNUC__ == 4 && __GNUC_MINOR__ < 8) || (__GNUC__ < 4)
// There is no support for C++11 thread_local keyword prior to gcc 4.8. Use __thread instead.
# define EASY_THREAD_LOCAL __thread
# endif
# if (__GNUC__ == 4 && __GNUC_MINOR__ < 3) || (__GNUC__ < 4)
// There is no support for C++11 magic statics feature prior to gcc 4.3. It becomes slightly harder to initialize static vars - additional "if" for each profiler block.
# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer)\
EASY_THREAD_LOCAL static VarType VarName = 0;\
if (!VarName)\
VarName = VarInitializer
# endif
# if (__GNUC__ == 4 && __GNUC_MINOR__ < 7) || (__GNUC__ < 4)
// There is no support for C++11 final keyword prior to gcc 4.7
# define EASY_FINAL
# endif
#endif
// END // TODO: Add other compilers support
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Default values
#ifndef EASY_THREAD_LOCAL
# define EASY_THREAD_LOCAL thread_local
# define EASY_THREAD_LOCAL_CPP11
#endif
#ifndef EASY_LOCAL_STATIC_PTR
# define EASY_LOCAL_STATIC_PTR(VarType, VarName, VarInitializer) static VarType VarName = VarInitializer
# define EASY_MAGIC_STATIC_CPP11
#endif
#ifndef EASY_FINAL
# define EASY_FINAL final
#endif
#ifndef PROFILER_API
# define PROFILER_API
#endif
//////////////////////////////////////////////////////////////////////////
#endif // EASY_PROFILER__COMPILER_SUPPORT__H_______

View File

@ -68,7 +68,7 @@ Block will be automatically completed by destructor.
\ingroup profiler
*/
# define EASY_BLOCK(name, ...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
@ -98,7 +98,7 @@ Name of the block automatically created with function name.
\ingroup profiler
*/
# define EASY_FUNCTION(...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
EASY_UNIQUE_LINE_ID, __func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
@ -138,8 +138,8 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
\ingroup profiler
*/
# define EASY_EVENT(name, ...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(\
::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\
EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), ::profiler::registerDescription(\
::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__)));\
::profiler::storeEvent(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));
@ -162,9 +162,9 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
\ingroup profiler
*/
# define EASY_THREAD(name)\
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = 0;\
::profiler::ThreadGuard EASY_TOKEN_CONCATENATE(unique_profiler_thread_guard, __LINE__);\
if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\
if (!EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__))\
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::registerThread(name,\
EASY_TOKEN_CONCATENATE(unique_profiler_thread_guard, __LINE__));
@ -385,35 +385,7 @@ namespace profiler {
//***********************************************
class PROFILER_API BlockDescriptor final : public BaseBlockDescriptor
{
friend ::ProfileManager;
const char* 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
const char* m_filename; ///< Source file name where this block is declared
EasyBlockStatus* m_pStatus; ///< Pointer to the enable flag in unordered_map
uint16_t m_size; ///< Used memory size
bool m_expired; ///< Is this descriptor expired
BlockDescriptor(EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
public:
BlockDescriptor(block_id_t _id, EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
inline const char* name() const {
return m_name;
}
inline const char* file() const {
return m_filename;
}
}; // END of class BlockDescriptor.
//***********************************************
class PROFILER_API Block final : public BaseBlockData
class PROFILER_API Block EASY_FINAL : public BaseBlockData
{
friend ::ProfileManager;
friend ::ThreadStorage;
@ -434,7 +406,7 @@ namespace profiler {
public:
Block(Block&& that);
Block(const BaseBlockDescriptor& _desc, const char* _runtimeName);
Block(const BaseBlockDescriptor* _desc, const char* _runtimeName);
Block(timestamp_t _begin_time, block_id_t _id, const char* _runtimeName);
~Block();
@ -449,26 +421,7 @@ namespace profiler {
//***********************************************
class PROFILER_API BlockDescRef final
{
const BaseBlockDescriptor& m_desc;
public:
explicit BlockDescRef(const BaseBlockDescriptor& _desc) : m_desc(_desc) { }
explicit BlockDescRef(const BaseBlockDescriptor* _desc) : m_desc(*_desc) { }
inline operator const BaseBlockDescriptor& () const { return m_desc; }
~BlockDescRef();
private:
BlockDescRef() = delete;
BlockDescRef(const BlockDescRef&) = delete;
BlockDescRef& operator = (const BlockDescRef&) = delete;
}; // END of class BlockDescRef.
class PROFILER_API ThreadGuard final {
class PROFILER_API ThreadGuard EASY_FINAL {
friend ::ProfileManager;
thread_id_t m_id = 0;
public:
@ -499,7 +452,7 @@ namespace profiler {
\ingroup profiler
*/
PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName);
PROFILER_API void storeEvent(const BaseBlockDescriptor* _desc, const char* _runtimeName);
/** Begins block.

View File

@ -32,52 +32,11 @@
#define EASY_PROFILER__AUX__H_______
#include <stdint.h>
#include <cstddef>
#include "profiler/easy_compiler_support.h"
#include "profiler/profiler_colors.h"
//////////////////////////////////////////////////////////////////////////
#ifdef _WIN32
# define __func__ __FUNCTION__
# if defined(_MSC_VER) && _MSC_VER <= 1800
// There is no support for C++11 thread_local keyword prior to Visual Studio 2015. Use __declspec(thread) instead.
# define EASY_THREAD_LOCAL __declspec(thread)
# endif
# ifdef _BUILD_PROFILER
# define PROFILER_API __declspec(dllexport)
# else
# define PROFILER_API __declspec(dllimport)
# endif
#elif defined (__clang__)
# if (__clang_major__ == 3 && __clang_minor__ < 3) || (__clang_major__ < 3)
# define EASY_THREAD_LOCAL __thread
# endif
#elif defined(__GNUC__)
# if (__GNUC__ == 4 && __GNUC_MINOR__ < 8) || (__GNUC__ < 4)
// There is no support for C++11 thread_local keyword prior to gcc 4.8. Use __thread instead.
# define EASY_THREAD_LOCAL __thread
# endif
#endif
// TODO: Check thread_local support for clang earlier than 3.3
#ifndef EASY_THREAD_LOCAL
# define EASY_THREAD_LOCAL thread_local
# define EASY_THREAD_LOCAL_CPP11
#endif
#ifndef PROFILER_API
# define PROFILER_API
#endif
//////////////////////////////////////////////////////////////////////////
namespace profiler {
enum EasyBlockStatus : uint8_t {
@ -89,7 +48,7 @@ namespace profiler {
FORCE_ON_WITHOUT_CHILDREN = FORCE_ON | OFF_RECURSIVE, ///< The block is ALWAYS ON but all of it's children are OFF.
};
struct passthrough_hash final {
struct passthrough_hash EASY_FINAL {
template <class T> inline size_t operator () (T _value) const {
return static_cast<size_t>(_value);
}
@ -112,12 +71,12 @@ namespace profiler {
namespace profiler {
template <const bool IS_REF> struct NameSwitch final {
template <const bool IS_REF> struct NameSwitch EASY_FINAL {
static const char* runtime_name(const char* name) { return name; }
static const char* compiletime_name(const char*, const char* autoGeneratedName) { return autoGeneratedName; }
};
template <> struct NameSwitch<true> final {
template <> struct NameSwitch<true> EASY_FINAL {
static const char* runtime_name(const char*) { return ""; }
static const char* compiletime_name(const char* name, const char*) { return name; }
};

View File

@ -36,7 +36,7 @@ namespace profiler {
typedef uint32_t block_index_t;
#pragma pack(push, 1)
struct BlockStatistics final
struct BlockStatistics EASY_FINAL
{
::profiler::timestamp_t total_duration; ///< Summary duration of all block calls
::profiler::timestamp_t min_duration; ///< Cached block->duration() value. TODO: Remove this if memory consumption will be too high
@ -71,7 +71,7 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////////
class BlocksTree final
class BlocksTree EASY_FINAL
{
typedef BlocksTree This;
@ -80,12 +80,12 @@ namespace profiler {
typedef ::std::vector<This> blocks_t;
typedef ::std::vector<::profiler::block_index_t> children_t;
children_t children; ///< List of children blocks. May be empty.
::profiler::SerializedBlock* node; ///< Pointer to serilized data (type, name, begin, end etc.)
::profiler::BlockStatistics* per_parent_stats; ///< Pointer to statistics for this block within the parent (may be nullptr for top-level blocks)
::profiler::BlockStatistics* per_frame_stats; ///< Pointer to statistics for this block within the frame (may be nullptr for top-level blocks)
::profiler::BlockStatistics* per_thread_stats; ///< Pointer to statistics for this block within the bounds of all frames per current thread
uint16_t depth; ///< Maximum number of sublevels (maximum children depth)
children_t children; ///< List of children blocks. May be empty.
::profiler::SerializedBlock* node; ///< Pointer to serilized data (type, name, begin, end etc.)
::profiler::BlockStatistics* per_parent_stats; ///< Pointer to statistics for this block within the parent (may be nullptr for top-level blocks)
::profiler::BlockStatistics* per_frame_stats; ///< Pointer to statistics for this block within the frame (may be nullptr for top-level blocks)
::profiler::BlockStatistics* per_thread_stats; ///< Pointer to statistics for this block within the bounds of all frames per current thread
uint16_t depth; ///< Maximum number of sublevels (maximum children depth)
BlocksTree()
: node(nullptr)
@ -170,7 +170,7 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////////
class BlocksTreeRoot final
class BlocksTreeRoot EASY_FINAL
{
typedef BlocksTreeRoot This;
@ -235,7 +235,7 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////////
class PROFILER_API SerializedData final
class PROFILER_API SerializedData EASY_FINAL
{
char* m_data;
size_t m_size;

View File

@ -25,7 +25,7 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////////
class PROFILER_API SerializedBlock final : public BaseBlockData
class PROFILER_API SerializedBlock EASY_FINAL : public BaseBlockData
{
friend ::ProfileManager;
friend ::ThreadStorage;
@ -48,7 +48,7 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////////
#pragma pack(push, 1)
class PROFILER_API SerializedBlockDescriptor final : public BaseBlockDescriptor
class PROFILER_API SerializedBlockDescriptor EASY_FINAL : public BaseBlockDescriptor
{
uint16_t m_nameLength;

View File

@ -148,7 +148,7 @@ inline ::profiler::color_t textColorForRgb(::profiler::color_t _color)
//////////////////////////////////////////////////////////////////////////
#pragma pack(push, 1)
struct EasyBlockItem final
struct EasyBlockItem Q_DECL_FINAL
{
//const ::profiler::BlocksTree* block; ///< Pointer to profiler block
qreal x; ///< x coordinate of the item (this is made qreal=double to avoid mistakes on very wide scene)
@ -170,7 +170,7 @@ struct EasyBlockItem final
}; // END of struct EasyBlockItem.
struct EasyBlock final
struct EasyBlock Q_DECL_FINAL
{
::profiler::BlocksTree tree;
uint32_t tree_item;
@ -202,7 +202,7 @@ typedef ::std::vector<EasyBlock> EasyBlocks;
//////////////////////////////////////////////////////////////////////////
struct EasySelectedBlock final
struct EasySelectedBlock Q_DECL_FINAL
{
const ::profiler::BlocksTreeRoot* root;
::profiler::block_index_t tree;

View File

@ -86,7 +86,7 @@ namespace profiler_gui {
//////////////////////////////////////////////////////////////////////////
struct EasyGlobals final
struct EasyGlobals Q_DECL_FINAL
{
static EasyGlobals& instance();

View File

@ -36,7 +36,7 @@
namespace profiler_gui {
class EasyGlobalSignals final : public QObject
class EasyGlobalSignals Q_DECL_FINAL : public QObject
{
Q_OBJECT

View File

@ -58,7 +58,7 @@ class QDockWidget;
//////////////////////////////////////////////////////////////////////////
class EasyFileReader final
class EasyFileReader Q_DECL_FINAL
{
::profiler::SerializedData m_serializedBlocks; ///<
::profiler::SerializedData m_serializedDescriptors; ///<

View File

@ -49,7 +49,7 @@ typedef ::std::unordered_map<::profiler::thread_id_t, EasyTreeWidgetItem*, ::pro
//////////////////////////////////////////////////////////////////////////
class EasyTreeWidgetLoader final
class EasyTreeWidgetLoader Q_DECL_FINAL
{
ThreadedItems m_topLevelItems; ///<
Items m_items; ///<
@ -82,7 +82,7 @@ public:
//////////////////////////////////////////////////////////////////////////
template <class T>
struct FillTreeClass final
struct FillTreeClass Q_DECL_FINAL
{
static void setTreeInternal1(T& _safelocker, Items& _items, ThreadedItems& _topLevelItems, ::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _colorizeRows);
static void setTreeInternal2(T& _safelocker, Items& _items, ThreadedItems& _topLevelItems, const ::profiler::timestamp_t& _beginTime, const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _colorizeRows);
@ -91,7 +91,7 @@ struct FillTreeClass final
//////////////////////////////////////////////////////////////////////////
struct StubLocker final
struct StubLocker Q_DECL_FINAL
{
void setDone() {}
bool interrupted() const { return false; }

View File

@ -44,10 +44,10 @@ Block::Block(Block&& that)
m_end = that.m_end;
}
Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName)
: BaseBlockData(1ULL, _descriptor.id())
Block::Block(const BaseBlockDescriptor* _descriptor, const char* _runtimeName)
: BaseBlockData(1ULL, _descriptor->id())
, m_name(_runtimeName)
, m_status(_descriptor.status())
, m_status(_descriptor->status())
{
}

View File

@ -54,9 +54,12 @@
namespace profiler {
const decltype(EVENT_DESCRIPTOR::Opcode) SWITCH_CONTEXT_OPCODE = 36;
const int RAW_TIMESTAMP_TIME_TYPE = 1;
//////////////////////////////////////////////////////////////////////////
struct ProcessInfo final {
struct ProcessInfo {
std::string name;
uint32_t id = 0;
int8_t valid = 0;
@ -67,7 +70,7 @@ namespace profiler {
// CSwitch class
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx
// EventType = 36
struct CSwitch final
struct CSwitch
{
uint32_t NewThreadId;
uint32_t OldThreadId;
@ -89,14 +92,13 @@ namespace profiler {
typedef ::std::unordered_map<uint32_t, ProcessInfo, ::profiler::do_not_calc_hash> process_info_map;
// Using static is safe because processTraceEvent() is called from one thread
static process_info_map PROCESS_INFO_TABLE;
static thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })();
process_info_map PROCESS_INFO_TABLE;
thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })();
//////////////////////////////////////////////////////////////////////////
void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent)
{
static const decltype(_traceEvent->EventHeader.EventDescriptor.Opcode) SWITCH_CONTEXT_OPCODE = 36;
if (_traceEvent->EventHeader.EventDescriptor.Opcode != SWITCH_CONTEXT_OPCODE)
return;
@ -188,10 +190,21 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////////
#ifndef EASY_MAGIC_STATIC_CPP11
class EasyEventTracerInstance {
friend EasyEventTracer;
EasyEventTracer instance;
} EASY_EVENT_TRACER;
#endif
EasyEventTracer& EasyEventTracer::instance()
{
#ifndef EASY_MAGIC_STATIC_CPP11
return EASY_EVENT_TRACER.instance;
#else
static EasyEventTracer tracer;
return tracer;
#endif
}
EasyEventTracer::EasyEventTracer()
@ -231,7 +244,7 @@ namespace profiler {
#if EASY_LOG_ENABLED != 0
if (!success)
::std::cerr << "Warning: EasyProfiler failed to set Debug privelege for the application. Some context switch events could not get process name.";
::std::cerr << "Warning: EasyProfiler failed to set Debug privelege for the application. Some context switch events could not get process name.\n";
#endif
return success;
@ -282,40 +295,43 @@ namespace profiler {
}
#if EASY_LOG_ENABLED != 0
::std::cerr << "Error: EasyProfiler.ETW not launched: ERROR_ALREADY_EXISTS. To stop another session execute cmd: logman stop \"" << KERNEL_LOGGER_NAME << "\" -ets";
::std::cerr << "Error: EasyProfiler.ETW not launched: ERROR_ALREADY_EXISTS. To stop another session execute cmd: logman stop \"" << KERNEL_LOGGER_NAME << "\" -ets\n";
#endif
return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE;
}
case ERROR_ACCESS_DENIED:
#if EASY_LOG_ENABLED != 0
::std::cerr << "Error: EasyProfiler.ETW not launched: ERROR_ACCESS_DENIED. Try to launch your application as Administrator.";
::std::cerr << "Error: EasyProfiler.ETW not launched: ERROR_ACCESS_DENIED. Try to launch your application as Administrator.\n";
#endif
return EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS;
case ERROR_BAD_LENGTH:
#if EASY_LOG_ENABLED != 0
::std::cerr << "Error: EasyProfiler.ETW not launched: ERROR_BAD_LENGTH. It seems that your KERNEL_LOGGER_NAME differs from \"" << m_properties.sessionName << "\". Try to re-compile easy_profiler or contact EasyProfiler developers.";
::std::cerr << "Error: EasyProfiler.ETW not launched: ERROR_BAD_LENGTH. It seems that your KERNEL_LOGGER_NAME differs from \"" << m_properties.sessionName << "\". Try to re-compile easy_profiler or contact EasyProfiler developers.\n";
#endif
return EVENT_TRACING_BAD_PROPERTIES_SIZE;
}
#if EASY_LOG_ENABLED != 0
::std::cerr << "Error: EasyProfiler.ETW not launched: StartTrace() returned " << startTraceResult;
::std::cerr << "Error: EasyProfiler.ETW not launched: StartTrace() returned " << startTraceResult << ::std::endl;
#endif
return EVENT_TRACING_MISTERIOUS_ERROR;
}
::profiler::EventTracingEnableStatus EasyEventTracer::enable(bool _force)
{
static const decltype(m_properties.base.Wnode.ClientContext) RAW_TIMESTAMP_TIME_TYPE = 1;
::profiler::guard_lock<::profiler::spin_lock> lock(m_spin);
if (m_bEnabled)
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
// Trying to set debug privilege for current process
// to be able to get other process information (process name)
/*
Trying to set debug privilege for current process
to be able to get other process information (process name).
Also it seems that debug privelege lets you to launch
event tracing without Administrator access rights.
*/
if (!m_bPrivilegeSet)
m_bPrivilegeSet = setDebugPrivilege();
@ -329,6 +345,7 @@ namespace profiler {
m_properties.base.EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
m_properties.base.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
// Start event tracing
auto res = startTrace(_force);
if (res != EVENT_TRACING_LAUNCHED_SUCCESSFULLY)
return res;
@ -342,7 +359,7 @@ namespace profiler {
if (m_openedHandle == INVALID_PROCESSTRACE_HANDLE)
{
#if EASY_LOG_ENABLED != 0
::std::cerr << "Error: EasyProfiler.ETW not launched: OpenTrace() returned invalid handle.";
::std::cerr << "Error: EasyProfiler.ETW not launched: OpenTrace() returned invalid handle.\n";
#endif
return EVENT_TRACING_OPEN_TRACE_ERROR;
}

View File

@ -50,11 +50,14 @@
namespace profiler {
class EasyEventTracer final
class EasyEventTracer EASY_FINAL
{
#ifndef EASY_MAGIC_STATIC_CPP11
friend class EasyEventTracerInstance;
#endif
#pragma pack(push, 1)
struct Properties final {
struct Properties {
EVENT_TRACE_PROPERTIES base;
char sessionName[sizeof(KERNEL_LOGGER_NAME)];
};

View File

@ -116,7 +116,7 @@ namespace profiler {
\ingroup profiler
*/
class hashed_cstr final : public cstring
class hashed_cstr : public cstring
{
typedef cstring Parent;
@ -180,7 +180,7 @@ namespace std {
namespace profiler {
class hashed_stdstring final
class hashed_stdstring
{
::std::string m_str;
size_t m_hash;

View File

@ -39,7 +39,7 @@
namespace profiler {
class OStream final
class OStream
{
::std::stringstream m_stream;

View File

@ -67,7 +67,7 @@ extern "C" {
MANAGER.setEnabled(isEnable);
}
PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName)
PROFILER_API void storeEvent(const BaseBlockDescriptor* _desc, const char* _runtimeName)
{
MANAGER.storeBlock(_desc, _runtimeName);
}
@ -145,37 +145,69 @@ BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, EasyBlockStatus _status
}
BlockDescriptor::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_name(_name)
, m_filename(_filename)
, m_pStatus(nullptr)
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
, m_expired(false)
{
}
//////////////////////////////////////////////////////////////////////////
BlockDescriptor::BlockDescriptor(EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
: BaseBlockDescriptor(0, _status, _line, _block_type, _color)
, m_name(_name)
, m_filename(_filename)
, m_pStatus(nullptr)
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
, m_expired(false)
{
}
#ifndef EASY_BLOCK_DESC_FULL_COPY
# define EASY_BLOCK_DESC_FULL_COPY 0
#endif
BlockDescRef::~BlockDescRef()
#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
{
MANAGER.markExpired(m_desc.id());
}
friend ProfileManager;
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
EASY_BLOCK_DESC_STRING m_filename; ///< Source file name where this block is declared
uint16_t m_size; ///< Used memory size
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_name(_name)
, m_filename(_filename)
, m_size(static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2))
{
}
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.
//////////////////////////////////////////////////////////////////////////
void ThreadStorage::storeBlock(const profiler::Block& block)
{
#if EASY_MEASURE_STORAGE_EXPAND != 0
static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED ? profiler::ON : profiler::OFF, EASY_UNIQUE_LINE_ID, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White);
EASY_LOCAL_STATIC_PTR(const BaseBlockDescriptor*, desc,\
MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED ? 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()));
@ -183,14 +215,13 @@ void ThreadStorage::storeBlock(const profiler::Block& block)
#if EASY_MEASURE_STORAGE_EXPAND != 0
const bool expanded = (desc->m_status & profiler::ON) && blocks.closedList.need_expand(size);
profiler::Block b(0ULL, desc->id(), "");
if (expanded) b.start();
if (expanded) beginTime = getCurrentTime();
#endif
auto data = blocks.closedList.allocate(size);
#if EASY_MEASURE_STORAGE_EXPAND != 0
if (expanded) b.finish();
if (expanded) endTime = getCurrentTime();
#endif
::new (data) SerializedBlock(block, name_length);
@ -199,10 +230,12 @@ void ThreadStorage::storeBlock(const profiler::Block& block)
#if EASY_MEASURE_STORAGE_EXPAND != 0
if (expanded)
{
name_length = 0;
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, name_length);
::new (data) SerializedBlock(b, 0);
blocks.usedMemorySize += size;
}
#endif
@ -258,21 +291,23 @@ ProfileManager::~ProfileManager()
}
}
#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 m_profileManager;
return m_profileManager;
}
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);
m_descriptors[_id]->m_expired = true;
static ProfileManager profileManager;
return profileManager;
#endif
}
ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id)
@ -287,9 +322,32 @@ ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_
return it != m_threads.end() ? &it->second : nullptr;
}
void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName)
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)
{
if (!m_isEnabled.load(std::memory_order_acquire) || !(_desc.m_status & profiler::ON))
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];
auto desc = new BlockDescriptor(static_cast<block_id_t>(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color);
m_usedMemorySize += desc->m_size;
m_descriptors.emplace_back(desc);
m_descriptorsMap.emplace(key, desc->id());
return desc;
}
void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
{
if (!m_isEnabled.load(std::memory_order_acquire) || !(_desc->m_status & profiler::ON))
return;
if (THREAD_STORAGE == nullptr)
@ -400,13 +458,21 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler
void ProfileManager::setEnabled(bool isEnable)
{
m_isEnabled.store(isEnable, std::memory_order_release);
auto time = getCurrentTime();
const bool prev = m_isEnabled.exchange(isEnable, std::memory_order_release);
if (prev == isEnable)
return;
#ifdef _WIN32
if (isEnable) {
if (isEnable)
{
m_beginTime = time;
if (m_isEventTracingEnabled.load(std::memory_order_acquire))
EasyEventTracer::instance().enable(true);
} else {
}
else
{
m_endTime = time;
EasyEventTracer::instance().disable();
}
#endif
@ -489,6 +555,10 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
_outputStream.write(0LL);
#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);
@ -498,21 +568,15 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
// Write block descriptors
for (const auto descriptor : m_descriptors)
{
if (descriptor == nullptr)
{
_outputStream.write<uint16_t>(0U);
continue;
}
const auto name_size = static_cast<uint16_t>(strlen(descriptor->name()) + 1);
const auto filename_size = static_cast<uint16_t>(strlen(descriptor->file()) + 1);
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->file(), filename_size);
_outputStream.write(descriptor->filename(), filename_size);
}
// Write blocks and context switch events for each thread
@ -544,17 +608,6 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
++it;
}
// 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)
{
if (desc == nullptr || !desc->m_expired)
continue;
m_usedMemorySize -= desc->m_size;
delete desc;
desc = nullptr;
}
//if (wasEnabled)
// ::profiler::setEnabled(true);
@ -595,26 +648,17 @@ const char* ProfileManager::registerThread(const char* name, ThreadGuard& thread
return THREAD_STORAGE->name.c_str();
}
void ProfileManager::setBlockStatus(block_id_t _id, const hashed_stdstring& _key, EasyBlockStatus _status)
void ProfileManager::setBlockStatus(block_id_t _id, EasyBlockStatus _status)
{
if (m_isEnabled.load(std::memory_order_acquire))
return; // Changing blocks statuses is restricted while profile session is active
guard_lock_t lock(m_storedSpin);
auto desc = m_descriptors[_id];
if (desc != nullptr)
if (_id < m_descriptors.size())
{
auto desc = m_descriptors[_id];
lock.unlock();
*desc->m_pStatus = _status;
desc->m_status = _status; // TODO: possible concurrent access, atomic may be needed
}
else
{
#ifdef _WIN32
blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode());
m_blocksEnableStatus[key] = _status;
#else
m_blocksEnableStatus[_key] = _status;
#endif
desc->m_status = _status;
}
}

View File

@ -81,8 +81,13 @@ namespace profiler {
//////////////////////////////////////////////////////////////////////////
#define EASY_ENABLE_BLOCK_STATUS 1
#define EASY_ENABLE_ALIGNMENT 0
#ifndef EASY_ENABLE_BLOCK_STATUS
# define EASY_ENABLE_BLOCK_STATUS 1
#endif
#ifndef EASY_ENABLE_ALIGNMENT
# define EASY_ENABLE_ALIGNMENT 0
#endif
#if EASY_ENABLE_ALIGNMENT == 0
# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR
@ -101,7 +106,7 @@ namespace profiler {
#endif
template <const uint16_t N>
class chunk_allocator final
class chunk_allocator
{
struct chunk { EASY_ALIGNED(int8_t, data[N], 64); chunk* prev = nullptr; };
@ -248,11 +253,11 @@ const uint16_t SIZEOF_CSWITCH = sizeof(profiler::BaseBlockData) + 1 + sizeof(uin
typedef std::vector<profiler::SerializedBlock*> serialized_list_t;
template <class T, const uint16_t N>
struct BlocksList final
struct BlocksList
{
BlocksList() = default;
class Stack final {
class Stack {
//std::stack<T> m_stack;
std::vector<T> m_stack;
@ -298,7 +303,7 @@ struct BlocksList final
};
struct ThreadStorage final
struct ThreadStorage
{
BlocksList<std::reference_wrapper<profiler::Block>, SIZEOF_CSWITCH * (uint16_t)128U> blocks;
BlocksList<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync;
@ -320,9 +325,13 @@ struct ThreadStorage final
//////////////////////////////////////////////////////////////////////////
class ProfileManager final
class BlockDescriptor;
class ProfileManager
{
friend profiler::BlockDescRef;
#ifndef EASY_MAGIC_STATIC_CPP11
friend class ProfileManagerInstance;
#endif
ProfileManager();
ProfileManager(const ProfileManager& p) = delete;
@ -330,21 +339,22 @@ class ProfileManager final
typedef profiler::guard_lock<profiler::spin_lock> guard_lock_t;
typedef std::map<profiler::thread_id_t, ThreadStorage> map_of_threads_stacks;
typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t;
typedef std::vector<BlockDescriptor*> block_descriptors_t;
#ifdef _WIN32
typedef std::unordered_map<profiler::hashed_cstr, profiler::EasyBlockStatus> blocks_enable_status_t;
typedef std::unordered_map<profiler::hashed_cstr, profiler::block_id_t> descriptors_map_t;
#else
typedef std::unordered_map<profiler::hashed_stdstring, profiler::EasyBlockStatus> blocks_enable_status_t;
typedef std::unordered_map<profiler::hashed_stdstring, profiler::block_id_t> descriptors_map_t;
#endif
map_of_threads_stacks m_threads;
block_descriptors_t m_descriptors;
blocks_enable_status_t m_blocksEnableStatus;
descriptors_map_t m_descriptorsMap;
uint64_t m_usedMemorySize = 0;
profiler::timestamp_t m_beginTime = 0;
profiler::timestamp_t m_endTime = 0;
profiler::spin_lock m_spin;
profiler::spin_lock m_storedSpin;
profiler::block_id_t m_idCounter = 0;
std::atomic_bool m_isEnabled;
std::atomic_bool m_isEventTracingEnabled;
@ -353,7 +363,7 @@ class ProfileManager final
#endif
uint32_t dumpBlocksToStream(profiler::OStream& _outputStream);
void setBlockStatus(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, profiler::EasyBlockStatus _status);
void setBlockStatus(profiler::block_id_t _id, profiler::EasyBlockStatus _status);
std::thread m_listenThread;
bool m_isAlreadyListened = false;
@ -367,32 +377,15 @@ public:
static ProfileManager& instance();
~ProfileManager();
template <class ... TArgs>
const profiler::BaseBlockDescriptor* addBlockDescriptor(profiler::EasyBlockStatus _defaultStatus, const char* _autogenUniqueId, TArgs ... _args)
{
auto desc = new profiler::BlockDescriptor(_defaultStatus, _args...);
const profiler::BaseBlockDescriptor* addBlockDescriptor(profiler::EasyBlockStatus _defaultStatus,
const char* _autogenUniqueId,
const char* _name,
const char* _filename,
int _line,
profiler::block_type_t _block_type,
profiler::color_t _color);
guard_lock_t lock(m_storedSpin);
m_usedMemorySize += desc->m_size;
desc->m_id = m_idCounter++;
m_descriptors.emplace_back(desc);
blocks_enable_status_t::key_type key(_autogenUniqueId);
auto it = m_blocksEnableStatus.find(key);
if (it != m_blocksEnableStatus.end())
{
desc->m_status = it->second;
desc->m_pStatus = &it->second;
}
else
{
desc->m_pStatus = &m_blocksEnableStatus.emplace(key, desc->status()).first->second;
}
return desc;
}
void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName);
void storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName);
void beginBlock(profiler::Block& _block);
void endBlock();
void setEnabled(bool isEnable);
@ -419,7 +412,6 @@ public:
void stopListenSignalToCapture();
private:
void markExpired(profiler::block_id_t _id);
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id);
ThreadStorage* _findThreadStorage(profiler::thread_id_t _thread_id);

View File

@ -217,6 +217,8 @@ void validate_pointers(::std::atomic<int>& _progress, const char* _oldbase, ::pr
//////////////////////////////////////////////////////////////////////////
const int64_t TIME_FACTOR = 1000000000LL;
extern "C" PROFILER_API::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progress, const char* filename,
::profiler::SerializedData& serialized_blocks,
::profiler::SerializedData& serialized_descriptors,
@ -236,6 +238,17 @@ extern "C" PROFILER_API::profiler::block_index_t fillTreesFromFile(::std::atomic
int64_t cpu_frequency = 0LL;
inFile.read((char*)&cpu_frequency, sizeof(int64_t));
::profiler::timestamp_t begin_time = 0, end_time = 0;
inFile.read((char*)&begin_time, sizeof(::profiler::timestamp_t));
inFile.read((char*)&end_time, sizeof(::profiler::timestamp_t));
if (cpu_frequency != 0)
{
begin_time *= TIME_FACTOR;
begin_time /= cpu_frequency;
end_time *= TIME_FACTOR;
end_time /= cpu_frequency;
}
uint32_t total_blocks_number = 0;
inFile.read((char*)&total_blocks_number, sizeof(decltype(total_blocks_number)));
if (total_blocks_number == 0)
@ -338,25 +351,29 @@ extern "C" PROFILER_API::profiler::block_index_t fillTreesFromFile(::std::atomic
inFile.read(data, sz);
i += sz;
auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data);
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
auto t_end = t_begin + 1;
if (cpu_frequency != 0)
{
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
auto t_end = t_begin + 1;
*t_begin *= 1000000000LL;
*t_begin *= TIME_FACTOR;
*t_begin /= cpu_frequency;
*t_end *= 1000000000LL;
*t_end *= TIME_FACTOR;
*t_end /= cpu_frequency;
}
blocks.emplace_back();
::profiler::BlocksTree& tree = blocks.back();
tree.node = baseData;
const auto block_index = blocks_counter++;
if (*t_end > begin_time)
{
if (*t_begin < begin_time)
*t_begin = begin_time;
root.sync.emplace_back(block_index);
blocks.emplace_back();
::profiler::BlocksTree& tree = blocks.back();
tree.node = baseData;
const auto block_index = blocks_counter++;
root.sync.emplace_back(block_index);
}
if (progress.load(::std::memory_order_acquire) < 0)
break; // Loading interrupted
@ -388,111 +405,116 @@ extern "C" PROFILER_API::profiler::block_index_t fillTreesFromFile(::std::atomic
if (descriptors[baseData->id()] == nullptr)
return 0;
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
auto t_end = t_begin + 1;
if (cpu_frequency != 0)
{
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
auto t_end = t_begin + 1;
*t_begin *= 1000000000LL;
*t_begin *= TIME_FACTOR;
*t_begin /= cpu_frequency;
*t_end *= 1000000000LL;
*t_end *= TIME_FACTOR;
*t_end /= cpu_frequency;
}
blocks.emplace_back();
::profiler::BlocksTree& tree = blocks.back();
tree.node = baseData;
const auto block_index = blocks_counter++;
auto& per_parent_statistics = parent_statistics[thread_id];
auto& per_thread_statistics = thread_statistics[thread_id];
if (*tree.node->name() != 0)
if (*t_end >= begin_time)
{
// If block has runtime name then generate new id for such block.
// Blocks with the same name will have same id.
if (*t_begin < begin_time)
*t_begin = begin_time;
IdMap::key_type key(tree.node->name());
auto it = identification_table.find(key);
if (it != identification_table.end())
blocks.emplace_back();
::profiler::BlocksTree& tree = blocks.back();
tree.node = baseData;
const auto block_index = blocks_counter++;
auto& per_parent_statistics = parent_statistics[thread_id];
auto& per_thread_statistics = thread_statistics[thread_id];
if (*tree.node->name() != 0)
{
// There is already block with such name, use it's id
baseData->setId(it->second);
}
else
{
// There were no blocks with such name, generate new id and save it in the table for further usage.
auto id = static_cast<::profiler::block_id_t>(descriptors.size());
identification_table.emplace(key, id);
if (descriptors.capacity() == descriptors.size())
descriptors.reserve((descriptors.size() * 3) >> 1);
descriptors.push_back(descriptors[baseData->id()]);
baseData->setId(id);
}
}
// If block has runtime name then generate new id for such block.
// Blocks with the same name will have same id.
if (!root.children.empty())
{
auto& back = blocks[root.children.back()];
auto t1 = back.node->end();
auto mt0 = tree.node->begin();
if (mt0 < t1)//parent - starts earlier than last ends
{
//auto lower = ::std::lower_bound(root.children.begin(), root.children.end(), tree);
/**/
EASY_BLOCK("Find children", ::profiler::colors::Blue);
auto rlower1 = ++root.children.rbegin();
for (; rlower1 != root.children.rend() && !(mt0 > blocks[*rlower1].node->begin()); ++rlower1);
auto lower = rlower1.base();
::std::move(lower, root.children.end(), ::std::back_inserter(tree.children));
root.children.erase(lower, root.children.end());
EASY_END_BLOCK;
::profiler::timestamp_t children_duration = 0;
if (gather_statistics)
IdMap::key_type key(tree.node->name());
auto it = identification_table.find(key);
if (it != identification_table.end())
{
EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta);
per_parent_statistics.clear();
//per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows
//per_parent_statistics.reserve(tree.children.size() * 2); // this gives no speed-up on Windows
// TODO: check this behavior on Linux
for (auto i : tree.children)
{
auto& child = blocks[i];
child.per_parent_stats = update_statistics(per_parent_statistics, child, i, block_index);
children_duration += child.node->duration();
if (tree.depth < child.depth)
tree.depth = child.depth;
}
// There is already block with such name, use it's id
baseData->setId(it->second);
}
else
{
for (auto i : tree.children)
{
const auto& child = blocks[i];
children_duration += child.node->duration();
if (tree.depth < child.depth)
tree.depth = child.depth;
}
// There were no blocks with such name, generate new id and save it in the table for further usage.
auto id = static_cast<::profiler::block_id_t>(descriptors.size());
identification_table.emplace(key, id);
if (descriptors.capacity() == descriptors.size())
descriptors.reserve((descriptors.size() * 3) >> 1);
descriptors.push_back(descriptors[baseData->id()]);
baseData->setId(id);
}
++tree.depth;
}
}
root.children.emplace_back(block_index);// ::std::move(tree));
if (!root.children.empty())
{
auto& back = blocks[root.children.back()];
auto t1 = back.node->end();
auto mt0 = tree.node->begin();
if (mt0 < t1)//parent - starts earlier than last ends
{
//auto lower = ::std::lower_bound(root.children.begin(), root.children.end(), tree);
/**/
EASY_BLOCK("Find children", ::profiler::colors::Blue);
auto rlower1 = ++root.children.rbegin();
for (; rlower1 != root.children.rend() && !(mt0 > blocks[*rlower1].node->begin()); ++rlower1);
auto lower = rlower1.base();
::std::move(lower, root.children.end(), ::std::back_inserter(tree.children));
root.children.erase(lower, root.children.end());
EASY_END_BLOCK;
::profiler::timestamp_t children_duration = 0;
if (gather_statistics)
{
EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta);
per_parent_statistics.clear();
//per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows
//per_parent_statistics.reserve(tree.children.size() * 2); // this gives no speed-up on Windows
// TODO: check this behavior on Linux
for (auto i : tree.children)
{
auto& child = blocks[i];
child.per_parent_stats = update_statistics(per_parent_statistics, child, i, block_index);
children_duration += child.node->duration();
if (tree.depth < child.depth)
tree.depth = child.depth;
}
}
else
{
for (auto i : tree.children)
{
const auto& child = blocks[i];
children_duration += child.node->duration();
if (tree.depth < child.depth)
tree.depth = child.depth;
}
}
++tree.depth;
}
}
root.children.emplace_back(block_index);// ::std::move(tree));
if (gather_statistics)
{
EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral);
tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index, thread_id);
if (gather_statistics)
{
EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral);
tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index, thread_id);
}
}
if (progress.load(::std::memory_order_acquire) < 0)

View File

@ -32,7 +32,7 @@ namespace profiler {
#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION)
// std::atomic_flag on Windows works slower than critical section, so we will use it instead of std::atomic_flag...
// By the way, Windows critical sections are slower than std::atomic_flag on Unix.
class spin_lock final { CRITICAL_SECTION m_lock; public:
class spin_lock { CRITICAL_SECTION m_lock; public:
void lock() {
EnterCriticalSection(&m_lock);
@ -52,7 +52,7 @@ namespace profiler {
};
#else
// std::atomic_flag on Unix works fine and very fast (almost instant!)
class spin_lock final { ::std::atomic_flag m_lock; public:
class spin_lock { ::std::atomic_flag m_lock; public:
void lock() {
while (m_lock.test_and_set(::std::memory_order_acquire));
@ -69,7 +69,7 @@ namespace profiler {
#endif
template <class T>
class guard_lock final
class guard_lock
{
T& m_lock;
bool m_isLocked = false;