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:
parent
c67bf1dda7
commit
d5d75d6340
@ -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_______
|
||||||
|
176
include/profiler/profiler_aux.h
Normal file
176
include/profiler/profiler_aux.h
Normal 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_______
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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
229
src/hashed_cstr.h
Normal 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
75
src/outstream.h
Normal 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_
|
@ -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());
|
||||||
|
@ -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______
|
||||||
|
107
src/reader.cpp
107
src/reader.cpp
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user