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,33 +19,7 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef EASY_PROFILER____H_______
|
||||
#define EASY_PROFILER____H_______
|
||||
|
||||
#ifdef _WIN32
|
||||
# define __func__ __FUNCTION__
|
||||
# if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
// There is no support for C++11 thread_local keyword prior to Visual Studio 2015. Use __declspec(thread) instead.
|
||||
# define EASY_THREAD_LOCAL __declspec(thread)
|
||||
# endif
|
||||
#elif defined(__GNUC__)
|
||||
#ifndef __clang__
|
||||
# if (__GNUC__ == 4 && __GNUC_MINOR__ < 8) || (__GNUC__ < 4)
|
||||
// There is no support for C++11 thread_local keyword prior to gcc 4.8. Use __thread instead.
|
||||
# define EASY_THREAD_LOCAL __thread
|
||||
#endif
|
||||
# endif
|
||||
#if defined ( __clang__ )
|
||||
# if (__clang_major__ == 3 && __clang_minor__ < 3) || (__clang_major__ < 3)
|
||||
# define EASY_THREAD_LOCAL __thread
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
// TODO: Check thread local support for clanv earlier than 3.3
|
||||
|
||||
#ifndef EASY_THREAD_LOCAL
|
||||
# define EASY_THREAD_LOCAL thread_local
|
||||
# define EASY_THREAD_LOCAL_CPP11
|
||||
#endif
|
||||
#include "profiler/profiler_aux.h"
|
||||
|
||||
#if defined ( __clang__ )
|
||||
# pragma clang diagnostic push
|
||||
@ -54,32 +28,27 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#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
|
||||
*/
|
||||
|
||||
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*) { return ""; }
|
||||
};
|
||||
/** If != 0 then EasyProfiler will measure time for blocks storage expansion.
|
||||
If 0 then EasyProfiler will be compiled without blocks of code responsible
|
||||
for measuring these events.
|
||||
|
||||
template <> struct NameSwitch<true> final {
|
||||
static const char* runtime_name(const char*) { return ""; }
|
||||
static const char* compiletime_name(const char* name) { return name; }
|
||||
};
|
||||
} // END of namespace profiler.
|
||||
These are "EasyProfiler.ExpandStorage" blocks on a diagram.
|
||||
|
||||
#define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::compiletime_name(name)
|
||||
#define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::runtime_name(name)
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_MEASURE_STORAGE_EXPAND 0
|
||||
|
||||
/** If true then "EasyProfiler.ExpandStorage" events are enabled by default and will be
|
||||
writed to output file or translated over the net.
|
||||
If false then you need to enable these events via GUI if you'll want to see them.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_STORAGE_EXPAND_ENABLED true
|
||||
|
||||
/** Macro of beginning of block with custom name and color.
|
||||
|
||||
@ -88,6 +57,8 @@ namespace profiler {
|
||||
void foo()
|
||||
{
|
||||
// some code ...
|
||||
|
||||
EASY_BLOCK("Check something", profiler::DISABLED); // Disabled block (There is possibility to enable this block later via GUI)
|
||||
if(something){
|
||||
EASY_BLOCK("Calling bar()"); // Block with default color
|
||||
bar();
|
||||
@ -96,6 +67,10 @@ namespace profiler {
|
||||
EASY_BLOCK("Calling baz()", profiler::colors::Red); // Red block
|
||||
baz();
|
||||
}
|
||||
EASY_END_BLOCK; // End of "Check something" block (Even if "Check something" is disabled, this EASY_END_BLOCK will not end any other block).
|
||||
|
||||
EASY_BLOCK("Some another block", profiler::colors::Blue, profiler::DISABLED); // Disabled block with Blue color
|
||||
// some another code...
|
||||
}
|
||||
\endcode
|
||||
|
||||
@ -104,8 +79,8 @@ Block will be automatically completed by destructor.
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_BLOCK(name, ...)\
|
||||
static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
|
||||
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
|
||||
EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__));\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
|
||||
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
|
||||
|
||||
@ -122,6 +97,11 @@ Block will be automatically completed by destructor.
|
||||
EASY_FUNCTION(profiler::colors::Green); // Green block with name="bar"
|
||||
//some code...
|
||||
}
|
||||
|
||||
void baz(){
|
||||
EASY_FUNCTION(profiler::DISABLED); // Disabled block with name="baz" and default color (There is possibility to enable this block later via GUI)
|
||||
// som code...
|
||||
}
|
||||
\endcode
|
||||
|
||||
Name of the block automatically created with function name.
|
||||
@ -129,8 +109,8 @@ Name of the block automatically created with function name.
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_FUNCTION(...)\
|
||||
static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
|
||||
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
|
||||
__func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__));\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\
|
||||
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
|
||||
|
||||
@ -169,16 +149,19 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_EVENT(name, ...)\
|
||||
static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = \
|
||||
::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\
|
||||
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__) = \
|
||||
::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__));\
|
||||
::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));
|
||||
|
||||
/** Macro enabling profiler
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
|
||||
|
||||
/** Macro disabling profiler
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_PROFILER_DISABLE ::profiler::setEnabled(false);
|
||||
@ -203,6 +186,8 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe
|
||||
# define EASY_MAIN_THREAD EASY_THREAD("Main")
|
||||
|
||||
#else
|
||||
# define EASY_MEASURE_STORAGE_EXPAND 0
|
||||
# define EASY_STORAGE_EXPAND_ENABLED false
|
||||
# define EASY_BLOCK(...)
|
||||
# define EASY_FUNCTION(...)
|
||||
# define EASY_END_BLOCK
|
||||
@ -213,26 +198,16 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe
|
||||
# define EASY_MAIN_THREAD
|
||||
#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 ThreadStorage;
|
||||
|
||||
namespace profiler {
|
||||
|
||||
class Block;
|
||||
class BaseBlockDescriptor;
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Core types
|
||||
|
||||
typedef uint64_t timestamp_t;
|
||||
typedef uint32_t thread_id_t;
|
||||
@ -249,17 +224,7 @@ namespace profiler {
|
||||
};
|
||||
typedef BlockType block_type_t;
|
||||
|
||||
extern "C" {
|
||||
PROFILER_API const BaseBlockDescriptor& registerDescription(const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color = ::profiler::colors::Default);
|
||||
PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName);
|
||||
PROFILER_API void beginBlock(Block& _block);
|
||||
PROFILER_API void endBlock();
|
||||
PROFILER_API void setEnabled(bool _isEnable);
|
||||
PROFILER_API uint32_t dumpBlocksToFile(const char* _filename);
|
||||
PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line);
|
||||
PROFILER_API void setContextSwitchLogFilename(const char* _name);
|
||||
PROFILER_API const char* getContextSwitchLogFilename();
|
||||
}
|
||||
//***********************************************
|
||||
|
||||
#pragma pack(push,1)
|
||||
class PROFILER_API BaseBlockDescriptor
|
||||
@ -274,7 +239,7 @@ namespace profiler {
|
||||
block_type_t m_type; ///< Type of the block (See BlockType)
|
||||
bool m_enabled; ///< If false then blocks with such id() will not be stored by profiler during profile session
|
||||
|
||||
BaseBlockDescriptor(block_id_t _id, int _line, block_type_t _block_type, color_t _color);
|
||||
BaseBlockDescriptor(block_id_t _id, bool _enabled, int _line, block_type_t _block_type, color_t _color);
|
||||
|
||||
public:
|
||||
|
||||
@ -283,22 +248,10 @@ namespace profiler {
|
||||
inline color_t color() const { return m_color; }
|
||||
inline block_type_t type() const { return m_type; }
|
||||
inline bool enabled() const { return m_enabled; }
|
||||
};
|
||||
|
||||
class PROFILER_API BlockDescriptor final : public BaseBlockDescriptor
|
||||
{
|
||||
friend ::ProfileManager;
|
||||
}; // END of class BaseBlockDescriptor.
|
||||
|
||||
const char* m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier
|
||||
const char* m_filename; ///< Source file name where this block is declared
|
||||
|
||||
public:
|
||||
|
||||
BlockDescriptor(uint64_t& _used_mem, block_id_t _id, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
|
||||
|
||||
inline const char* name() const { return m_name; }
|
||||
inline const char* file() const { return m_filename; }
|
||||
};
|
||||
//***********************************************
|
||||
|
||||
class PROFILER_API BaseBlockData
|
||||
{
|
||||
@ -325,20 +278,52 @@ namespace profiler {
|
||||
private:
|
||||
|
||||
BaseBlockData() = delete;
|
||||
};
|
||||
|
||||
}; // END of class BaseBlockData.
|
||||
#pragma pack(pop)
|
||||
|
||||
//***********************************************
|
||||
|
||||
class PROFILER_API BlockDescriptor final : public BaseBlockDescriptor
|
||||
{
|
||||
friend ::ProfileManager;
|
||||
|
||||
const char* m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier
|
||||
const char* m_filename; ///< Source file name where this block is declared
|
||||
|
||||
BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
|
||||
void setId(block_id_t _id);
|
||||
|
||||
public:
|
||||
|
||||
BlockDescriptor(uint64_t& _used_mem, block_id_t _id, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
|
||||
|
||||
inline const char* name() const {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
inline const char* file() const {
|
||||
return m_filename;
|
||||
}
|
||||
|
||||
}; // END of class BlockDescriptor.
|
||||
|
||||
//***********************************************
|
||||
|
||||
class PROFILER_API Block final : public BaseBlockData
|
||||
{
|
||||
friend ::ProfileManager;
|
||||
friend ::ThreadStorage;
|
||||
|
||||
const char* m_name;
|
||||
bool m_enabled;
|
||||
|
||||
private:
|
||||
|
||||
void start();
|
||||
void start(timestamp_t _time);
|
||||
void finish();
|
||||
void finish(timestamp_t _end_time);
|
||||
void finish(timestamp_t _time);
|
||||
inline bool finished() const { return m_end >= m_begin; }
|
||||
inline bool enabled() const { return m_enabled; }
|
||||
|
||||
@ -346,11 +331,51 @@ namespace profiler {
|
||||
|
||||
Block(Block&& that);
|
||||
Block(const BaseBlockDescriptor& _desc, const char* _runtimeName);
|
||||
Block(timestamp_t _begin_time, block_id_t _id, bool _enabled, const char* _runtimeName);
|
||||
Block(timestamp_t _begin_time, block_id_t _id, const char* _runtimeName);
|
||||
~Block();
|
||||
|
||||
inline const char* name() const { return m_name; }
|
||||
};
|
||||
|
||||
}; // END of class Block.
|
||||
|
||||
//***********************************************
|
||||
|
||||
class PROFILER_API BlockDescRef final
|
||||
{
|
||||
const BaseBlockDescriptor& m_desc;
|
||||
|
||||
public:
|
||||
|
||||
BlockDescRef(const BaseBlockDescriptor& _desc) : m_desc(_desc) { }
|
||||
inline operator const BaseBlockDescriptor& () const { return m_desc; }
|
||||
~BlockDescRef();
|
||||
|
||||
private:
|
||||
|
||||
BlockDescRef() = delete;
|
||||
BlockDescRef(const BlockDescRef&) = delete;
|
||||
BlockDescRef& operator = (const BlockDescRef&) = delete;
|
||||
|
||||
}; // END of class BlockDescRef.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Core API
|
||||
|
||||
extern "C" {
|
||||
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color);
|
||||
PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName);
|
||||
PROFILER_API void beginBlock(Block& _block);
|
||||
PROFILER_API void endBlock();
|
||||
PROFILER_API void setEnabled(bool _isEnable);
|
||||
PROFILER_API uint32_t dumpBlocksToFile(const char* _filename);
|
||||
PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line);
|
||||
PROFILER_API void setContextSwitchLogFilename(const char* _name);
|
||||
PROFILER_API const char* getContextSwitchLogFilename();
|
||||
}
|
||||
|
||||
inline void setEnabled(::profiler::EasyEnableFlag _isEnable) {
|
||||
setEnabled(_isEnable == ::profiler::ENABLED);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
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)
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red700);
|
||||
EASY_FUNCTION(profiler::colors::Red700, profiler::DISABLED);
|
||||
return i * i * i * i / 100;
|
||||
}
|
||||
|
||||
|
@ -68,25 +68,37 @@ Block::Block(Block&& that)
|
||||
}
|
||||
|
||||
Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName)
|
||||
: Block(getCurrentTime(), _descriptor.id(), _descriptor.enabled(), _runtimeName)
|
||||
: BaseBlockData(_descriptor.enabled() ? getCurrentTime() : 1ULL, _descriptor.id())
|
||||
, m_name(_runtimeName)
|
||||
, m_enabled(_descriptor.enabled())
|
||||
{
|
||||
}
|
||||
|
||||
Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, bool _enabled, const char* _runtimeName)
|
||||
Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, const char* _runtimeName)
|
||||
: BaseBlockData(_begin_time, _descriptor_id)
|
||||
, m_name(_runtimeName)
|
||||
, m_enabled(_enabled)
|
||||
, m_enabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
void Block::start()
|
||||
{
|
||||
m_begin = getCurrentTime();
|
||||
}
|
||||
|
||||
void Block::start(timestamp_t _time)
|
||||
{
|
||||
m_begin = _time;
|
||||
}
|
||||
|
||||
void Block::finish()
|
||||
{
|
||||
m_end = getCurrentTime();
|
||||
}
|
||||
|
||||
void Block::finish(timestamp_t _end_time)
|
||||
void Block::finish(timestamp_t _time)
|
||||
{
|
||||
m_end = _end_time;
|
||||
m_end = _time;
|
||||
}
|
||||
|
||||
Block::~Block()
|
||||
|
@ -43,12 +43,12 @@ namespace profiler {
|
||||
if (sizeof(CSwitch) != _traceEvent->UserDataLength)
|
||||
return;
|
||||
|
||||
//EASY_FUNCTION(::profiler::colors::Red);
|
||||
//EASY_FUNCTION(::profiler::colors::White);
|
||||
|
||||
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
|
||||
const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
|
||||
|
||||
static const auto& desc = MANAGER.addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
static const auto& desc = MANAGER.addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id());
|
||||
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
|
||||
}
|
||||
@ -145,7 +145,6 @@ namespace profiler {
|
||||
m_stubThread = ::std::move(::std::thread([this]()
|
||||
{
|
||||
EASY_THREAD("EasyProfiler.ETW");
|
||||
//EASY_BLOCK("ProcessTrace()", ::profiler::colors::Red);
|
||||
ProcessTrace(&m_openedHandle, 1, 0, 0);
|
||||
}));
|
||||
|
||||
|
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
|
||||
* : 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 <string.h>
|
||||
|
||||
#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;
|
||||
#endif
|
||||
|
||||
extern timestamp_t getCurrentTime();
|
||||
|
||||
//auto& MANAGER = ProfileManager::instance();
|
||||
#define MANAGER ProfileManager::instance()
|
||||
|
||||
extern "C" {
|
||||
|
||||
PROFILER_API const BaseBlockDescriptor& registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
||||
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
||||
{
|
||||
return MANAGER.addBlockDescriptor(_name, _filename, _line, _block_type, _color);
|
||||
return MANAGER.addBlockDescriptor(_enabled, _name, _filename, _line, _block_type, _color);
|
||||
}
|
||||
|
||||
PROFILER_API void endBlock()
|
||||
@ -109,44 +110,87 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, int _line, block_type_t _block_type, color_t _color)
|
||||
BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, bool _enabled, int _line, block_type_t _block_type, color_t _color)
|
||||
: m_id(_id)
|
||||
, m_line(_line)
|
||||
, m_type(_block_type)
|
||||
, m_color(_color)
|
||||
, m_enabled(true)
|
||||
, m_enabled(_enabled)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
||||
: BaseBlockDescriptor(_id, _line, _block_type, _color)
|
||||
BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
||||
: BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color)
|
||||
, m_name(_name)
|
||||
, m_filename(_filename)
|
||||
{
|
||||
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
|
||||
}
|
||||
|
||||
BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
||||
: BaseBlockDescriptor(0, _enabled, _line, _block_type, _color)
|
||||
, m_name(_name)
|
||||
, m_filename(_filename)
|
||||
{
|
||||
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
|
||||
}
|
||||
|
||||
void BlockDescriptor::setId(block_id_t _id)
|
||||
{
|
||||
m_id = _id;
|
||||
}
|
||||
|
||||
BlockDescRef::~BlockDescRef()
|
||||
{
|
||||
MANAGER.markExpired(m_desc.id());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ThreadStorage::storeBlock(const profiler::Block& block)
|
||||
{
|
||||
#if EASY_MEASURE_STORAGE_EXPAND != 0
|
||||
static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White);
|
||||
#endif
|
||||
|
||||
auto name_length = static_cast<uint16_t>(strlen(block.name()));
|
||||
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.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)
|
||||
{
|
||||
auto name_length = static_cast<uint16_t>(strlen(block.name()));
|
||||
auto size = static_cast<uint16_t>(sizeof(BaseBlockData) + name_length + 1);
|
||||
auto data = sync.alloc.allocate(size);
|
||||
::new (static_cast<void*>(data)) SerializedBlock(block, name_length);
|
||||
auto data = sync.closedList.allocate(size);
|
||||
::new (data) SerializedBlock(block, name_length);
|
||||
sync.usedMemorySize += size;
|
||||
sync.closedList.emplace_back(reinterpret_cast<SerializedBlock*>(data));
|
||||
}
|
||||
|
||||
void ThreadStorage::clearClosed()
|
||||
@ -159,25 +203,9 @@ void ThreadStorage::clearClosed()
|
||||
|
||||
EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
|
||||
|
||||
// #ifdef _WIN32
|
||||
// LPTOP_LEVEL_EXCEPTION_FILTER PREVIOUS_FILTER = NULL;
|
||||
// LONG WINAPI easyTopLevelExceptionFilter(struct _EXCEPTION_POINTERS* ExceptionInfo)
|
||||
// {
|
||||
// std::ofstream testexp("TEST_EXP.txt", std::fstream::binary);
|
||||
// testexp.write("APPLICATION CRASHED!", strlen("APPLICATION CRASHED!"));
|
||||
//
|
||||
// EasyEventTracer::instance().disable();
|
||||
// if (PREVIOUS_FILTER)
|
||||
// return PREVIOUS_FILTER(ExceptionInfo);
|
||||
// return EXCEPTION_CONTINUE_SEARCH;
|
||||
// }
|
||||
// #endif
|
||||
|
||||
ProfileManager::ProfileManager()
|
||||
{
|
||||
// #ifdef _WIN32
|
||||
// PREVIOUS_FILTER = SetUnhandledExceptionFilter(easyTopLevelExceptionFilter);
|
||||
// #endif
|
||||
m_expiredDescriptors.reserve(1024U);
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager()
|
||||
@ -197,6 +225,28 @@ ProfileManager& ProfileManager::instance()
|
||||
return m_profileManager;
|
||||
}
|
||||
|
||||
void ProfileManager::markExpired(profiler::block_id_t _id)
|
||||
{
|
||||
// Mark block descriptor as expired (descriptor may become expired if it's .dll/.so have been unloaded during application execution).
|
||||
// We can not delete this descriptor now, because we need to send/write all collected data first.
|
||||
|
||||
guard_lock_t lock(m_storedSpin);
|
||||
m_expiredDescriptors.push_back(_id);
|
||||
}
|
||||
|
||||
ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
return m_threads[_thread_id];
|
||||
}
|
||||
|
||||
ThreadStorage* ProfileManager::findThreadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
auto it = m_threads.find(_thread_id);
|
||||
return it != m_threads.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
void ProfileManager::storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName)
|
||||
{
|
||||
if (!m_isEnabled || !_desc.enabled())
|
||||
@ -226,7 +276,7 @@ void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profil
|
||||
{
|
||||
auto ts = findThreadStorage(_thread_id);
|
||||
if (ts != nullptr)
|
||||
ts->sync.openedList.emplace(_time, _id, true, "");
|
||||
ts->sync.openedList.emplace(_time, _id, "");
|
||||
}
|
||||
|
||||
void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id)
|
||||
@ -234,7 +284,7 @@ void ProfileManager::storeContextSwitch(profiler::thread_id_t _thread_id, profil
|
||||
auto ts = findThreadStorage(_thread_id);
|
||||
if (ts != nullptr)
|
||||
{
|
||||
profiler::Block b(_time, _id, true, "");
|
||||
profiler::Block b(_time, _id, "");
|
||||
b.finish(_time);
|
||||
ts->storeCSwitch(b);
|
||||
}
|
||||
@ -279,39 +329,16 @@ void ProfileManager::setEnabled(bool isEnable)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class FileWriter final
|
||||
{
|
||||
std::ofstream m_file;
|
||||
|
||||
public:
|
||||
|
||||
explicit FileWriter(const char* _filename) : m_file(_filename, std::fstream::binary) { }
|
||||
|
||||
template <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)
|
||||
uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
|
||||
{
|
||||
const bool wasEnabled = m_isEnabled;
|
||||
if (wasEnabled)
|
||||
::profiler::setEnabled(false);
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
// Read thread context switch events from temporary file
|
||||
|
||||
uint64_t timestamp;
|
||||
uint32_t thread_from, thread_to;
|
||||
|
||||
@ -319,19 +346,16 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
|
||||
|
||||
if(infile.is_open())
|
||||
{
|
||||
static const auto desc = addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
static const auto& desc = addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, profiler::BLOCK_TYPE_CONTEXT_SWITCH, profiler::colors::White);
|
||||
while (infile >> timestamp >> thread_from >> thread_to)
|
||||
{
|
||||
beginContextSwitch(thread_from, timestamp, desc);
|
||||
beginContextSwitch(thread_from, timestamp, desc.id());
|
||||
endContextSwitch(thread_to, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
FileWriter of(filename);
|
||||
|
||||
// Calculate used memory total size and total blocks number
|
||||
uint64_t usedMemorySize = 0;
|
||||
uint32_t blocks_number = 0;
|
||||
for (const auto& thread_storage : m_threads)
|
||||
@ -341,53 +365,80 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
|
||||
blocks_number += static_cast<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
|
||||
of.write(CPU_FREQUENCY);
|
||||
_outputStream.write(CPU_FREQUENCY);
|
||||
#else
|
||||
of.write(0LL);
|
||||
_outputStream.write(0LL);
|
||||
#endif
|
||||
|
||||
of.write(blocks_number);
|
||||
of.write(usedMemorySize);
|
||||
of.write(static_cast<uint32_t>(m_descriptors.size()));
|
||||
of.write(m_usedMemorySize);
|
||||
// Write blocks number and used memory size
|
||||
_outputStream.write(blocks_number);
|
||||
_outputStream.write(usedMemorySize);
|
||||
_outputStream.write(static_cast<uint32_t>(m_descriptors.size()));
|
||||
_outputStream.write(m_usedMemorySize);
|
||||
|
||||
// Write block descriptors
|
||||
for (const auto descriptor : m_descriptors)
|
||||
{
|
||||
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 size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size);
|
||||
|
||||
of.write(size);
|
||||
of.write<profiler::BaseBlockDescriptor>(*descriptor);
|
||||
of.write(name_size);
|
||||
of.write(descriptor->name(), name_size);
|
||||
of.write(descriptor->file(), filename_size);
|
||||
_outputStream.write(size);
|
||||
_outputStream.write<profiler::BaseBlockDescriptor>(*descriptor);
|
||||
_outputStream.write(name_size);
|
||||
_outputStream.write(descriptor->name(), name_size);
|
||||
_outputStream.write(descriptor->file(), filename_size);
|
||||
}
|
||||
|
||||
// Write blocks and context switch events for each thread
|
||||
for (auto& thread_storage : m_threads)
|
||||
{
|
||||
auto& t = thread_storage.second;
|
||||
|
||||
of.write(thread_storage.first);
|
||||
_outputStream.write(thread_storage.first);
|
||||
|
||||
of.write(static_cast<uint32_t>(t.sync.closedList.size()));
|
||||
for (auto b : t.sync.closedList)
|
||||
of.writeBlock(b);
|
||||
_outputStream.write(t.sync.closedList.size());
|
||||
t.sync.closedList.serialize(_outputStream);
|
||||
|
||||
of.write(static_cast<uint32_t>(t.blocks.closedList.size()));
|
||||
for (auto b : t.blocks.closedList)
|
||||
of.writeBlock(b);
|
||||
_outputStream.write(t.blocks.closedList.size());
|
||||
t.blocks.closedList.serialize(_outputStream);
|
||||
|
||||
t.clearClosed();
|
||||
}
|
||||
|
||||
if (!m_expiredDescriptors.empty())
|
||||
{
|
||||
// Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution)
|
||||
|
||||
std::sort(m_expiredDescriptors.begin(), m_expiredDescriptors.end());
|
||||
for (auto it = m_expiredDescriptors.rbegin(), rend = m_expiredDescriptors.rend(); it != rend; ++it)
|
||||
{
|
||||
auto id = *it;
|
||||
delete m_descriptors[id];
|
||||
m_descriptors.erase(m_descriptors.begin() + id);
|
||||
}
|
||||
m_expiredDescriptors.clear();
|
||||
}
|
||||
|
||||
//if (wasEnabled)
|
||||
// ::profiler::setEnabled(true);
|
||||
|
||||
return blocks_number;
|
||||
}
|
||||
|
||||
uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
|
||||
{
|
||||
profiler::OStream outputStream;
|
||||
const auto blocksNumber = dumpBlocksToStream(outputStream);
|
||||
|
||||
std::ofstream of(_filename, std::fstream::binary);
|
||||
of << outputStream.stream().str();
|
||||
|
||||
return blocksNumber;
|
||||
}
|
||||
|
||||
const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
||||
{
|
||||
if (THREAD_STORAGE == nullptr)
|
||||
@ -397,7 +448,7 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename
|
||||
|
||||
if (!THREAD_STORAGE->named)
|
||||
{
|
||||
const auto& desc = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black);
|
||||
const auto& desc = addBlockDescriptor(true, _funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black);
|
||||
|
||||
profiler::Block b(desc, name);
|
||||
b.finish(b.begin());
|
||||
|
@ -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/>.
|
||||
**/
|
||||
|
||||
#ifndef ___PROFILER____MANAGER____H______
|
||||
#define ___PROFILER____MANAGER____H______
|
||||
#ifndef EASY_PROFILER____MANAGER____H______
|
||||
#define EASY_PROFILER____MANAGER____H______
|
||||
|
||||
#include "profiler/profiler.h"
|
||||
|
||||
#include "spin_lock.h"
|
||||
|
||||
#include <stack>
|
||||
#include "outstream.h"
|
||||
//#include "hashed_cstr.h"
|
||||
#include <map>
|
||||
#include <list>
|
||||
#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
|
||||
{
|
||||
struct chunk { T data[N]; };
|
||||
struct chunk { EASY_ALIGNED(int8_t, data[N], 64); chunk* prev = nullptr; };
|
||||
|
||||
std::list<chunk> m_chunks;
|
||||
uint16_t m_size;
|
||||
struct chunk_list
|
||||
{
|
||||
chunk* last = nullptr;
|
||||
|
||||
~chunk_list()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
do {
|
||||
auto p = last;
|
||||
last = last->prev;
|
||||
EASY_FREE(p);
|
||||
} while (last != nullptr);
|
||||
}
|
||||
|
||||
chunk& back()
|
||||
{
|
||||
return *last;
|
||||
}
|
||||
|
||||
void emplace_back()
|
||||
{
|
||||
auto prev = last;
|
||||
last = ::new (EASY_MALLOC(sizeof(chunk), 64)) chunk();
|
||||
last->prev = prev;
|
||||
*(uint16_t*)last->data = 0;
|
||||
}
|
||||
|
||||
void invert()
|
||||
{
|
||||
chunk* next = nullptr;
|
||||
|
||||
while (last->prev != nullptr) {
|
||||
auto p = last->prev;
|
||||
last->prev = next;
|
||||
next = last;
|
||||
last = p;
|
||||
}
|
||||
|
||||
last->prev = next;
|
||||
}
|
||||
};
|
||||
|
||||
//typedef std::list<chunk> chunk_list;
|
||||
|
||||
chunk_list m_chunks;
|
||||
uint32_t m_size;
|
||||
uint16_t m_shift;
|
||||
|
||||
public:
|
||||
|
||||
chunk_allocator() : m_size(0)
|
||||
chunk_allocator() : m_size(0), m_shift(0)
|
||||
{
|
||||
m_chunks.emplace_back();
|
||||
}
|
||||
|
||||
T* allocate(uint16_t n)
|
||||
void* allocate(uint16_t n)
|
||||
{
|
||||
if (m_size + n <= N)
|
||||
++m_size;
|
||||
|
||||
if (!need_expand(n))
|
||||
{
|
||||
T* data = m_chunks.back().data + m_size;
|
||||
m_size += n;
|
||||
int8_t* data = m_chunks.back().data + m_shift;
|
||||
m_shift += n + sizeof(uint16_t);
|
||||
|
||||
*(uint16_t*)data = n;
|
||||
data = data + sizeof(uint16_t);
|
||||
|
||||
if (m_shift < N)
|
||||
*(uint16_t*)(data + n) = 0;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
m_size = n;
|
||||
m_shift = n + sizeof(uint16_t);
|
||||
m_chunks.emplace_back();
|
||||
return m_chunks.back().data;
|
||||
auto data = m_chunks.back().data;
|
||||
|
||||
*(uint16_t*)data = n;
|
||||
data = data + sizeof(uint16_t);
|
||||
|
||||
*(uint16_t*)(data + n) = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
inline bool need_expand(uint16_t n) const
|
||||
{
|
||||
return (m_shift + n + sizeof(uint16_t)) > N;
|
||||
}
|
||||
|
||||
inline uint32_t size() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_size = 0;
|
||||
m_shift = 0;
|
||||
m_chunks.clear();
|
||||
m_chunks.emplace_back();
|
||||
}
|
||||
|
||||
/** Serialize data to stream.
|
||||
|
||||
\warning Data will be cleared after serialization.
|
||||
*/
|
||||
void serialize(profiler::OStream& _outputStream)
|
||||
{
|
||||
m_chunks.invert();
|
||||
|
||||
auto current = m_chunks.last;
|
||||
do {
|
||||
const int8_t* data = current->data;
|
||||
uint16_t i = 0;
|
||||
do {
|
||||
const uint16_t size = sizeof(uint16_t) + *(uint16_t*)data;
|
||||
_outputStream.write((const char*)data, size);
|
||||
data = data + size;
|
||||
i += size;
|
||||
} while (i < N && *(uint16_t*)data != 0);
|
||||
current = current->prev;
|
||||
} while (current != nullptr);
|
||||
|
||||
clear();
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const uint16_t SIZEOF_CSWITCH = sizeof(profiler::BaseBlockData) + 1;
|
||||
const uint16_t SIZEOF_CSWITCH = sizeof(profiler::BaseBlockData) + 1 + sizeof(uint16_t);
|
||||
|
||||
typedef std::vector<profiler::SerializedBlock*> serialized_list_t;
|
||||
|
||||
@ -139,14 +255,12 @@ struct BlocksList final
|
||||
}
|
||||
};
|
||||
|
||||
chunk_allocator<char, N> alloc;
|
||||
Stack openedList;
|
||||
serialized_list_t closedList;
|
||||
chunk_allocator<N> closedList;
|
||||
uint64_t usedMemorySize = 0;
|
||||
|
||||
void clearClosed() {
|
||||
serialized_list_t().swap(closedList);
|
||||
alloc.clear();
|
||||
//closedList.clear();
|
||||
usedMemorySize = 0;
|
||||
}
|
||||
};
|
||||
@ -156,7 +270,7 @@ class ThreadStorage final
|
||||
{
|
||||
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;
|
||||
std::string name;
|
||||
bool named = false;
|
||||
@ -172,6 +286,8 @@ public:
|
||||
|
||||
class ProfileManager final
|
||||
{
|
||||
friend profiler::BlockDescRef;
|
||||
|
||||
ProfileManager();
|
||||
ProfileManager(const ProfileManager& p) = delete;
|
||||
ProfileManager& operator=(const ProfileManager&) = delete;
|
||||
@ -179,28 +295,36 @@ class ProfileManager final
|
||||
typedef profiler::guard_lock<profiler::spin_lock> guard_lock_t;
|
||||
typedef std::map<profiler::thread_id_t, ThreadStorage> map_of_threads_stacks;
|
||||
typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t;
|
||||
typedef std::vector<profiler::block_id_t> expired_ids_t;
|
||||
|
||||
map_of_threads_stacks m_threads;
|
||||
block_descriptors_t m_descriptors;
|
||||
expired_ids_t m_expiredDescriptors;
|
||||
uint64_t m_usedMemorySize = 0;
|
||||
profiler::spin_lock m_spin;
|
||||
profiler::spin_lock m_storedSpin;
|
||||
profiler::block_id_t m_idCounter = 0;
|
||||
bool m_isEnabled = false;
|
||||
|
||||
std::string m_csInfoFilename = "/tmp/cs_profiling_info.log";
|
||||
|
||||
uint32_t dumpBlocksToStream(profiler::OStream& _outputStream);
|
||||
|
||||
public:
|
||||
|
||||
static ProfileManager& instance();
|
||||
~ProfileManager();
|
||||
|
||||
template <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);
|
||||
const auto id = static_cast<profiler::block_id_t>(m_descriptors.size());
|
||||
m_descriptors.emplace_back(new profiler::BlockDescriptor(m_usedMemorySize, id, _args...));
|
||||
return *m_descriptors.back();
|
||||
desc->setId(m_idCounter++);
|
||||
m_descriptors.emplace_back(desc);
|
||||
|
||||
return *desc;
|
||||
}
|
||||
|
||||
void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName);
|
||||
@ -226,18 +350,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
return m_threads[_thread_id];
|
||||
}
|
||||
|
||||
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
auto it = m_threads.find(_thread_id);
|
||||
return it != m_threads.end() ? &it->second : nullptr;
|
||||
}
|
||||
void markExpired(profiler::block_id_t _id);
|
||||
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id);
|
||||
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id);
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // EASY_PROFILER____MANAGER____H______
|
||||
|
107
src/reader.cpp
107
src/reader.cpp
@ -43,6 +43,7 @@
|
||||
************************************************************************/
|
||||
|
||||
#include "profiler/reader.h"
|
||||
#include "hashed_cstr.h"
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
@ -89,111 +90,17 @@ namespace profiler {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
/** \brief Simple C-string pointer with length.
|
||||
|
||||
It is used as base class for a key in std::unordered_map.
|
||||
It is used to get better performance than std::string.
|
||||
It simply stores a pointer and a length, there is no
|
||||
any memory allocation and copy.
|
||||
|
||||
\note It is absolutely safe to store pointer because std::unordered_map,
|
||||
which uses it as a key, exists only inside fillTreesFromFile function.
|
||||
|
||||
*/
|
||||
class cstring
|
||||
{
|
||||
protected:
|
||||
|
||||
const char* str;
|
||||
size_t str_len;
|
||||
|
||||
public:
|
||||
|
||||
explicit cstring(const char* _str) : str(_str), str_len(strlen(_str))
|
||||
{
|
||||
}
|
||||
|
||||
cstring(const cstring& _other) : str(_other.str), str_len(_other.str_len)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator == (const cstring& _other) const
|
||||
{
|
||||
return str_len == _other.str_len && !strncmp(str, _other.str, str_len);
|
||||
}
|
||||
|
||||
inline bool operator != (const cstring& _other) const
|
||||
{
|
||||
return !operator == (_other);
|
||||
}
|
||||
|
||||
inline bool operator < (const cstring& _other) const
|
||||
{
|
||||
if (str_len == _other.str_len)
|
||||
{
|
||||
return strncmp(str, _other.str, str_len) < 0;
|
||||
}
|
||||
|
||||
return str_len < _other.str_len;
|
||||
}
|
||||
};
|
||||
|
||||
/** \brief cstring with precalculated hash.
|
||||
|
||||
This is used to calculate hash for C-string and to cache it
|
||||
to be used in the future without recurring hash calculatoin.
|
||||
|
||||
\note This class is used as a key in std::unordered_map.
|
||||
|
||||
*/
|
||||
class hashed_cstr : public cstring
|
||||
{
|
||||
typedef cstring Parent;
|
||||
|
||||
public:
|
||||
|
||||
size_t str_hash;
|
||||
|
||||
explicit hashed_cstr(const char* _str) : Parent(_str), str_hash(0)
|
||||
{
|
||||
str_hash = ::std::_Hash_seq((const unsigned char *)str, str_len);
|
||||
}
|
||||
|
||||
hashed_cstr(const hashed_cstr& _other) : Parent(_other), str_hash(_other.str_hash)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool operator == (const hashed_cstr& _other) const
|
||||
{
|
||||
return str_hash == _other.str_hash && Parent::operator == (_other);
|
||||
}
|
||||
|
||||
inline bool operator != (const hashed_cstr& _other) const
|
||||
{
|
||||
return !operator == (_other);
|
||||
}
|
||||
};
|
||||
|
||||
namespace std {
|
||||
|
||||
/** \brief Simply returns precalculated hash of a C-string. */
|
||||
template <> struct hash<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<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
|
||||
|
||||
// TODO: optimize for Linux too
|
||||
#include <string>
|
||||
// TODO: Create optimized version of profiler::hashed_cstr for Linux too.
|
||||
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap;
|
||||
typedef ::std::unordered_map<::std::string, ::profiler::block_id_t> IdMap;
|
||||
typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap;
|
||||
|
||||
#endif
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user