diff --git a/include/profiler/profiler.h b/include/profiler/profiler.h index 49491bf..905f025 100644 --- a/include/profiler/profiler.h +++ b/include/profiler/profiler.h @@ -19,67 +19,36 @@ along with this program.If not, see . #ifndef EASY_PROFILER____H_______ #define EASY_PROFILER____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 -#elif defined(__GNUC__) -#ifndef __clang__ -# 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 -#if defined ( __clang__ ) -# if (__clang_major__ == 3 && __clang_minor__ < 3) || (__clang_major__ < 3) -# define EASY_THREAD_LOCAL __thread -#endif -#endif - -#endif - -// TODO: Check thread local support for clanv earlier than 3.3 - -#ifndef EASY_THREAD_LOCAL -# define EASY_THREAD_LOCAL thread_local -# define EASY_THREAD_LOCAL_CPP11 -#endif +#include "profiler/profiler_aux.h" #if defined ( __clang__ ) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" #endif #ifndef FULL_DISABLE_PROFILER -#include - -#define EASY_TOKEN_JOIN(x, y) x ## y -#define EASY_TOKEN_CONCATENATE(x, y) EASY_TOKEN_JOIN(x, y) -#define EASY_UNIQUE_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x) -#define EASY_UNIQUE_DESC(x) EASY_TOKEN_CONCATENATE(unique_profiler_descriptor_, x) - /** \defgroup profiler Profiler */ -namespace profiler { - template struct NameSwitch final { - static const char* runtime_name(const char* name) { return name; } - static const char* compiletime_name(const char*) { return ""; } - }; +/** If != 0 then EasyProfiler will measure time for blocks storage expansion. +If 0 then EasyProfiler will be compiled without blocks of code responsible +for measuring these events. - template <> struct NameSwitch final { - static const char* runtime_name(const char*) { return ""; } - static const char* compiletime_name(const char* name) { return name; } - }; -} // END of namespace profiler. +These are "EasyProfiler.ExpandStorage" blocks on a diagram. -#define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::compiletime_name(name) -#define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::runtime_name(name) +\ingroup profiler +*/ +# define EASY_MEASURE_STORAGE_EXPAND 0 +/** If true then "EasyProfiler.ExpandStorage" events are enabled by default and will be +writed to output file or translated over the net. +If false then you need to enable these events via GUI if you'll want to see them. + +\ingroup profiler +*/ +# define EASY_STORAGE_EXPAND_ENABLED true /** Macro of beginning of block with custom name and color. @@ -88,6 +57,8 @@ namespace profiler { void foo() { // some code ... + + EASY_BLOCK("Check something", profiler::DISABLED); // Disabled block (There is possibility to enable this block later via GUI) if(something){ EASY_BLOCK("Calling bar()"); // Block with default color bar(); @@ -96,6 +67,10 @@ namespace profiler { EASY_BLOCK("Calling baz()", profiler::colors::Red); // Red block baz(); } + EASY_END_BLOCK; // End of "Check something" block (Even if "Check something" is disabled, this EASY_END_BLOCK will not end any other block). + + EASY_BLOCK("Some another block", profiler::colors::Blue, profiler::DISABLED); // Disabled block with Blue color + // some another code... } \endcode @@ -103,9 +78,9 @@ Block will be automatically completed by destructor. \ingroup profiler */ -#define EASY_BLOCK(name, ...)\ - static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ - ::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\ +# define EASY_BLOCK(name, ...)\ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ + 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 @@ -122,15 +97,20 @@ Block will be automatically completed by destructor. EASY_FUNCTION(profiler::colors::Green); // Green block with name="bar" //some code... } + + void baz(){ + EASY_FUNCTION(profiler::DISABLED); // Disabled block with name="baz" and default color (There is possibility to enable this block later via GUI) + // som code... + } \endcode Name of the block automatically created with function name. \ingroup profiler */ -#define EASY_FUNCTION(...)\ - static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\ - ::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\ +# define EASY_FUNCTION(...)\ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ + __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 @@ -157,7 +137,7 @@ int foo() \ingroup profiler */ -#define EASY_END_BLOCK ::profiler::endBlock(); +# define EASY_END_BLOCK ::profiler::endBlock(); /** Macro of creating event with custom name and color. @@ -168,20 +148,23 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION. \ingroup profiler */ -#define EASY_EVENT(name, ...)\ - static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = \ - ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\ +# define EASY_EVENT(name, ...)\ + static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = \ + ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ + ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__));\ ::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); /** Macro enabling profiler + \ingroup profiler */ -#define EASY_PROFILER_ENABLE ::profiler::setEnabled(true); +# define EASY_PROFILER_ENABLE ::profiler::setEnabled(true); /** Macro disabling profiler + \ingroup profiler */ -#define EASY_PROFILER_DISABLE ::profiler::setEnabled(false); +# define EASY_PROFILER_DISABLE ::profiler::setEnabled(false); /** Macro of naming current thread. @@ -189,7 +172,7 @@ If this thread has been already named then nothing changes. \ingroup profiler */ -#define EASY_THREAD(name)\ +# define EASY_THREAD(name)\ EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\ if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\ EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__); @@ -200,39 +183,31 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe \ingroup profiler */ -#define EASY_MAIN_THREAD EASY_THREAD("Main") +# define EASY_MAIN_THREAD EASY_THREAD("Main") #else -#define EASY_BLOCK(...) -#define EASY_FUNCTION(...) -#define EASY_END_BLOCK -#define EASY_PROFILER_ENABLE -#define EASY_PROFILER_DISABLE -#define EASY_EVENT(...) -#define EASY_THREAD(...) -#define EASY_MAIN_THREAD +# define EASY_MEASURE_STORAGE_EXPAND 0 +# define EASY_STORAGE_EXPAND_ENABLED false +# define EASY_BLOCK(...) +# define EASY_FUNCTION(...) +# define EASY_END_BLOCK +# define EASY_PROFILER_ENABLE +# define EASY_PROFILER_DISABLE +# define EASY_EVENT(...) +# define EASY_THREAD(...) +# define EASY_MAIN_THREAD #endif -#include -#include -#include "profiler/profiler_colors.h" - -#ifdef _WIN32 -#ifdef _BUILD_PROFILER -#define PROFILER_API __declspec(dllexport) -#else -#define PROFILER_API __declspec(dllimport) -#endif -#else -#define PROFILER_API -#endif +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// class ProfileManager; +class ThreadStorage; namespace profiler { - class Block; - class BaseBlockDescriptor; + ////////////////////////////////////////////////////////////////////// + // Core types typedef uint64_t timestamp_t; typedef uint32_t thread_id_t; @@ -249,17 +224,7 @@ namespace profiler { }; typedef BlockType block_type_t; - extern "C" { - PROFILER_API const BaseBlockDescriptor& registerDescription(const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color = ::profiler::colors::Default); - PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName); - PROFILER_API void beginBlock(Block& _block); - PROFILER_API void endBlock(); - PROFILER_API void setEnabled(bool _isEnable); - PROFILER_API uint32_t dumpBlocksToFile(const char* _filename); - PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line); - PROFILER_API void setContextSwitchLogFilename(const char* _name); - PROFILER_API const char* getContextSwitchLogFilename(); - } + //*********************************************** #pragma pack(push,1) class PROFILER_API BaseBlockDescriptor @@ -274,7 +239,7 @@ namespace profiler { block_type_t m_type; ///< Type of the block (See BlockType) bool m_enabled; ///< If false then blocks with such id() will not be stored by profiler during profile session - BaseBlockDescriptor(block_id_t _id, int _line, block_type_t _block_type, color_t _color); + BaseBlockDescriptor(block_id_t _id, bool _enabled, int _line, block_type_t _block_type, color_t _color); public: @@ -283,22 +248,10 @@ namespace profiler { inline color_t color() const { return m_color; } inline block_type_t type() const { return m_type; } inline bool enabled() const { return m_enabled; } - }; - class PROFILER_API BlockDescriptor final : public BaseBlockDescriptor - { - friend ::ProfileManager; + }; // END of class BaseBlockDescriptor. - 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 - - public: - - BlockDescriptor(uint64_t& _used_mem, block_id_t _id, 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; } - }; + //*********************************************** class PROFILER_API BaseBlockData { @@ -325,20 +278,52 @@ namespace profiler { private: BaseBlockData() = delete; - }; + + }; // END of class BaseBlockData. #pragma pack(pop) + //*********************************************** + + 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 + + BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color); + void setId(block_id_t _id); + + public: + + BlockDescriptor(uint64_t& _used_mem, block_id_t _id, bool _enabled, 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 { friend ::ProfileManager; + friend ::ThreadStorage; const char* m_name; bool m_enabled; private: + void start(); + void start(timestamp_t _time); void finish(); - void finish(timestamp_t _end_time); + void finish(timestamp_t _time); inline bool finished() const { return m_end >= m_begin; } inline bool enabled() const { return m_enabled; } @@ -346,18 +331,58 @@ namespace profiler { Block(Block&& that); Block(const BaseBlockDescriptor& _desc, const char* _runtimeName); - Block(timestamp_t _begin_time, block_id_t _id, bool _enabled, const char* _runtimeName); + Block(timestamp_t _begin_time, block_id_t _id, const char* _runtimeName); ~Block(); inline const char* name() const { return m_name; } - }; + + }; // END of class Block. + + //*********************************************** + + class PROFILER_API BlockDescRef final + { + const BaseBlockDescriptor& m_desc; + + public: + + 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. + + ////////////////////////////////////////////////////////////////////// + // Core API + + extern "C" { + PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color); + PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName); + PROFILER_API void beginBlock(Block& _block); + PROFILER_API void endBlock(); + PROFILER_API void setEnabled(bool _isEnable); + PROFILER_API uint32_t dumpBlocksToFile(const char* _filename); + PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line); + PROFILER_API void setContextSwitchLogFilename(const char* _name); + PROFILER_API const char* getContextSwitchLogFilename(); + } + + inline void setEnabled(::profiler::EasyEnableFlag _isEnable) { + setEnabled(_isEnable == ::profiler::ENABLED); + } ////////////////////////////////////////////////////////////////////// } // END of namespace profiler. #if defined ( __clang__ ) -#pragma clang diagnostic pop +# pragma clang diagnostic pop #endif #endif // EASY_PROFILER____H_______ diff --git a/include/profiler/profiler_aux.h b/include/profiler/profiler_aux.h new file mode 100644 index 0000000..5ab3a4b --- /dev/null +++ b/include/profiler/profiler_aux.h @@ -0,0 +1,176 @@ +/************************************************************************ +* file name : profiler_aux.h +* ----------------- : +* creation time : 2016/06/11 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : This file contains auxiliary profiler macros and funcitons. +* ----------------- : +* change log : * 2016/06/11 Victor Zarubkin: Moved sources from profiler.h +* : +* : * +* ----------------- : +* 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 . +************************************************************************/ + +#ifndef EASY_PROFILER__AUX__H_______ +#define EASY_PROFILER__AUX__H_______ + +#include +#include +#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 EasyEnableFlag : uint8_t + { + DISABLED = 0, + ENABLED = 1 + }; + +} + +////////////////////////////////////////////////////////////////////////// + +#ifndef FULL_DISABLE_PROFILER + +#include + +# define EASY_STRINGIFY(a) #a +# define EASY_TOKEN_JOIN(x, y) x ## y +# define EASY_TOKEN_CONCATENATE(x, y) EASY_TOKEN_JOIN(x, y) +# define EASY_UNIQUE_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x) +# define EASY_UNIQUE_DESC(x) EASY_TOKEN_CONCATENATE(unique_profiler_descriptor_, x) + +namespace profiler { + + template struct NameSwitch 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 final { + static const char* runtime_name(const char*) { return ""; } + static const char* compiletime_name(const char* name, const char*) { return name; } + }; + + //*********************************************** + + inline color_t extract_color() { + return ::profiler::colors::Default; + } + + template + inline color_t extract_color(::profiler::EasyEnableFlag, TArgs...) { + return ::profiler::colors::Default; + } + + template + inline color_t extract_color(color_t _color, TArgs...) { + return _color; + } + + template + inline color_t extract_color(T, color_t _color, TArgs...) { + return _color; + } + + template + inline color_t extract_color(TArgs...) { + static_assert(false, "No profiler::color_t in arguments list for EASY_BLOCK(name, ...)!"); + return ::profiler::colors::Default; + } + + //*********************************************** + + inline bool extract_enable_flag() { + return true; + } + + template + inline bool extract_enable_flag(T, ::profiler::EasyEnableFlag _flag, TArgs...) { + return _flag == ::profiler::ENABLED; + } + + template + inline bool extract_enable_flag(::profiler::EasyEnableFlag _flag, TArgs...) { + return _flag == ::profiler::ENABLED; + } + + template + inline bool extract_enable_flag(TArgs...) { + static_assert(sizeof...(TArgs) < 2, "No EasyEnableFlag in arguments list for EASY_BLOCK(name, ...)!"); + return true; + } + + //*********************************************** + +} // END of namespace profiler. + +# define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFY(__LINE__) +# define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::compiletime_name(name, EASY_UNIQUE_LINE_ID) +# define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::runtime_name(name) + +#endif // FULL_DISABLE_PROFILER + +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__AUX__H_______ diff --git a/sample/main.cpp b/sample/main.cpp index 143fb94..c741a24 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -105,7 +105,7 @@ void prepareRender(){ int multPhys(int i) { - EASY_FUNCTION(profiler::colors::Red700); + EASY_FUNCTION(profiler::colors::Red700, profiler::DISABLED); return i * i * i * i / 100; } diff --git a/src/block.cpp b/src/block.cpp index 48b2ce3..9d7597a 100644 --- a/src/block.cpp +++ b/src/block.cpp @@ -68,25 +68,37 @@ Block::Block(Block&& that) } Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName) - : Block(getCurrentTime(), _descriptor.id(), _descriptor.enabled(), _runtimeName) + : BaseBlockData(_descriptor.enabled() ? getCurrentTime() : 1ULL, _descriptor.id()) + , m_name(_runtimeName) + , m_enabled(_descriptor.enabled()) { } -Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, bool _enabled, const char* _runtimeName) +Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, const char* _runtimeName) : BaseBlockData(_begin_time, _descriptor_id) , m_name(_runtimeName) - , m_enabled(_enabled) + , m_enabled(true) { } +void Block::start() +{ + m_begin = getCurrentTime(); +} + +void Block::start(timestamp_t _time) +{ + m_begin = _time; +} + void Block::finish() { m_end = getCurrentTime(); } -void Block::finish(timestamp_t _end_time) +void Block::finish(timestamp_t _time) { - m_end = _end_time; + m_end = _time; } Block::~Block() diff --git a/src/event_trace_win.cpp b/src/event_trace_win.cpp index d60ed94..5550ba1 100644 --- a/src/event_trace_win.cpp +++ b/src/event_trace_win.cpp @@ -43,12 +43,12 @@ namespace profiler { if (sizeof(CSwitch) != _traceEvent->UserDataLength) return; - //EASY_FUNCTION(::profiler::colors::Red); + //EASY_FUNCTION(::profiler::colors::White); auto _contextSwitchEvent = reinterpret_cast(_traceEvent->UserData); const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); - static const auto& desc = MANAGER.addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); + static const auto& desc = MANAGER.addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id()); MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time); } @@ -145,7 +145,6 @@ namespace profiler { m_stubThread = ::std::move(::std::thread([this]() { EASY_THREAD("EasyProfiler.ETW"); - //EASY_BLOCK("ProcessTrace()", ::profiler::colors::Red); ProcessTrace(&m_openedHandle, 1, 0, 0); })); diff --git a/src/hashed_cstr.h b/src/hashed_cstr.h new file mode 100644 index 0000000..0691c34 --- /dev/null +++ b/src/hashed_cstr.h @@ -0,0 +1,229 @@ +/************************************************************************ +* file name : hashed_str.h +* ----------------- : +* creation time : 2016/09/11 +* author : Victor Zarubkin +* email : v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains definition of C-strings with calculated hash-code. +* : These strings may be used as optimized keys for std::unordered_map. +* ----------------- : +* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from reader.cpp +* : +* : * +* ----------------- : +* 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 . +************************************************************************/ + +#ifndef EASY_PROFILER__HASHED_CSTR__H_ +#define EASY_PROFILER__HASHED_CSTR__H_ + +#include +#include +#include + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 + +namespace profiler { + + /** \brief Simple C-string pointer with length. + + It is used as base class for a key in std::unordered_map. + It is used to get better performance than std::string. + It simply stores a pointer and a length, there is no + any memory allocation and copy. + + \warning Make sure you know what you are doing. You have to be sure that + pointed C-string will exist until you finish using this cstring. + + \ingroup profiler + */ + class cstring + { + protected: + + const char* m_str; + size_t m_len; + + public: + + explicit cstring(const char* _str) : m_str(_str), m_len(strlen(_str)) + { + } + + cstring(const cstring&) = default; + cstring& operator = (const cstring&) = default; + + inline bool operator == (const cstring& _other) const + { + return m_len == _other.m_len && !strncmp(m_str, _other.m_str, m_len); + } + + inline bool operator != (const cstring& _other) const + { + return !operator == (_other); + } + + inline bool operator < (const cstring& _other) const + { + if (m_len == _other.m_len) + { + return strncmp(m_str, _other.m_str, m_len) < 0; + } + + return m_len < _other.m_len; + } + + }; // END of class cstring. + + /** \brief cstring with precalculated hash. + + This is used to calculate hash for C-string and to cache it + to be used in the future without recurring hash calculatoin. + + \note This class is used as a key in std::unordered_map. + + \ingroup profiler + */ + class hashed_cstr final : public cstring + { + typedef cstring Parent; + + size_t m_hash; + + public: + + explicit hashed_cstr(const char* _str) : Parent(_str), m_hash(0) + { + m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len); + } + + hashed_cstr(const hashed_cstr&) = default; + hashed_cstr& operator = (const hashed_cstr&) = default; + + inline bool operator == (const hashed_cstr& _other) const + { + return m_hash == _other.m_hash && Parent::operator == (_other); + } + + inline bool operator != (const hashed_cstr& _other) const + { + return !operator == (_other); + } + + inline size_t hcode() const + { + return m_hash; + } + + }; // END of class hashed_cstr. + +} // END of namespace profiler. + +namespace std { + + /** \brief Simply returns precalculated hash of a C-string. */ + template <> struct hash<::profiler::hashed_cstr> { + typedef ::profiler::hashed_cstr argument_type; + typedef size_t result_type; + inline size_t operator () (const ::profiler::hashed_cstr& _str) const { + return _str.hcode(); + } + }; + +} // END of namespace std. + +#else //////////////////////////////////////////////////////////////////// + +// TODO: Create hashed_cstr for Linux (need to use Linux version of std::_Hash_seq) + +#endif + +namespace profiler { + + class hashed_stdstring final + { + ::std::string m_str; + size_t m_hash; + + public: + + hashed_stdstring(const char* _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(const ::std::string& _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(::std::string&& _str) : m_str(::std::forward<::std::string&&>(_str)), m_hash(::std::hash<::std::string>()(m_str)) + { + } + + hashed_stdstring(hashed_stdstring&& _other) : m_str(::std::move(_other.m_str)), m_hash(_other.m_hash) + { + } + + hashed_stdstring(const hashed_stdstring&) = default; + hashed_stdstring& operator = (const hashed_stdstring&) = default; + + hashed_stdstring& operator = (hashed_stdstring&& _other) + { + m_str = ::std::move(_other.m_str); + m_hash = _other.m_hash; + return *this; + } + + inline bool operator == (const hashed_stdstring& _other) const + { + return m_hash == _other.m_hash && m_str == _other.m_str; + } + + inline bool operator != (const hashed_stdstring& _other) const + { + return !operator == (_other); + } + + inline size_t hcode() const + { + return m_hash; + } + + }; // END of class hashed_stdstring. + +} // END of namespace profiler. + +namespace std { + + /** \brief Simply returns precalculated hash of a std::string. */ + template <> struct hash<::profiler::hashed_stdstring> { + typedef ::profiler::hashed_stdstring argument_type; + typedef size_t result_type; + inline size_t operator () (const ::profiler::hashed_stdstring& _str) const { + return _str.hcode(); + } + }; + +} // END of namespace std. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__HASHED_CSTR__H_ diff --git a/src/outstream.h b/src/outstream.h new file mode 100644 index 0000000..ee82b1c --- /dev/null +++ b/src/outstream.h @@ -0,0 +1,75 @@ +/************************************************************************ +* file name : outstream.h +* ----------------- : +* creation time : 2016/09/11 +* authors : Sergey Yagovtsev, Victor Zarubkin +* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com +* ----------------- : +* description : The file contains definition of output stream helpers. +* ----------------- : +* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from profiler_manager.h/.cpp +* : +* : * +* ----------------- : +* 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 . +************************************************************************/ + +#ifndef EASY_PROFILER__OUTPUT_STREAM__H_ +#define EASY_PROFILER__OUTPUT_STREAM__H_ + +#include +#include + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +namespace profiler { + + class OStream final + { + ::std::stringstream m_stream; + + public: + + explicit OStream() : m_stream(std::ios_base::out | std::ios_base::binary) + { + + } + + template void write(const char* _data, T _size) + { + m_stream.write(_data, _size); + } + + template void write(const T& _data) + { + m_stream.write((const char*)&_data, sizeof(T)); + } + + const ::std::stringstream& stream() const + { + return m_stream; + } + + }; // END of class OStream. + +} // END of namespace profiler. + +////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// + +#endif // EASY_PROFILER__OUTPUT_STREAM__H_ diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index 118390c..935e2c7 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -23,14 +23,13 @@ * : You should have received a copy of the GNU General Public License * : along with this program.If not, see . ************************************************************************/ -#include "profile_manager.h" -#include "profiler/serialized_block.h" -#include "event_trace_win.h" +#include #include -#include - #include +#include "profiler/serialized_block.h" +#include "profile_manager.h" +#include "event_trace_win.h" ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -41,14 +40,16 @@ using namespace profiler; extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY; #endif +extern timestamp_t getCurrentTime(); + //auto& MANAGER = ProfileManager::instance(); #define MANAGER ProfileManager::instance() extern "C" { - PROFILER_API const BaseBlockDescriptor& registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) { - return MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color); + return MANAGER.addBlockDescriptor(_enabled, _name, _filename, _line, _block_type, _color); } PROFILER_API void endBlock() @@ -109,44 +110,87 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length) ////////////////////////////////////////////////////////////////////////// -BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, int _line, block_type_t _block_type, color_t _color) +BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, bool _enabled, int _line, block_type_t _block_type, color_t _color) : m_id(_id) , m_line(_line) , m_type(_block_type) , m_color(_color) - , m_enabled(true) + , m_enabled(_enabled) { } -BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) - : BaseBlockDescriptor(_id, _line, _block_type, _color) +BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + : BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color) , m_name(_name) , m_filename(_filename) { _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; } +BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color) + : BaseBlockDescriptor(0, _enabled, _line, _block_type, _color) + , m_name(_name) + , m_filename(_filename) +{ + _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; +} + +void BlockDescriptor::setId(block_id_t _id) +{ + m_id = _id; +} + +BlockDescRef::~BlockDescRef() +{ + MANAGER.markExpired(m_desc.id()); +} + ////////////////////////////////////////////////////////////////////////// void ThreadStorage::storeBlock(const profiler::Block& block) { +#if EASY_MEASURE_STORAGE_EXPAND != 0 + static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White); +#endif + auto name_length = static_cast(strlen(block.name())); auto size = static_cast(sizeof(BaseBlockData) + name_length + 1); - auto data = blocks.alloc.allocate(size); - ::new (static_cast(data)) SerializedBlock(block, name_length); + +#if EASY_MEASURE_STORAGE_EXPAND != 0 + const bool expanded = desc.enabled() && blocks.closedList.need_expand(size); + profiler::Block b(0ULL, desc.id(), ""); + 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); blocks.usedMemorySize += size; - blocks.closedList.emplace_back(reinterpret_cast(data)); + +#if EASY_MEASURE_STORAGE_EXPAND != 0 + if (expanded) + { + name_length = 0; + size = static_cast(sizeof(BaseBlockData) + 1); + data = blocks.closedList.allocate(size); + ::new (data) SerializedBlock(b, name_length); + blocks.usedMemorySize += size; + } +#endif } void ThreadStorage::storeCSwitch(const profiler::Block& block) { auto name_length = static_cast(strlen(block.name())); auto size = static_cast(sizeof(BaseBlockData) + name_length + 1); - auto data = sync.alloc.allocate(size); - ::new (static_cast(data)) SerializedBlock(block, name_length); + auto data = sync.closedList.allocate(size); + ::new (data) SerializedBlock(block, name_length); sync.usedMemorySize += size; - sync.closedList.emplace_back(reinterpret_cast(data)); } void ThreadStorage::clearClosed() @@ -159,25 +203,9 @@ void ThreadStorage::clearClosed() EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; -// #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 - ProfileManager::ProfileManager() { -// #ifdef _WIN32 -// PREVIOUS_FILTER = SetUnhandledExceptionFilter(easyTopLevelExceptionFilter); -// #endif + m_expiredDescriptors.reserve(1024U); } ProfileManager::~ProfileManager() @@ -197,6 +225,28 @@ ProfileManager& ProfileManager::instance() 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_expiredDescriptors.push_back(_id); +} + +ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id) +{ + guard_lock_t lock(m_spin); + return m_threads[_thread_id]; +} + +ThreadStorage* ProfileManager::findThreadStorage(profiler::thread_id_t _thread_id) +{ + guard_lock_t lock(m_spin); + auto it = m_threads.find(_thread_id); + return it != m_threads.end() ? &it->second : nullptr; +} + void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName) { if (!m_isEnabled || !_desc.enabled()) @@ -226,7 +276,7 @@ void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profil { auto ts = findThreadStorage(_thread_id); if (ts != nullptr) - ts->sync.openedList.emplace(_time, _id, true, ""); + ts->sync.openedList.emplace(_time, _id, ""); } void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id) @@ -234,7 +284,7 @@ void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profil auto ts = findThreadStorage(_thread_id); if (ts != nullptr) { - profiler::Block b(_time, _id, true, ""); + profiler::Block b(_time, _id, ""); b.finish(_time); ts->storeCSwitch(b); } @@ -279,39 +329,16 @@ void ProfileManager::setEnabled(bool isEnable) ////////////////////////////////////////////////////////////////////////// -class FileWriter final -{ - std::ofstream m_file; - -public: - - explicit FileWriter(const char* _filename) : m_file(_filename, std::fstream::binary) { } - - template void write(const char* _data, T _size) { - m_file.write(_data, _size); - } - - template void write(const T& _data) { - m_file.write((const char*)&_data, sizeof(T)); - } - - void writeBlock(const profiler::SerializedBlock* _block) - { - auto sz = static_cast(sizeof(BaseBlockData) + strlen(_block->name()) + 1); - write(sz); - write(_block->data(), sz); - } -}; - -////////////////////////////////////////////////////////////////////////// - -uint32_t ProfileManager::dumpBlocksToFile(const char* filename) +uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) { const bool wasEnabled = m_isEnabled; if (wasEnabled) ::profiler::setEnabled(false); + #ifndef _WIN32 + // Read thread context switch events from temporary file + uint64_t timestamp; uint32_t thread_from, thread_to; @@ -319,19 +346,16 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename) if(infile.is_open()) { - static const auto desc = addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); + static const auto& desc = addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, profiler::BLOCK_TYPE_CONTEXT_SWITCH, profiler::colors::White); while (infile >> timestamp >> thread_from >> thread_to) { - beginContextSwitch(thread_from, timestamp, desc); + beginContextSwitch(thread_from, timestamp, desc.id()); endContextSwitch(thread_to, timestamp); } } - #endif - - FileWriter of(filename); - + // Calculate used memory total size and total blocks number uint64_t usedMemorySize = 0; uint32_t blocks_number = 0; for (const auto& thread_storage : m_threads) @@ -341,53 +365,80 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename) blocks_number += static_cast(t.blocks.closedList.size()) + static_cast(t.sync.closedList.size()); } + // Write CPU frequency to let GUI calculate real time value from CPU clocks #ifdef _WIN32 - of.write(CPU_FREQUENCY); + _outputStream.write(CPU_FREQUENCY); #else - of.write(0LL); + _outputStream.write(0LL); #endif - of.write(blocks_number); - of.write(usedMemorySize); - of.write(static_cast(m_descriptors.size())); - of.write(m_usedMemorySize); + // Write blocks number and used memory size + _outputStream.write(blocks_number); + _outputStream.write(usedMemorySize); + _outputStream.write(static_cast(m_descriptors.size())); + _outputStream.write(m_usedMemorySize); + // Write block descriptors for (const auto descriptor : m_descriptors) { const auto name_size = static_cast(strlen(descriptor->name()) + 1); const auto filename_size = static_cast(strlen(descriptor->file()) + 1); const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); - of.write(size); - of.write(*descriptor); - of.write(name_size); - of.write(descriptor->name(), name_size); - of.write(descriptor->file(), filename_size); + _outputStream.write(size); + _outputStream.write(*descriptor); + _outputStream.write(name_size); + _outputStream.write(descriptor->name(), name_size); + _outputStream.write(descriptor->file(), filename_size); } + // Write blocks and context switch events for each thread for (auto& thread_storage : m_threads) { auto& t = thread_storage.second; - of.write(thread_storage.first); + _outputStream.write(thread_storage.first); - of.write(static_cast(t.sync.closedList.size())); - for (auto b : t.sync.closedList) - of.writeBlock(b); + _outputStream.write(t.sync.closedList.size()); + t.sync.closedList.serialize(_outputStream); - of.write(static_cast(t.blocks.closedList.size())); - for (auto b : t.blocks.closedList) - of.writeBlock(b); + _outputStream.write(t.blocks.closedList.size()); + t.blocks.closedList.serialize(_outputStream); t.clearClosed(); } -// if (wasEnabled) -// ::profiler::setEnabled(true); + if (!m_expiredDescriptors.empty()) + { + // Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution) + + std::sort(m_expiredDescriptors.begin(), m_expiredDescriptors.end()); + for (auto it = m_expiredDescriptors.rbegin(), rend = m_expiredDescriptors.rend(); it != rend; ++it) + { + auto id = *it; + delete m_descriptors[id]; + m_descriptors.erase(m_descriptors.begin() + id); + } + m_expiredDescriptors.clear(); + } + + //if (wasEnabled) + // ::profiler::setEnabled(true); return blocks_number; } +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; +} + const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line) { if (THREAD_STORAGE == nullptr) @@ -397,7 +448,7 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename if (!THREAD_STORAGE->named) { - const auto& desc = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black); + const auto& desc = addBlockDescriptor(true, _funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black); profiler::Block b(desc, name); b.finish(b.begin()); diff --git a/src/profile_manager.h b/src/profile_manager.h index 2b54726..9e40153 100644 --- a/src/profile_manager.h +++ b/src/profile_manager.h @@ -16,14 +16,13 @@ You should have received a copy of the GNU General Public License along with this program.If not, see . **/ -#ifndef ___PROFILER____MANAGER____H______ -#define ___PROFILER____MANAGER____H______ +#ifndef EASY_PROFILER____MANAGER____H______ +#define EASY_PROFILER____MANAGER____H______ #include "profiler/profiler.h" - #include "spin_lock.h" - -#include +#include "outstream.h" +//#include "hashed_cstr.h" #include #include #include @@ -56,46 +55,163 @@ namespace profiler { class SerializedBlock; } ////////////////////////////////////////////////////////////////////////// -template +//#define EASY_ENABLE_ALIGNMENT + +#ifndef EASY_ENABLE_ALIGNMENT +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR +# define EASY_MALLOC(MEMSIZE, A) malloc(MEMSIZE) +# define EASY_FREE(MEMPTR) free(MEMPTR) +#else +# if defined(_WIN32) +# define EASY_ALIGNED(TYPE, VAR, A) __declspec(align(A)) TYPE VAR +# define EASY_MALLOC(MEMSIZE, A) _aligned_malloc(MEMSIZE, A) +# define EASY_FREE(MEMPTR) _aligned_free(MEMPTR) +# elif defined(__GNUC__) +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR __attribute__(aligned(A)) +# else +# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR +# endif +#endif + +template class chunk_allocator final { - struct chunk { T data[N]; }; + struct chunk { EASY_ALIGNED(int8_t, data[N], 64); chunk* prev = nullptr; }; - std::list m_chunks; - uint16_t m_size; + struct chunk_list + { + chunk* last = nullptr; + + ~chunk_list() + { + clear(); + } + + void clear() + { + do { + auto p = last; + last = last->prev; + EASY_FREE(p); + } while (last != nullptr); + } + + chunk& back() + { + return *last; + } + + void emplace_back() + { + auto prev = last; + last = ::new (EASY_MALLOC(sizeof(chunk), 64)) chunk(); + last->prev = prev; + *(uint16_t*)last->data = 0; + } + + void invert() + { + chunk* next = nullptr; + + while (last->prev != nullptr) { + auto p = last->prev; + last->prev = next; + next = last; + last = p; + } + + last->prev = next; + } + }; + + //typedef std::list chunk_list; + + chunk_list m_chunks; + uint32_t m_size; + uint16_t m_shift; public: - chunk_allocator() : m_size(0) + chunk_allocator() : m_size(0), m_shift(0) { m_chunks.emplace_back(); } - T* allocate(uint16_t n) + void* allocate(uint16_t n) { - if (m_size + n <= N) + ++m_size; + + if (!need_expand(n)) { - T* data = m_chunks.back().data + m_size; - m_size += n; + int8_t* data = m_chunks.back().data + m_shift; + m_shift += n + sizeof(uint16_t); + + *(uint16_t*)data = n; + data = data + sizeof(uint16_t); + + if (m_shift < N) + *(uint16_t*)(data + n) = 0; + return data; } - m_size = n; + m_shift = n + sizeof(uint16_t); m_chunks.emplace_back(); - return m_chunks.back().data; + auto data = m_chunks.back().data; + + *(uint16_t*)data = n; + data = data + sizeof(uint16_t); + + *(uint16_t*)(data + n) = 0; + return data; + } + + inline bool need_expand(uint16_t n) const + { + return (m_shift + n + sizeof(uint16_t)) > N; + } + + inline uint32_t size() const + { + return m_size; } void clear() { m_size = 0; + m_shift = 0; m_chunks.clear(); m_chunks.emplace_back(); } + + /** Serialize data to stream. + + \warning Data will be cleared after serialization. + */ + void serialize(profiler::OStream& _outputStream) + { + m_chunks.invert(); + + auto current = m_chunks.last; + do { + const int8_t* data = current->data; + uint16_t i = 0; + do { + const uint16_t size = sizeof(uint16_t) + *(uint16_t*)data; + _outputStream.write((const char*)data, size); + data = data + size; + i += size; + } while (i < N && *(uint16_t*)data != 0); + current = current->prev; + } while (current != nullptr); + + clear(); + } }; ////////////////////////////////////////////////////////////////////////// -const uint16_t SIZEOF_CSWITCH = sizeof(profiler::BaseBlockData) + 1; +const uint16_t SIZEOF_CSWITCH = sizeof(profiler::BaseBlockData) + 1 + sizeof(uint16_t); typedef std::vector serialized_list_t; @@ -139,14 +255,12 @@ struct BlocksList final } }; - chunk_allocator alloc; Stack openedList; - serialized_list_t closedList; + chunk_allocator closedList; uint64_t usedMemorySize = 0; void clearClosed() { - serialized_list_t().swap(closedList); - alloc.clear(); + //closedList.clear(); usedMemorySize = 0; } }; @@ -156,8 +270,8 @@ class ThreadStorage final { public: - BlocksList, SIZEOF_CSWITCH * (uint16_t)1024U> blocks; - BlocksList sync; + BlocksList, SIZEOF_CSWITCH * (uint16_t)128U> blocks; + BlocksList sync; std::string name; bool named = false; @@ -172,6 +286,8 @@ public: class ProfileManager final { + friend profiler::BlockDescRef; + ProfileManager(); ProfileManager(const ProfileManager& p) = delete; ProfileManager& operator=(const ProfileManager&) = delete; @@ -179,28 +295,36 @@ class ProfileManager final typedef profiler::guard_lock guard_lock_t; typedef std::map map_of_threads_stacks; typedef std::vector block_descriptors_t; + typedef std::vector expired_ids_t; - map_of_threads_stacks m_threads; - block_descriptors_t m_descriptors; - uint64_t m_usedMemorySize = 0; - profiler::spin_lock m_spin; - profiler::spin_lock m_storedSpin; - bool m_isEnabled = false; + map_of_threads_stacks m_threads; + block_descriptors_t m_descriptors; + expired_ids_t m_expiredDescriptors; + uint64_t m_usedMemorySize = 0; + profiler::spin_lock m_spin; + profiler::spin_lock m_storedSpin; + profiler::block_id_t m_idCounter = 0; + bool m_isEnabled = false; std::string m_csInfoFilename = "/tmp/cs_profiling_info.log"; + uint32_t dumpBlocksToStream(profiler::OStream& _outputStream); + public: static ProfileManager& instance(); ~ProfileManager(); template - const profiler::BaseBlockDescriptor& addBlockDescriptor(TArgs ... _args) + const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, TArgs ... _args) { + auto desc = new profiler::BlockDescriptor(m_usedMemorySize, _enabledByDefault, _args...); + guard_lock_t lock(m_storedSpin); - const auto id = static_cast(m_descriptors.size()); - m_descriptors.emplace_back(new profiler::BlockDescriptor(m_usedMemorySize, id, _args...)); - return *m_descriptors.back(); + desc->setId(m_idCounter++); + m_descriptors.emplace_back(desc); + + return *desc; } void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName); @@ -226,18 +350,9 @@ public: private: - ThreadStorage& threadStorage(profiler::thread_id_t _thread_id) - { - guard_lock_t lock(m_spin); - return m_threads[_thread_id]; - } - - ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id) - { - guard_lock_t lock(m_spin); - auto it = m_threads.find(_thread_id); - return it != m_threads.end() ? &it->second : nullptr; - } + void markExpired(profiler::block_id_t _id); + ThreadStorage& threadStorage(profiler::thread_id_t _thread_id); + ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id); }; -#endif +#endif // EASY_PROFILER____MANAGER____H______ diff --git a/src/reader.cpp b/src/reader.cpp index da5abc7..44c7c94 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -43,6 +43,7 @@ ************************************************************************/ #include "profiler/reader.h" +#include "hashed_cstr.h" #include #include #include @@ -89,111 +90,17 @@ namespace profiler { #ifdef _WIN32 -/** \brief Simple C-string pointer with length. - -It is used as base class for a key in std::unordered_map. -It is used to get better performance than std::string. -It simply stores a pointer and a length, there is no -any memory allocation and copy. - -\note It is absolutely safe to store pointer because std::unordered_map, -which uses it as a key, exists only inside fillTreesFromFile function. - -*/ -class cstring -{ -protected: - - const char* str; - size_t str_len; - -public: - - explicit cstring(const char* _str) : str(_str), str_len(strlen(_str)) - { - } - - cstring(const cstring& _other) : str(_other.str), str_len(_other.str_len) - { - } - - inline bool operator == (const cstring& _other) const - { - return str_len == _other.str_len && !strncmp(str, _other.str, str_len); - } - - inline bool operator != (const cstring& _other) const - { - return !operator == (_other); - } - - inline bool operator < (const cstring& _other) const - { - if (str_len == _other.str_len) - { - return strncmp(str, _other.str, str_len) < 0; - } - - return str_len < _other.str_len; - } -}; - -/** \brief cstring with precalculated hash. - -This is used to calculate hash for C-string and to cache it -to be used in the future without recurring hash calculatoin. - -\note This class is used as a key in std::unordered_map. - -*/ -class hashed_cstr : public cstring -{ - typedef cstring Parent; - -public: - - size_t str_hash; - - explicit hashed_cstr(const char* _str) : Parent(_str), str_hash(0) - { - str_hash = ::std::_Hash_seq((const unsigned char *)str, str_len); - } - - hashed_cstr(const hashed_cstr& _other) : Parent(_other), str_hash(_other.str_hash) - { - } - - inline bool operator == (const hashed_cstr& _other) const - { - return str_hash == _other.str_hash && Parent::operator == (_other); - } - - inline bool operator != (const hashed_cstr& _other) const - { - return !operator == (_other); - } -}; - -namespace std { - - /** \brief Simply returns precalculated hash of a C-string. */ - template <> struct hash { - inline size_t operator () (const hashed_cstr& _str) const { - return _str.str_hash; - } - }; - -} - typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; -typedef ::std::unordered_map IdMap; + +/** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map, +which uses it as a key, exists only inside fillTreesFromFile function. */ +typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t> IdMap; #else -// TODO: optimize for Linux too -#include +// TODO: Create optimized version of profiler::hashed_cstr for Linux too. typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; -typedef ::std::unordered_map<::std::string, ::profiler::block_id_t> IdMap; +typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap; #endif