0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-29 10:05:48 +08:00

364 lines
11 KiB
C
Raw Normal View History

2016-02-16 23:21:12 +03:00
/**
Lightweight profiler library for c++
Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
2016-02-16 23:21:12 +03:00
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/>.
2016-02-17 23:43:37 +03:00
**/
2016-02-16 23:21:12 +03:00
#ifndef EASY_PROFILER____H_______
#define EASY_PROFILER____H_______
2016-02-16 23:21:12 +03:00
#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__)
2016-09-07 21:29:37 +03:00
#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
2016-09-07 21:29:37 +03:00
#endif
# endif
2016-09-07 21:29:37 +03:00
#if defined ( __clang__ )
# if (__clang_major__ == 3 && __clang_minor__ < 3) || (__clang_major__ < 3)
# define EASY_THREAD_LOCAL __thread
#endif
#endif
#endif
2016-09-07 21:29:37 +03:00
// 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
2016-02-16 23:21:12 +03:00
#endif
2016-08-31 21:51:22 +03:00
#if defined ( __clang__ )
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
#endif
#ifndef FULL_DISABLE_PROFILER
2016-02-16 23:21:12 +03:00
#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 ""; }
};
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.
#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)
2016-02-16 23:21:12 +03:00
/** Macro of beginning of block with custom name and color.
\code
2016-09-07 21:32:14 +03:00
#include "profiler/profiler.h"
void foo()
{
// some code ...
if(something){
EASY_BLOCK("Calling bar()"); // Block with default color
bar();
}
else{
EASY_BLOCK("Calling baz()", profiler::colors::Red); // Red block
baz();
}
2016-09-07 21:32:14 +03:00
}
\endcode
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__);\
::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
2016-02-16 23:21:12 +03:00
/** Macro of beginning of block with function name and custom color.
\code
2016-09-07 21:32:14 +03:00
#include "profiler/profiler.h"
void foo(){
EASY_FUNCTION(); // Block with name="foo" and default color
//some code...
}
void bar(){
EASY_FUNCTION(profiler::colors::Green); // Green block with name="bar"
//some code...
}
\endcode
Name of the block automatically created with function name.
\ingroup profiler
*/
2016-08-31 21:51:00 +03:00
#define EASY_FUNCTION(...)\
static const ::profiler::BaseBlockDescriptor& EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\
2016-08-31 21:51:00 +03:00
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\
2016-08-31 21:51:00 +03:00
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
/** Macro of completion of last nearest open block.
2016-02-16 23:21:12 +03:00
\code
#include "profiler/profiler.h"
int foo()
{
// some code ...
2016-09-07 21:32:14 +03:00
int sum = 0;
EASY_BLOCK("Calculating sum");
for (int i = 0; i < 10; ++i){
sum += i;
}
EASY_END_BLOCK;
// some antoher code here ...
return sum;
}
\endcode
\ingroup profiler
*/
#define EASY_END_BLOCK ::profiler::endBlock();
2016-02-16 23:21:12 +03:00
/** Macro of creating event with custom name and color.
Event is a block with zero duration and special type.
\warning Event ends immidiately and calling EASY_END_BLOCK after EASY_EVENT
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__);\
::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));
/** Macro enabling profiler
\ingroup profiler
*/
#define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
2016-02-16 23:21:12 +03:00
/** Macro disabling profiler
\ingroup profiler
*/
#define EASY_PROFILER_DISABLE ::profiler::setEnabled(false);
2016-02-16 23:21:12 +03:00
/** Macro of naming current thread.
If this thread has been already named then nothing changes.
\ingroup profiler
*/
#define EASY_THREAD(name)\
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\
if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__);
2016-07-31 22:12:11 +03:00
/** Macro of naming main thread.
This is only for user comfort. There is no difference for EasyProfiler GUI between different threads.
\ingroup profiler
*/
#define EASY_MAIN_THREAD EASY_THREAD("Main")
2016-07-31 22:12:11 +03:00
#else
#define EASY_BLOCK(...)
#define EASY_FUNCTION(...)
#define EASY_END_BLOCK
#define EASY_PROFILER_ENABLE
#define EASY_PROFILER_DISABLE
#define EASY_EVENT(...)
#define EASY_THREAD(...)
#define EASY_MAIN_THREAD
#endif
2016-02-16 23:21:12 +03:00
#include <stdint.h>
2016-02-17 23:24:35 +03:00
#include <cstddef>
#include "profiler/profiler_colors.h"
2016-02-16 23:21:12 +03:00
#ifdef _WIN32
2016-09-07 21:32:14 +03:00
#ifdef _BUILD_PROFILER
#define PROFILER_API __declspec(dllexport)
2016-02-16 23:21:12 +03:00
#else
2016-09-07 21:32:14 +03:00
#define PROFILER_API __declspec(dllimport)
2016-02-16 23:21:12 +03:00
#endif
#else
#define PROFILER_API
#endif
class ProfileManager;
namespace profiler {
2016-09-07 21:32:14 +03:00
class Block;
class BaseBlockDescriptor;
2016-02-16 23:21:12 +03:00
typedef uint64_t timestamp_t;
typedef uint32_t thread_id_t;
typedef uint32_t block_id_t;
2016-03-03 15:48:00 +03:00
enum BlockType : uint8_t
{
BLOCK_TYPE_EVENT = 0,
BLOCK_TYPE_THREAD_SIGN,
BLOCK_TYPE_BLOCK,
BLOCK_TYPE_CONTEXT_SWITCH,
BLOCK_TYPES_NUMBER
};
typedef BlockType block_type_t;
2016-09-07 21:32:14 +03:00
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();
2016-09-07 21:32:14 +03:00
}
#pragma pack(push,1)
class PROFILER_API BaseBlockDescriptor
{
friend ::ProfileManager;
protected:
block_id_t m_id; ///< This descriptor id (We can afford this spending because there are much more blocks than descriptors)
int m_line; ///< Line number in the source file
color_t m_color; ///< Color of the block packed into 1-byte structure
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);
2016-02-24 06:31:05 +03:00
public:
inline block_id_t id() const { return m_id; }
inline int line() const { return m_line; }
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;
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:
2016-02-18 19:27:17 +03:00
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
{
friend ::ProfileManager;
protected:
timestamp_t m_begin;
timestamp_t m_end;
block_id_t m_id;
public:
2016-09-04 19:35:58 +03:00
BaseBlockData(const BaseBlockData&) = default;
BaseBlockData(timestamp_t _begin_time, block_id_t _id);
inline timestamp_t begin() const { return m_begin; }
inline timestamp_t end() const { return m_end; }
inline block_id_t id() const { return m_id; }
inline timestamp_t duration() const { return m_end - m_begin; }
inline void setId(block_id_t _id) { m_id = _id; }
2016-09-04 19:35:58 +03:00
private:
BaseBlockData() = delete;
};
#pragma pack(pop)
class PROFILER_API Block final : public BaseBlockData
{
friend ::ProfileManager;
const char* m_name;
bool m_enabled;
private:
void finish();
void finish(timestamp_t _end_time);
inline bool finished() const { return m_end >= m_begin; }
inline bool enabled() const { return m_enabled; }
public:
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();
2016-02-24 06:31:05 +03:00
inline const char* name() const { return m_name; }
};
2016-02-24 06:31:05 +03:00
//////////////////////////////////////////////////////////////////////
} // END of namespace profiler.
2016-02-16 23:21:12 +03:00
2016-08-31 21:51:22 +03:00
#if defined ( __clang__ )
#pragma clang diagnostic pop
#endif
#endif // EASY_PROFILER____H_______