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

(profiler_core) Further work with real-time enable/disable of blocks;

(profiler_core) Optimized storeBlock() algorithm, removed unnecessary std::vector;
(profiler_core) Writing to std::stringstream and then to file (prepare for streaming over WAN/LAN);
(profiler_core) Moved hached_cstr into separate header.
This commit is contained in:
Victor Zarubkin 2016-09-11 16:57:35 +03:00
parent c67bf1dda7
commit d5d75d6340
10 changed files with 950 additions and 361 deletions

View File

@ -19,67 +19,36 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#ifndef EASY_PROFILER____H_______ #ifndef EASY_PROFILER____H_______
#define EASY_PROFILER____H_______ #define EASY_PROFILER____H_______
#ifdef _WIN32 #include "profiler/profiler_aux.h"
# 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
#if defined ( __clang__ ) #if defined ( __clang__ )
#pragma clang diagnostic push # pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif #endif
#ifndef FULL_DISABLE_PROFILER #ifndef FULL_DISABLE_PROFILER
#include <type_traits>
#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 \defgroup profiler Profiler
*/ */
namespace profiler { /** If != 0 then EasyProfiler will measure time for blocks storage expansion.
template <const bool IS_REF> struct NameSwitch final { If 0 then EasyProfiler will be compiled without blocks of code responsible
static const char* runtime_name(const char* name) { return name; } for measuring these events.
static const char* compiletime_name(const char*) { return ""; }
};
template <> struct NameSwitch<true> final { These are "EasyProfiler.ExpandStorage" blocks on a diagram.
static const char* runtime_name(const char*) { return ""; }
static const char* compiletime_name(const char* name) { return name; }
};
} // END of namespace profiler.
#define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::compiletime_name(name) \ingroup profiler
#define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::runtime_name(name) */
# 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. /** Macro of beginning of block with custom name and color.
@ -88,6 +57,8 @@ namespace profiler {
void foo() void foo()
{ {
// some code ... // some code ...
EASY_BLOCK("Check something", profiler::DISABLED); // Disabled block (There is possibility to enable this block later via GUI)
if(something){ if(something){
EASY_BLOCK("Calling bar()"); // Block with default color EASY_BLOCK("Calling bar()"); // Block with default color
bar(); bar();
@ -96,6 +67,10 @@ namespace profiler {
EASY_BLOCK("Calling baz()", profiler::colors::Red); // Red block EASY_BLOCK("Calling baz()", profiler::colors::Red); // Red block
baz(); 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 \endcode
@ -103,9 +78,9 @@ Block will be automatically completed by destructor.
\ingroup profiler \ingroup profiler
*/ */
#define EASY_BLOCK(name, ...)\ # define EASY_BLOCK(name, ...)\
static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
::profiler::BLOCK_TYPE_BLOCK , ## __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::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 ::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" EASY_FUNCTION(profiler::colors::Green); // Green block with name="bar"
//some code... //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 \endcode
Name of the block automatically created with function name. Name of the block automatically created with function name.
\ingroup profiler \ingroup profiler
*/ */
#define EASY_FUNCTION(...)\ # define EASY_FUNCTION(...)\
static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
::profiler::BLOCK_TYPE_BLOCK , ## __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::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
@ -157,7 +137,7 @@ int foo()
\ingroup profiler \ingroup profiler
*/ */
#define EASY_END_BLOCK ::profiler::endBlock(); # define EASY_END_BLOCK ::profiler::endBlock();
/** Macro of creating event with custom name and color. /** Macro of creating event with custom name and color.
@ -168,20 +148,23 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
\ingroup profiler \ingroup profiler
*/ */
#define EASY_EVENT(name, ...)\ # define EASY_EVENT(name, ...)\
static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = \ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = \
::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\ ::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)); ::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));
/** Macro enabling profiler /** Macro enabling profiler
\ingroup profiler \ingroup profiler
*/ */
#define EASY_PROFILER_ENABLE ::profiler::setEnabled(true); # define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
/** Macro disabling profiler /** Macro disabling profiler
\ingroup profiler \ingroup profiler
*/ */
#define EASY_PROFILER_DISABLE ::profiler::setEnabled(false); # define EASY_PROFILER_DISABLE ::profiler::setEnabled(false);
/** Macro of naming current thread. /** Macro of naming current thread.
@ -189,7 +172,7 @@ If this thread has been already named then nothing changes.
\ingroup profiler \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;\ 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)\ if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__); 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 \ingroup profiler
*/ */
#define EASY_MAIN_THREAD EASY_THREAD("Main") # define EASY_MAIN_THREAD EASY_THREAD("Main")
#else #else
#define EASY_BLOCK(...) # define EASY_MEASURE_STORAGE_EXPAND 0
#define EASY_FUNCTION(...) # define EASY_STORAGE_EXPAND_ENABLED false
#define EASY_END_BLOCK # define EASY_BLOCK(...)
#define EASY_PROFILER_ENABLE # define EASY_FUNCTION(...)
#define EASY_PROFILER_DISABLE # define EASY_END_BLOCK
#define EASY_EVENT(...) # define EASY_PROFILER_ENABLE
#define EASY_THREAD(...) # define EASY_PROFILER_DISABLE
#define EASY_MAIN_THREAD # define EASY_EVENT(...)
# define EASY_THREAD(...)
# define EASY_MAIN_THREAD
#endif #endif
#include <stdint.h> //////////////////////////////////////////////////////////////////////////
#include <cstddef> //////////////////////////////////////////////////////////////////////////
#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 ProfileManager;
class ThreadStorage;
namespace profiler { namespace profiler {
class Block; //////////////////////////////////////////////////////////////////////
class BaseBlockDescriptor; // Core types
typedef uint64_t timestamp_t; typedef uint64_t timestamp_t;
typedef uint32_t thread_id_t; typedef uint32_t thread_id_t;
@ -249,17 +224,7 @@ namespace profiler {
}; };
typedef BlockType block_type_t; 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) #pragma pack(push,1)
class PROFILER_API BaseBlockDescriptor class PROFILER_API BaseBlockDescriptor
@ -274,7 +239,7 @@ namespace profiler {
block_type_t m_type; ///< Type of the block (See BlockType) 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 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: public:
@ -283,22 +248,10 @@ namespace profiler {
inline color_t color() const { return m_color; } inline color_t color() const { return m_color; }
inline block_type_t type() const { return m_type; } inline block_type_t type() const { return m_type; }
inline bool enabled() const { return m_enabled; } inline bool enabled() const { return m_enabled; }
};
class PROFILER_API BlockDescriptor final : public BaseBlockDescriptor }; // END of class 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
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 class PROFILER_API BaseBlockData
{ {
@ -325,20 +278,52 @@ namespace profiler {
private: private:
BaseBlockData() = delete; BaseBlockData() = delete;
};
}; // END of class BaseBlockData.
#pragma pack(pop) #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 class PROFILER_API Block final : public BaseBlockData
{ {
friend ::ProfileManager; friend ::ProfileManager;
friend ::ThreadStorage;
const char* m_name; const char* m_name;
bool m_enabled; bool m_enabled;
private: private:
void start();
void start(timestamp_t _time);
void finish(); void finish();
void finish(timestamp_t _end_time); void finish(timestamp_t _time);
inline bool finished() const { return m_end >= m_begin; } inline bool finished() const { return m_end >= m_begin; }
inline bool enabled() const { return m_enabled; } inline bool enabled() const { return m_enabled; }
@ -346,18 +331,58 @@ namespace profiler {
Block(Block&& that); 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, bool _enabled, const char* _runtimeName); Block(timestamp_t _begin_time, block_id_t _id, const char* _runtimeName);
~Block(); ~Block();
inline const char* name() const { return m_name; } 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. } // END of namespace profiler.
#if defined ( __clang__ ) #if defined ( __clang__ )
#pragma clang diagnostic pop # pragma clang diagnostic pop
#endif #endif
#endif // EASY_PROFILER____H_______ #endif // EASY_PROFILER____H_______

View File

@ -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 <http://www.gnu.org/licenses/>.
************************************************************************/
#ifndef EASY_PROFILER__AUX__H_______
#define EASY_PROFILER__AUX__H_______
#include <stdint.h>
#include <cstddef>
#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 <type_traits>
# 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 <const bool IS_REF> 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<true> 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 <class ... TArgs>
inline color_t extract_color(::profiler::EasyEnableFlag, TArgs...) {
return ::profiler::colors::Default;
}
template <class ... TArgs>
inline color_t extract_color(color_t _color, TArgs...) {
return _color;
}
template <class T, class ... TArgs>
inline color_t extract_color(T, color_t _color, TArgs...) {
return _color;
}
template <class ... TArgs>
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 <class T, class ... TArgs>
inline bool extract_enable_flag(T, ::profiler::EasyEnableFlag _flag, TArgs...) {
return _flag == ::profiler::ENABLED;
}
template <class ... TArgs>
inline bool extract_enable_flag(::profiler::EasyEnableFlag _flag, TArgs...) {
return _flag == ::profiler::ENABLED;
}
template <class ... TArgs>
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<decltype(name)>::value>::compiletime_name(name, EASY_UNIQUE_LINE_ID)
# define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::runtime_name(name)
#endif // FULL_DISABLE_PROFILER
//////////////////////////////////////////////////////////////////////////
#endif // EASY_PROFILER__AUX__H_______

View File

@ -105,7 +105,7 @@ void prepareRender(){
int multPhys(int i) int multPhys(int i)
{ {
EASY_FUNCTION(profiler::colors::Red700); EASY_FUNCTION(profiler::colors::Red700, profiler::DISABLED);
return i * i * i * i / 100; return i * i * i * i / 100;
} }

View File

@ -68,25 +68,37 @@ Block::Block(Block&& that)
} }
Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName) 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) : BaseBlockData(_begin_time, _descriptor_id)
, m_name(_runtimeName) , 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() void Block::finish()
{ {
m_end = getCurrentTime(); 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() Block::~Block()

View File

@ -43,12 +43,12 @@ namespace profiler {
if (sizeof(CSwitch) != _traceEvent->UserDataLength) if (sizeof(CSwitch) != _traceEvent->UserDataLength)
return; return;
//EASY_FUNCTION(::profiler::colors::Red); //EASY_FUNCTION(::profiler::colors::White);
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData); auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); 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.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id());
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time); MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
} }
@ -145,7 +145,6 @@ namespace profiler {
m_stubThread = ::std::move(::std::thread([this]() m_stubThread = ::std::move(::std::thread([this]()
{ {
EASY_THREAD("EasyProfiler.ETW"); EASY_THREAD("EasyProfiler.ETW");
//EASY_BLOCK("ProcessTrace()", ::profiler::colors::Red);
ProcessTrace(&m_openedHandle, 1, 0, 0); ProcessTrace(&m_openedHandle, 1, 0, 0);
})); }));

229
src/hashed_cstr.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
************************************************************************/
#ifndef EASY_PROFILER__HASHED_CSTR__H_
#define EASY_PROFILER__HASHED_CSTR__H_
#include <functional>
#include <string.h>
#include <string>
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#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_

75
src/outstream.h Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
************************************************************************/
#ifndef EASY_PROFILER__OUTPUT_STREAM__H_
#define EASY_PROFILER__OUTPUT_STREAM__H_
#include <sstream>
#include <string.h>
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
namespace profiler {
class OStream final
{
::std::stringstream m_stream;
public:
explicit OStream() : m_stream(std::ios_base::out | std::ios_base::binary)
{
}
template <typename T> void write(const char* _data, T _size)
{
m_stream.write(_data, _size);
}
template <class T> 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_

View File

@ -23,14 +23,13 @@
* : You should have received a copy of the GNU General Public License * : You should have received a copy of the GNU General Public License
* : along with this program.If not, see <http://www.gnu.org/licenses/>. * : along with this program.If not, see <http://www.gnu.org/licenses/>.
************************************************************************/ ************************************************************************/
#include "profile_manager.h"
#include "profiler/serialized_block.h"
#include "event_trace_win.h"
#include <algorithm>
#include <thread> #include <thread>
#include <string.h>
#include <fstream> #include <fstream>
#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; extern decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY;
#endif #endif
extern timestamp_t getCurrentTime();
//auto& MANAGER = ProfileManager::instance(); //auto& MANAGER = ProfileManager::instance();
#define MANAGER ProfileManager::instance() #define MANAGER ProfileManager::instance()
extern "C" { 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() 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_id(_id)
, m_line(_line) , m_line(_line)
, m_type(_block_type) , m_type(_block_type)
, m_color(_color) , 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) 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, _line, _block_type, _color) : BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color)
, m_name(_name) , m_name(_name)
, m_filename(_filename) , m_filename(_filename)
{ {
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; _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) 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<uint16_t>(strlen(block.name())); auto name_length = static_cast<uint16_t>(strlen(block.name()));
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1); auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
auto data = blocks.alloc.allocate(size);
::new (static_cast<void*>(data)) SerializedBlock(block, name_length); #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.usedMemorySize += size;
blocks.closedList.emplace_back(reinterpret_cast<SerializedBlock*>(data));
#if EASY_MEASURE_STORAGE_EXPAND != 0
if (expanded)
{
name_length = 0;
size = static_cast<uint16_t>(sizeof(BaseBlockData) + 1);
data = blocks.closedList.allocate(size);
::new (data) SerializedBlock(b, name_length);
blocks.usedMemorySize += size;
}
#endif
} }
void ThreadStorage::storeCSwitch(const profiler::Block& block) void ThreadStorage::storeCSwitch(const profiler::Block& block)
{ {
auto name_length = static_cast<uint16_t>(strlen(block.name())); auto name_length = static_cast<uint16_t>(strlen(block.name()));
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1); auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
auto data = sync.alloc.allocate(size); auto data = sync.closedList.allocate(size);
::new (static_cast<void*>(data)) SerializedBlock(block, name_length); ::new (data) SerializedBlock(block, name_length);
sync.usedMemorySize += size; sync.usedMemorySize += size;
sync.closedList.emplace_back(reinterpret_cast<SerializedBlock*>(data));
} }
void ThreadStorage::clearClosed() void ThreadStorage::clearClosed()
@ -159,25 +203,9 @@ void ThreadStorage::clearClosed()
EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr; 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() ProfileManager::ProfileManager()
{ {
// #ifdef _WIN32 m_expiredDescriptors.reserve(1024U);
// PREVIOUS_FILTER = SetUnhandledExceptionFilter(easyTopLevelExceptionFilter);
// #endif
} }
ProfileManager::~ProfileManager() ProfileManager::~ProfileManager()
@ -197,6 +225,28 @@ ProfileManager& ProfileManager::instance()
return 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_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) void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName)
{ {
if (!m_isEnabled || !_desc.enabled()) if (!m_isEnabled || !_desc.enabled())
@ -226,7 +276,7 @@ void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profil
{ {
auto ts = findThreadStorage(_thread_id); auto ts = findThreadStorage(_thread_id);
if (ts != nullptr) 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) 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); auto ts = findThreadStorage(_thread_id);
if (ts != nullptr) if (ts != nullptr)
{ {
profiler::Block b(_time, _id, true, ""); profiler::Block b(_time, _id, "");
b.finish(_time); b.finish(_time);
ts->storeCSwitch(b); ts->storeCSwitch(b);
} }
@ -279,39 +329,16 @@ void ProfileManager::setEnabled(bool isEnable)
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
class FileWriter final uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
{
std::ofstream m_file;
public:
explicit FileWriter(const char* _filename) : m_file(_filename, std::fstream::binary) { }
template <typename T> void write(const char* _data, T _size) {
m_file.write(_data, _size);
}
template <class T> void write(const T& _data) {
m_file.write((const char*)&_data, sizeof(T));
}
void writeBlock(const profiler::SerializedBlock* _block)
{
auto sz = static_cast<uint16_t>(sizeof(BaseBlockData) + strlen(_block->name()) + 1);
write(sz);
write(_block->data(), sz);
}
};
//////////////////////////////////////////////////////////////////////////
uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
{ {
const bool wasEnabled = m_isEnabled; const bool wasEnabled = m_isEnabled;
if (wasEnabled) if (wasEnabled)
::profiler::setEnabled(false); ::profiler::setEnabled(false);
#ifndef _WIN32 #ifndef _WIN32
// Read thread context switch events from temporary file
uint64_t timestamp; uint64_t timestamp;
uint32_t thread_from, thread_to; uint32_t thread_from, thread_to;
@ -319,19 +346,16 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
if(infile.is_open()) 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) while (infile >> timestamp >> thread_from >> thread_to)
{ {
beginContextSwitch(thread_from, timestamp, desc); beginContextSwitch(thread_from, timestamp, desc.id());
endContextSwitch(thread_to, timestamp); endContextSwitch(thread_to, timestamp);
} }
} }
#endif #endif
// Calculate used memory total size and total blocks number
FileWriter of(filename);
uint64_t usedMemorySize = 0; uint64_t usedMemorySize = 0;
uint32_t blocks_number = 0; uint32_t blocks_number = 0;
for (const auto& thread_storage : m_threads) for (const auto& thread_storage : m_threads)
@ -341,53 +365,80 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
blocks_number += static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size()); blocks_number += static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
} }
// Write CPU frequency to let GUI calculate real time value from CPU clocks
#ifdef _WIN32 #ifdef _WIN32
of.write(CPU_FREQUENCY); _outputStream.write(CPU_FREQUENCY);
#else #else
of.write(0LL); _outputStream.write(0LL);
#endif #endif
of.write(blocks_number); // Write blocks number and used memory size
of.write(usedMemorySize); _outputStream.write(blocks_number);
of.write(static_cast<uint32_t>(m_descriptors.size())); _outputStream.write(usedMemorySize);
of.write(m_usedMemorySize); _outputStream.write(static_cast<uint32_t>(m_descriptors.size()));
_outputStream.write(m_usedMemorySize);
// Write block descriptors
for (const auto descriptor : m_descriptors) for (const auto descriptor : m_descriptors)
{ {
const auto name_size = static_cast<uint16_t>(strlen(descriptor->name()) + 1); 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 filename_size = static_cast<uint16_t>(strlen(descriptor->file()) + 1);
const auto size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); const auto size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size);
of.write(size); _outputStream.write(size);
of.write<profiler::BaseBlockDescriptor>(*descriptor); _outputStream.write<profiler::BaseBlockDescriptor>(*descriptor);
of.write(name_size); _outputStream.write(name_size);
of.write(descriptor->name(), name_size); _outputStream.write(descriptor->name(), name_size);
of.write(descriptor->file(), filename_size); _outputStream.write(descriptor->file(), filename_size);
} }
// Write blocks and context switch events for each thread
for (auto& thread_storage : m_threads) for (auto& thread_storage : m_threads)
{ {
auto& t = thread_storage.second; auto& t = thread_storage.second;
of.write(thread_storage.first); _outputStream.write(thread_storage.first);
of.write(static_cast<uint32_t>(t.sync.closedList.size())); _outputStream.write(t.sync.closedList.size());
for (auto b : t.sync.closedList) t.sync.closedList.serialize(_outputStream);
of.writeBlock(b);
of.write(static_cast<uint32_t>(t.blocks.closedList.size())); _outputStream.write(t.blocks.closedList.size());
for (auto b : t.blocks.closedList) t.blocks.closedList.serialize(_outputStream);
of.writeBlock(b);
t.clearClosed(); t.clearClosed();
} }
// if (wasEnabled) if (!m_expiredDescriptors.empty())
// ::profiler::setEnabled(true); {
// 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; 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) const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
{ {
if (THREAD_STORAGE == nullptr) if (THREAD_STORAGE == nullptr)
@ -397,7 +448,7 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename
if (!THREAD_STORAGE->named) 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); profiler::Block b(desc, name);
b.finish(b.begin()); b.finish(b.begin());

View File

@ -16,14 +16,13 @@ You should have received a copy of the GNU General Public License
along with this program.If not, see <http://www.gnu.org/licenses/>. along with this program.If not, see <http://www.gnu.org/licenses/>.
**/ **/
#ifndef ___PROFILER____MANAGER____H______ #ifndef EASY_PROFILER____MANAGER____H______
#define ___PROFILER____MANAGER____H______ #define EASY_PROFILER____MANAGER____H______
#include "profiler/profiler.h" #include "profiler/profiler.h"
#include "spin_lock.h" #include "spin_lock.h"
#include "outstream.h"
#include <stack> //#include "hashed_cstr.h"
#include <map> #include <map>
#include <list> #include <list>
#include <vector> #include <vector>
@ -56,46 +55,163 @@ namespace profiler { class SerializedBlock; }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
template <class T, const uint16_t N> //#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 <const uint16_t N>
class chunk_allocator final class chunk_allocator final
{ {
struct chunk { T data[N]; }; struct chunk { EASY_ALIGNED(int8_t, data[N], 64); chunk* prev = nullptr; };
std::list<chunk> m_chunks; struct chunk_list
uint16_t m_size; {
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> chunk_list;
chunk_list m_chunks;
uint32_t m_size;
uint16_t m_shift;
public: public:
chunk_allocator() : m_size(0) chunk_allocator() : m_size(0), m_shift(0)
{ {
m_chunks.emplace_back(); 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; int8_t* data = m_chunks.back().data + m_shift;
m_size += n; 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; return data;
} }
m_size = n; m_shift = n + sizeof(uint16_t);
m_chunks.emplace_back(); 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() void clear()
{ {
m_size = 0; m_size = 0;
m_shift = 0;
m_chunks.clear(); m_chunks.clear();
m_chunks.emplace_back(); 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<profiler::SerializedBlock*> serialized_list_t; typedef std::vector<profiler::SerializedBlock*> serialized_list_t;
@ -139,14 +255,12 @@ struct BlocksList final
} }
}; };
chunk_allocator<char, N> alloc;
Stack openedList; Stack openedList;
serialized_list_t closedList; chunk_allocator<N> closedList;
uint64_t usedMemorySize = 0; uint64_t usedMemorySize = 0;
void clearClosed() { void clearClosed() {
serialized_list_t().swap(closedList); //closedList.clear();
alloc.clear();
usedMemorySize = 0; usedMemorySize = 0;
} }
}; };
@ -156,8 +270,8 @@ class ThreadStorage final
{ {
public: public:
BlocksList<std::reference_wrapper<profiler::Block>, SIZEOF_CSWITCH * (uint16_t)1024U> blocks; BlocksList<std::reference_wrapper<profiler::Block>, SIZEOF_CSWITCH * (uint16_t)128U> blocks;
BlocksList<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync; BlocksList<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync;
std::string name; std::string name;
bool named = false; bool named = false;
@ -172,6 +286,8 @@ public:
class ProfileManager final class ProfileManager final
{ {
friend profiler::BlockDescRef;
ProfileManager(); ProfileManager();
ProfileManager(const ProfileManager& p) = delete; ProfileManager(const ProfileManager& p) = delete;
ProfileManager& operator=(const ProfileManager&) = delete; ProfileManager& operator=(const ProfileManager&) = delete;
@ -179,28 +295,36 @@ class ProfileManager final
typedef profiler::guard_lock<profiler::spin_lock> guard_lock_t; typedef profiler::guard_lock<profiler::spin_lock> guard_lock_t;
typedef std::map<profiler::thread_id_t, ThreadStorage> map_of_threads_stacks; typedef std::map<profiler::thread_id_t, ThreadStorage> map_of_threads_stacks;
typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t; typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t;
typedef std::vector<profiler::block_id_t> expired_ids_t;
map_of_threads_stacks m_threads; map_of_threads_stacks m_threads;
block_descriptors_t m_descriptors; block_descriptors_t m_descriptors;
uint64_t m_usedMemorySize = 0; expired_ids_t m_expiredDescriptors;
profiler::spin_lock m_spin; uint64_t m_usedMemorySize = 0;
profiler::spin_lock m_storedSpin; profiler::spin_lock m_spin;
bool m_isEnabled = false; 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"; std::string m_csInfoFilename = "/tmp/cs_profiling_info.log";
uint32_t dumpBlocksToStream(profiler::OStream& _outputStream);
public: public:
static ProfileManager& instance(); static ProfileManager& instance();
~ProfileManager(); ~ProfileManager();
template <class ... TArgs> template <class ... TArgs>
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); guard_lock_t lock(m_storedSpin);
const auto id = static_cast<profiler::block_id_t>(m_descriptors.size()); desc->setId(m_idCounter++);
m_descriptors.emplace_back(new profiler::BlockDescriptor(m_usedMemorySize, id, _args...)); m_descriptors.emplace_back(desc);
return *m_descriptors.back();
return *desc;
} }
void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName); void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName);
@ -226,18 +350,9 @@ public:
private: private:
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id) void markExpired(profiler::block_id_t _id);
{ ThreadStorage& threadStorage(profiler::thread_id_t _thread_id);
guard_lock_t lock(m_spin); ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id);
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;
}
}; };
#endif #endif // EASY_PROFILER____MANAGER____H______

View File

@ -43,6 +43,7 @@
************************************************************************/ ************************************************************************/
#include "profiler/reader.h" #include "profiler/reader.h"
#include "hashed_cstr.h"
#include <fstream> #include <fstream>
#include <iterator> #include <iterator>
#include <algorithm> #include <algorithm>
@ -89,111 +90,17 @@ namespace profiler {
#ifdef _WIN32 #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<hashed_cstr> {
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<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap;
typedef ::std::unordered_map<hashed_cstr, ::profiler::block_id_t> 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 #else
// TODO: optimize for Linux too // TODO: Create optimized version of profiler::hashed_cstr for Linux too.
#include <string>
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; 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 #endif