Merge commit
80
README.md
@ -8,10 +8,19 @@
|
||||
|
||||
|
||||
# About
|
||||
Lightweight profiler library for c++
|
||||
Lightweight profiler library for c++
|
||||
|
||||
You can profile any function in you code. Furthermore this library provide measuring time of any block of code. Also the library can capture system's context switch.
|
||||
You can see the results of measuring in simple gui application which provide full statistic and render beautiful timeline.
|
||||
You can profile any function in you code. Furthermore this library provide measuring time of any block of code.
|
||||
For example, information for 12 millions of blocks is using less than 300Mb of memory.
|
||||
Working profiler slows your application execution for only 1-2%.
|
||||
|
||||
Disabled profiler will not affect your application execution in any way. You can leave it in your Release build
|
||||
and enable it in run-time at any moment during application launch to see what is happening at the moment.
|
||||
|
||||
Also the library can capture system's context switch events between threads. Context switch information includes
|
||||
duration, target thread id, thread owner process id, thread owner process name.
|
||||
|
||||
You can see the results of measuring in simple GUI application which provides full statistics and renders beautiful time-line.
|
||||
|
||||
# Build
|
||||
|
||||
@ -24,7 +33,7 @@ For core:
|
||||
For GUI:
|
||||
* Qt 5.3.0 or later
|
||||
|
||||
## linux
|
||||
## Linux
|
||||
|
||||
```bash
|
||||
$ mkdir build
|
||||
@ -33,50 +42,79 @@ $ cmake ..
|
||||
$ make
|
||||
```
|
||||
|
||||
## windows
|
||||
## Windows
|
||||
|
||||
If you use qtcreator IDE you can just open `CMakeLists.txt` file in root directory.
|
||||
If you are using QtCreator IDE you can just open `CMakeLists.txt` file in root directory.
|
||||
If you are using Visual Studio you can generate solution by cmake generator command.
|
||||
|
||||
If you use Visual Studio you can generate solution by cmake command. In this case you should specify path to cmake scripts in Qt5 dir (usually in lib/cmake subdir), for example:
|
||||
### Way 1
|
||||
Specify path to cmake scripts in Qt5 dir (usually in lib/cmake subdir) and execute cmake generator command,
|
||||
for example:
|
||||
```batch
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -DCMAKE_PREFIX_PATH="C:\Qt\5.3\msvc2013_64\lib\cmake" ..
|
||||
$ cmake -DCMAKE_PREFIX_PATH="C:\Qt\5.3\msvc2013_64\lib\cmake" .. -G "Visual Studio 12 2013 Win64"
|
||||
```
|
||||
|
||||
### Way 2
|
||||
Create system variable "Qt5Widgets_DIR" and set it's value to "[path-to-Qt5-binaries]\lib\cmake\Qt5Widgets".
|
||||
For example, "C:\Qt\5.3\msvc2013_64\lib\cmake\Qt5Widgets".
|
||||
And then run cmake generator as follows:
|
||||
```batch
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake .. -G "Visual Studio 12 2013 Win64"
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
First of all you can specify path to include directory which contains `include/profiler` directory. For linking with ease_profiler you can specify path to library.
|
||||
First of all you can specify path to include directory which contains `include/profiler` directory.
|
||||
For linking with easy_profiler you can specify path to library.
|
||||
|
||||
Example of usage.
|
||||
|
||||
This code snippet will generate block with function name and grouped it in Magenta group:
|
||||
This code snippet will generate block with function name and Magenta color:
|
||||
```cpp
|
||||
#include <profiler/profiler.h>
|
||||
|
||||
void frame(){
|
||||
EASY_FUNCTION(profiler::colors::Magenta);
|
||||
void frame() {
|
||||
EASY_FUNCTION(profiler::colors::Magenta); // Magenta block with name "frame"
|
||||
prepareRender();
|
||||
calculatePhysics();
|
||||
}
|
||||
```
|
||||
To profile any block you may do this as following. You can specify these blocks also with Google material design color or just set name of block (in this case color will be OrangeA100):
|
||||
|
||||
To profile any block you may do this as following.
|
||||
You can specify these blocks also with Google material design colors or just set name of the block
|
||||
(in this case it will have default color which is `Amber100`):
|
||||
```cpp
|
||||
#include <profiler/profiler.h>
|
||||
|
||||
void frame(){
|
||||
//some code
|
||||
EASY_BLOCK("Calculating summ");
|
||||
for(int i = 0; i < 10; i++){
|
||||
void frame() {
|
||||
// some code
|
||||
EASY_BLOCK("Calculating sum"); // Block with default color
|
||||
int sum = 0;
|
||||
for (int i = 0; i < 10; ++i)
|
||||
sum += i;
|
||||
}
|
||||
EASY_END_BLOCK;
|
||||
|
||||
EASY_BLOCK("Calculating multiplication", profiler::colors::Blue50);
|
||||
for(int i = 0; i < 10; i++){
|
||||
EASY_BLOCK("Calculating multiplication", profiler::colors::Blue500); // Blue block
|
||||
int mul = 1;
|
||||
for (int i = 1; i < 11; ++i)
|
||||
mul *= i;
|
||||
}
|
||||
EASY_END_BLOCK;
|
||||
}
|
||||
```
|
||||
|
||||
You can also use your own colors. easy_profiler is using standard 32-bit ARGB color format.
|
||||
Example:
|
||||
```cpp
|
||||
#include <profiler/profiler.h>
|
||||
|
||||
void foo() {
|
||||
EASY_FUNCTION(0xfff080aa); // Function block with custom color
|
||||
// some code
|
||||
}
|
||||
```
|
||||
|
||||
[![Analytics](https://ga-beacon.appspot.com/UA-82899176-1/easy_profiler/readme)](https://github.com/yse/easy_profiler)
|
||||
|
@ -19,75 +19,55 @@ 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
|
||||
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
|
||||
#endif
|
||||
|
||||
#ifndef FULL_DISABLE_PROFILER
|
||||
|
||||
#include <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 EasyProfiler
|
||||
*/
|
||||
|
||||
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.
|
||||
|
||||
/** Macro of beginning of block with custom name and color.
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_STORAGE_EXPAND_ENABLED true
|
||||
|
||||
/** If true then EasyProfiler event tracing is enabled by default
|
||||
and will be turned on and off when you call profiler::setEnabled().
|
||||
Otherwise, it have to be turned on via GUI and then it will be
|
||||
turned on/off with next calls of profiler::setEnabled().
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_EVENT_TRACING_ENABLED true
|
||||
|
||||
/** Macro for beginning of a block with custom name and color.
|
||||
|
||||
\code
|
||||
#include "profiler/profiler.h"
|
||||
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 +76,10 @@ namespace profiler {
|
||||
EASY_BLOCK("Calling baz()", profiler::colors::Red); // Red block
|
||||
baz();
|
||||
}
|
||||
EASY_END_BLOCK; // End of "Check something" block (Even if "Check something" is disabled, this EASY_END_BLOCK will not end any other block).
|
||||
|
||||
EASY_BLOCK("Some another block", profiler::colors::Blue, profiler::DISABLED); // Disabled block with Blue color
|
||||
// some another code...
|
||||
}
|
||||
\endcode
|
||||
|
||||
@ -103,13 +87,13 @@ Block will be automatically completed by destructor.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_BLOCK(name, ...)\
|
||||
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
|
||||
# define EASY_BLOCK(name, ...)\
|
||||
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
|
||||
EASY_UNIQUE_LINE_ID, 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
|
||||
|
||||
/** Macro of beginning of block with function name and custom color.
|
||||
/** Macro for beginning of a block with function name and custom color.
|
||||
|
||||
\code
|
||||
#include "profiler/profiler.h"
|
||||
@ -122,19 +106,24 @@ Block will be automatically completed by destructor.
|
||||
EASY_FUNCTION(profiler::colors::Green); // Green block with name="bar"
|
||||
//some code...
|
||||
}
|
||||
|
||||
void baz(){
|
||||
EASY_FUNCTION(profiler::DISABLED); // Disabled block with name="baz" and default color (There is possibility to enable this block later via GUI)
|
||||
// som code...
|
||||
}
|
||||
\endcode
|
||||
|
||||
Name of the block automatically created with function name.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_FUNCTION(...)\
|
||||
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(__func__, __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_BLOCK , ## __VA_ARGS__);\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_BLOCK, EASY_UNIQUE_DESC(__LINE__), "");\
|
||||
# define EASY_FUNCTION(...)\
|
||||
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
|
||||
EASY_UNIQUE_LINE_ID, __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
|
||||
|
||||
/** Macro of completion of last nearest open block.
|
||||
/** Macro for completion of last opened block.
|
||||
|
||||
\code
|
||||
#include "profiler/profiler.h"
|
||||
@ -157,9 +146,9 @@ int foo()
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_END_BLOCK ::profiler::endBlock();
|
||||
# define EASY_END_BLOCK ::profiler::endBlock();
|
||||
|
||||
/** Macro of creating event with custom name and color.
|
||||
/** Macro for creating event with custom name and color.
|
||||
|
||||
Event is a block with zero duration and special type.
|
||||
|
||||
@ -168,74 +157,91 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_EVENT(name, ...)\
|
||||
static const ::profiler::block_id_t EASY_UNIQUE_DESC(__LINE__) = ::profiler::registerDescription(EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_EVENT , ## __VA_ARGS__);\
|
||||
::profiler::Block EASY_UNIQUE_BLOCK(__LINE__)(::profiler::BLOCK_TYPE_EVENT, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
|
||||
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
|
||||
# define EASY_EVENT(name, ...)\
|
||||
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(\
|
||||
::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\
|
||||
__FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\
|
||||
::profiler::storeEvent(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);
|
||||
|
||||
/** Macro of naming current thread.
|
||||
|
||||
If this thread has been already named then nothing changes.
|
||||
/** Macro for enabling profiler.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_THREAD(name)\
|
||||
# define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
|
||||
|
||||
/** Macro for disabling profiler.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_PROFILER_DISABLE ::profiler::setEnabled(false);
|
||||
|
||||
/** Macro for current thread registration.
|
||||
|
||||
\note If this thread has been already registered then nothing happens.
|
||||
|
||||
\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__);
|
||||
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::registerThread(name);
|
||||
|
||||
/** Macro of naming main thread.
|
||||
/** Macro for main thread registration.
|
||||
|
||||
This is only for user comfort. There is no difference for EasyProfiler GUI between different threads.
|
||||
This is just for user's comfort. There is no difference for EasyProfiler GUI between different threads.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
#define EASY_MAIN_THREAD EASY_THREAD("Main")
|
||||
# define EASY_MAIN_THREAD EASY_THREAD("Main")
|
||||
|
||||
# ifndef _WIN32
|
||||
/** Macro for setting temporary log-file path for Unix event tracing system.
|
||||
|
||||
\note Default value is "/tmp/cs_profiling_info.log".
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_EVENT_TRACING_SET_LOG(filename) ::profiler::setContextSwitchLogFilename(filename);
|
||||
|
||||
/** Macro returning current path to the temporary log-file for Unix event tracing system.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_EVENT_TRACING_LOG ::profiler::getContextSwitchLogFilename();
|
||||
# endif
|
||||
|
||||
#else
|
||||
#define EASY_BLOCK(...)
|
||||
#define EASY_FUNCTION(...)
|
||||
#define EASY_END_BLOCK
|
||||
#define EASY_PROFILER_ENABLE
|
||||
#define EASY_PROFILER_DISABLE
|
||||
#define EASY_EVENT(...)
|
||||
#define EASY_THREAD(...)
|
||||
#define EASY_MAIN_THREAD
|
||||
# define EASY_MEASURE_STORAGE_EXPAND 0
|
||||
# define EASY_STORAGE_EXPAND_ENABLED false
|
||||
# define EASY_EVENT_TRACING_ENABLED false
|
||||
# define EASY_BLOCK(...)
|
||||
# define EASY_FUNCTION(...)
|
||||
# define EASY_END_BLOCK
|
||||
# define EASY_PROFILER_ENABLE
|
||||
# define EASY_PROFILER_DISABLE
|
||||
# define EASY_EVENT(...)
|
||||
# define EASY_THREAD(...)
|
||||
# define EASY_MAIN_THREAD
|
||||
|
||||
# ifndef _WIN32
|
||||
# define EASY_EVENT_TRACING_SET_LOG(filename)
|
||||
# define EASY_EVENT_TRACING_LOG ""
|
||||
# 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 ThreadStorage;
|
||||
|
||||
namespace profiler {
|
||||
|
||||
const uint32_t DEFAULT_PORT = 28077;
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Core types
|
||||
|
||||
class Block;
|
||||
const uint16_t DEFAULT_PORT = 28077;
|
||||
|
||||
typedef uint64_t timestamp_t;
|
||||
typedef uint32_t thread_id_t;
|
||||
@ -244,58 +250,40 @@ namespace profiler {
|
||||
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;
|
||||
|
||||
extern "C" {
|
||||
PROFILER_API block_id_t registerDescription(const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color = DefaultBlockColor);
|
||||
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();
|
||||
|
||||
PROFILER_API void startListenSignalToCapture();
|
||||
PROFILER_API void stopListenSignalToCapture();
|
||||
}
|
||||
//***********************************************
|
||||
|
||||
#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(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:
|
||||
|
||||
inline block_id_t id() const { return m_id; }
|
||||
inline int line() const { return m_line; }
|
||||
inline block_type_t type() const { return m_type; }
|
||||
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
|
||||
{
|
||||
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
|
||||
}; // END of class BaseBlockDescriptor.
|
||||
|
||||
public:
|
||||
|
||||
BlockDescriptor(uint64_t& _used_mem, 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
|
||||
{
|
||||
@ -322,37 +310,184 @@ 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
|
||||
bool* m_pEnable; ///< Pointer to the enable flag in unordered_map
|
||||
bool m_expired; ///< Is this descriptor expired
|
||||
|
||||
BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
|
||||
|
||||
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);
|
||||
inline bool isFinished() const { return m_end >= m_begin; }
|
||||
void finish(timestamp_t _time);
|
||||
inline bool finished() const { return m_end >= m_begin; }
|
||||
inline bool enabled() const { return m_enabled; }
|
||||
|
||||
public:
|
||||
|
||||
Block(Block&& that);
|
||||
Block(block_type_t _block_type, block_id_t _id, const char* _name);
|
||||
Block(timestamp_t _begin_time, block_type_t _block_type, block_id_t _id, const char* _name);
|
||||
Block(const BaseBlockDescriptor& _desc, const char* _runtimeName);
|
||||
Block(timestamp_t _begin_time, block_id_t _id, const char* _runtimeName);
|
||||
~Block();
|
||||
|
||||
inline const char* name() const { return m_name; }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Block(const Block&) = delete;
|
||||
Block& operator = (const Block&) = delete;
|
||||
|
||||
}; // END of class Block.
|
||||
|
||||
//***********************************************
|
||||
|
||||
class PROFILER_API BlockDescRef final
|
||||
{
|
||||
const BaseBlockDescriptor& m_desc;
|
||||
|
||||
public:
|
||||
|
||||
explicit BlockDescRef(const BaseBlockDescriptor& _desc) : m_desc(_desc) { }
|
||||
explicit 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
|
||||
// Note: it is better to use macros defined above than a direct calls to API.
|
||||
|
||||
extern "C" {
|
||||
|
||||
/** Registers static description of a block.
|
||||
|
||||
It is general information which is common for all such blocks.
|
||||
Includes color, block type (see BlockType), file-name, line-number, compile-time name of a block and enable-flag.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API const BaseBlockDescriptor* registerDescription(bool _enabled, const char* _autogenUniqueId, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color);
|
||||
|
||||
/** Stores event in the blocks list.
|
||||
|
||||
An event ends instantly and has zero duration.
|
||||
|
||||
\param _desc Reference to the previously registered description.
|
||||
\param _runtimeName Standard zero-terminated string which will be copied to the events buffer.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName);
|
||||
|
||||
/** Begins block.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API void beginBlock(Block& _block);
|
||||
|
||||
/** Ends last started block.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API void endBlock();
|
||||
|
||||
/** Enable or disable profiler.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API void setEnabled(bool _isEnable);
|
||||
|
||||
/** Save all gathered blocks into file.
|
||||
|
||||
\note This also disables profiler.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API uint32_t dumpBlocksToFile(const char* _filename);
|
||||
|
||||
/** Register current thread and give it a name.
|
||||
|
||||
\note Only first call of registerThread() for the current thread will have an effect.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API const char* registerThread(const char* _name);
|
||||
|
||||
#ifndef _WIN32
|
||||
/** Set temporary log-file path for Unix event tracing system.
|
||||
|
||||
\note Default value is "/tmp/cs_profiling_info.log".
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API void setContextSwitchLogFilename(const char* _name);
|
||||
|
||||
/** Returns current path to the temporary log-file for Unix event tracing system.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API const char* getContextSwitchLogFilename();
|
||||
#endif
|
||||
|
||||
PROFILER_API void startListenSignalToCapture();
|
||||
PROFILER_API void stopListenSignalToCapture();
|
||||
|
||||
}
|
||||
|
||||
inline void setEnabled(::profiler::EasyEnableFlag _isEnable) {
|
||||
setEnabled(_isEnable == ::profiler::ENABLED);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
#if defined ( __clang__ )
|
||||
#pragma clang diagnostic pop
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // EASY_PROFILER____H_______
|
||||
|
183
include/profiler/profiler_aux.h
Normal file
@ -0,0 +1,183 @@
|
||||
/************************************************************************
|
||||
* 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
|
||||
};
|
||||
|
||||
struct passthrough_hash final {
|
||||
template <class T> inline size_t operator () (T _value) const {
|
||||
return static_cast<size_t>(_value);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef FULL_DISABLE_PROFILER
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
# define EASY_STRINGIFY(a) #a
|
||||
# define EASY_STRINGIFICATION(a) EASY_STRINGIFY(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(sizeof...(TArgs) < 2, "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_STRINGIFICATION(__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_______
|
@ -26,77 +26,24 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace profiler {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef uint32_t color_t; // Standard four-byte ARGB color format
|
||||
|
||||
//typedef uint8_t color_t; // One-byte RGB color format: RRR-GGG-BB
|
||||
//typedef uint32_t rgb32_t; // Standard four-byte ARGB color format
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace colors {
|
||||
|
||||
// ///< Extracts [0 .. 224] Red value from one-byte RGB format. Possible values are: [0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xe0].
|
||||
// inline rgb32_t get_red(color_t color) { return color & 0xe0; }
|
||||
//
|
||||
// ///< Extracts [0 .. 224] Green value from one-byte RGB format. Possible values are: [0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xe0].
|
||||
// inline rgb32_t get_green(color_t color) { return (color & 0x1c) << 3; }
|
||||
//
|
||||
// ///< Extracts [0 .. 192] Blue value from one-byte RGB format. Possible values are: [0x0, 0x40, 0x80, 0xc0]
|
||||
// inline rgb32_t get_blue(color_t color) { return (color & 3) << 6; }
|
||||
//
|
||||
//
|
||||
// ///< Extracts [0 .. 255] Red value from four-byte RGB format.
|
||||
// inline rgb32_t rgb_red(rgb32_t color) { return (color & 0x00ff0000) >> 16; }
|
||||
//
|
||||
// ///< Extracts [0 .. 255] Green value from four-byte RGB format.
|
||||
// inline rgb32_t rgb_green(rgb32_t color) { return (color & 0x0000ff00) >> 8; }
|
||||
//
|
||||
// ///< Extracts [0 .. 255] Blue value from four-byte RGB format.
|
||||
// inline rgb32_t rgb_blue(rgb32_t color) { return color & 0x000000ff; }
|
||||
//
|
||||
// ///< Unpacks one-byte RGB value into standard four-byte RGB value.
|
||||
// inline rgb32_t convert_to_rgb(color_t color) { return (get_red(color) << 16) | ((color & 0x1c) << 11) | get_blue(color); }
|
||||
//
|
||||
// ///< Packs standard four-byte RGB value into one-byte RGB value. R & G values packed with 0x20 (32) step, B value is packed with 0x40 (64) step.
|
||||
// inline color_t from_rgb(rgb32_t color) { return (rgb_red(color) & 0xe0) | (((color & 0x0000ff00) >> 11) & 0x1c) | (rgb_blue(color) >> 6); }
|
||||
//
|
||||
// ///< Packs standard four-byte RGB value into one-byte RGB value. R & G values packed with 0x20 (32) step, B value is packed with 0x40 (64) step.
|
||||
// inline color_t from_rgb(color_t red, color_t green, color_t blue) { return (red & 0xe0) | ((green >> 3) & 0x1c) | (blue >> 6); }
|
||||
///< Change alpha for color. Only 8 major bytes (0xff000000) used from alpha.
|
||||
inline color_t modify_alpha32(color_t _color, color_t _alpha) {
|
||||
return (_alpha & 0xff000000) | (_color & 0x00ffffff);
|
||||
}
|
||||
|
||||
///< Change alpha for color.
|
||||
inline color_t modify_alpha8(color_t _color, uint8_t _alpha) {
|
||||
return (static_cast<color_t>(_alpha) << 24) | (_color & 0x00ffffff);
|
||||
}
|
||||
|
||||
// const color_t Black = 0x00; // 0x00000000
|
||||
// const color_t Random = Black; // Black // Currently GUI interprets Black color as permission to select random color for block
|
||||
// const color_t Lightgray = 0x6E; // 0x00606080
|
||||
// const color_t Darkgray = 0x25; // 0x00202040
|
||||
// const color_t White = 0xFF; // 0x00E0E0C0
|
||||
// const color_t Red = 0xE0; // 0x00E00000
|
||||
// const color_t Green = 0x1C; // 0x0000E000
|
||||
// const color_t Blue = 0x03; // 0x000000C0
|
||||
// const color_t Magenta = (Red | Blue); // 0x00E000C0
|
||||
// const color_t Cyan = (Green | Blue); // 0x0000E0C0
|
||||
// const color_t Yellow = (Red | Green); // 0x00E0E000
|
||||
// const color_t Darkred = 0x60; // 0x00600000
|
||||
// const color_t Darkgreen = 0x0C; // 0x00006000
|
||||
// const color_t Darkblue = 0x01; // 0x00000040
|
||||
// const color_t Darkmagenta = (Darkred | Darkblue); // 0x00600040
|
||||
// const color_t Darkcyan = (Darkgreen | Darkblue); // 0x00006040
|
||||
// const color_t Darkyellow = (Darkred | Darkgreen); // 0x00606000
|
||||
// const color_t Navy = 0x02; // 0x00000080
|
||||
// const color_t Teal = 0x12; // 0x00008080
|
||||
// const color_t Maroon = 0x80; // 0x00800000
|
||||
// const color_t Purple = 0x82; // 0x00800080
|
||||
// const color_t Olive = 0x90; // 0x00808000
|
||||
// const color_t Grey = 0x92; // 0x00808080
|
||||
// const color_t Silver = 0xDB; // 0x00C0C0C0
|
||||
// const color_t Orange = 0xF4; // 0x00E0A000
|
||||
// const color_t Coral = 0xF6; // 0x00E0A080
|
||||
// const color_t Brick = 0xED; // 0x00E06040
|
||||
// const color_t Clay = 0xD6; // 0x00C0A080
|
||||
// const color_t Skin = 0xFA; // 0x00E0C080
|
||||
// const color_t Palegold = 0xFE; // 0x00E0E080
|
||||
|
||||
///< Create color from ARGB components.
|
||||
inline color_t color(uint8_t _red, uint8_t _green, uint8_t _blue, uint8_t _alpha = 0xff) {
|
||||
return (static_cast<color_t>(_alpha) << 24) | (static_cast<color_t>(_red) << 16) | (static_cast<color_t>(_green) << 8) | static_cast<color_t>(_blue);
|
||||
}
|
||||
|
||||
|
||||
// Google Material Design colors
|
||||
@ -382,6 +329,7 @@ namespace profiler {
|
||||
|
||||
const color_t Red = Red500;
|
||||
const color_t DarkRed = Red900;
|
||||
const color_t Coral = Red200;
|
||||
const color_t RichRed = 0xffff0000;
|
||||
const color_t Pink = Pink500;
|
||||
const color_t Rose = PinkA100;
|
||||
@ -408,7 +356,6 @@ namespace profiler {
|
||||
const color_t Lime = Lime500;
|
||||
const color_t Olive = Lime900;
|
||||
const color_t Yellow = Yellow500;
|
||||
const color_t DarkYellow = DarkRed | DarkGreen;
|
||||
const color_t RichYellow = YellowA200;
|
||||
const color_t Amber = Amber500;
|
||||
const color_t Gold = Amber300;
|
||||
@ -417,20 +364,19 @@ namespace profiler {
|
||||
const color_t Skin = Orange100;
|
||||
const color_t DeepOrange = DeepOrange500;
|
||||
const color_t Brick = DeepOrange900;
|
||||
const color_t Coral = DeepOrange200;
|
||||
const color_t Brown = Brown500;
|
||||
const color_t DarkBrown = Brown900;
|
||||
const color_t CreamWhite = Brown50;
|
||||
const color_t CreamWhite = Orange50;
|
||||
const color_t Wheat = Amber100;
|
||||
const color_t Grey = Grey500;
|
||||
const color_t Dark = Grey900;
|
||||
const color_t Silver = Grey300;
|
||||
const color_t BlueGrey = BlueGrey500;
|
||||
|
||||
const color_t Default = Wheat;
|
||||
|
||||
} // END of namespace colors.
|
||||
|
||||
const color_t DefaultBlockColor = colors::OrangeA100;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
@ -21,8 +21,9 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <atomic>
|
||||
#include "profiler/profiler.h"
|
||||
#include "profiler/serialized_block.h"
|
||||
@ -42,14 +43,16 @@ namespace profiler {
|
||||
::profiler::timestamp_t max_duration; ///< Cached block->duration() value. TODO: Remove this if memory consumption will be too high
|
||||
::profiler::block_index_t min_duration_block; ///< Will be used in GUI to jump to the block with min duration
|
||||
::profiler::block_index_t max_duration_block; ///< Will be used in GUI to jump to the block with max duration
|
||||
::profiler::block_index_t parent_block; ///< Index of block which is "parent" for "per_parent_stats" or "frame" for "per_frame_stats" or thread-id for "per_thread_stats"
|
||||
::profiler::calls_number_t calls_number; ///< Block calls number
|
||||
|
||||
BlockStatistics(::profiler::timestamp_t _duration, ::profiler::block_index_t _block_index)
|
||||
explicit BlockStatistics(::profiler::timestamp_t _duration, ::profiler::block_index_t _block_index, ::profiler::block_index_t _parent_index)
|
||||
: total_duration(_duration)
|
||||
, min_duration(_duration)
|
||||
, max_duration(_duration)
|
||||
, min_duration_block(_block_index)
|
||||
, max_duration_block(_block_index)
|
||||
, parent_block(_parent_index)
|
||||
, calls_number(1)
|
||||
{
|
||||
}
|
||||
@ -94,12 +97,12 @@ namespace profiler {
|
||||
|
||||
BlocksTree(This&& that) : BlocksTree()
|
||||
{
|
||||
makeMove(::std::forward<This&&>(that));
|
||||
make_move(::std::forward<This&&>(that));
|
||||
}
|
||||
|
||||
This& operator = (This&& that)
|
||||
{
|
||||
makeMove(::std::forward<This&&>(that));
|
||||
make_move(::std::forward<This&&>(that));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@ -113,9 +116,7 @@ namespace profiler {
|
||||
bool operator < (const This& other) const
|
||||
{
|
||||
if (!node || !other.node)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return node->begin() < other.node->begin();
|
||||
}
|
||||
|
||||
@ -139,7 +140,7 @@ namespace profiler {
|
||||
BlocksTree(const This&) = delete;
|
||||
This& operator = (const This&) = delete;
|
||||
|
||||
void makeMove(This&& that)
|
||||
void make_move(This&& that)
|
||||
{
|
||||
if (per_thread_stats != that.per_thread_stats)
|
||||
release_stats(per_thread_stats);
|
||||
@ -167,33 +168,28 @@ namespace profiler {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define EASY_STORE_CSWITCH_SEPARATELY
|
||||
//#undef EASY_STORE_CSWITCH_SEPARATELY
|
||||
|
||||
class BlocksTreeRoot final
|
||||
{
|
||||
typedef BlocksTreeRoot This;
|
||||
|
||||
public:
|
||||
|
||||
BlocksTree::children_t children;
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
BlocksTree::children_t sync;
|
||||
#endif
|
||||
const char* thread_name;
|
||||
::profiler::thread_id_t thread_id;
|
||||
uint16_t depth;
|
||||
BlocksTree::children_t children; ///< List of children indexes
|
||||
BlocksTree::children_t sync; ///< List of context-switch events
|
||||
std::string thread_name; ///< Name of this thread
|
||||
::profiler::timestamp_t active_time; ///< Active time of this thread (sum of all children duration)
|
||||
::profiler::thread_id_t thread_id; ///< System Id of this thread
|
||||
uint16_t depth; ///< Maximum stack depth (number of levels)
|
||||
|
||||
BlocksTreeRoot() : thread_name(""), thread_id(0), depth(0)
|
||||
BlocksTreeRoot() : thread_name(""), active_time(0), thread_id(0), depth(0)
|
||||
{
|
||||
}
|
||||
|
||||
BlocksTreeRoot(This&& that)
|
||||
: children(::std::move(that.children))
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
, sync(::std::move(that.sync))
|
||||
#endif
|
||||
, thread_name(that.thread_name)
|
||||
, thread_name(::std::move(that.thread_name))
|
||||
, active_time(that.active_time)
|
||||
, thread_id(that.thread_id)
|
||||
, depth(that.depth)
|
||||
{
|
||||
@ -202,15 +198,26 @@ namespace profiler {
|
||||
This& operator = (This&& that)
|
||||
{
|
||||
children = ::std::move(that.children);
|
||||
#ifdef EASY_STORE_CSWITCH_SEPARATELY
|
||||
sync = ::std::move(that.sync);
|
||||
#endif
|
||||
thread_name = that.thread_name;
|
||||
thread_name = ::std::move(that.thread_name);
|
||||
active_time = that.active_time;
|
||||
thread_id = that.thread_id;
|
||||
depth = that.depth;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool got_name() const
|
||||
{
|
||||
//return thread_name && *thread_name != 0;
|
||||
return thread_name.front() != 0;
|
||||
}
|
||||
|
||||
inline const char* name() const
|
||||
{
|
||||
//return thread_name;
|
||||
return thread_name.c_str();
|
||||
}
|
||||
|
||||
bool operator < (const This& other) const
|
||||
{
|
||||
return thread_id < other.thread_id;
|
||||
@ -224,7 +231,7 @@ namespace profiler {
|
||||
}; // END of class BlocksTreeRoot.
|
||||
|
||||
typedef ::profiler::BlocksTree::blocks_t blocks_t;
|
||||
typedef ::std::map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot> thread_blocks_tree_t;
|
||||
typedef ::std::unordered_map<::profiler::thread_id_t, ::profiler::BlocksTreeRoot, ::profiler::passthrough_hash> thread_blocks_tree_t;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -21,6 +21,8 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "profiler/profiler.h"
|
||||
|
||||
class ThreadStorage;
|
||||
|
||||
namespace profiler {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -5,6 +5,7 @@ project(profiler_gui)
|
||||
#set(CMAKE_PREFIX_PATH f:/qt/5.5/5.6/msvc2013_64/lib/cmake)
|
||||
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
@ -33,6 +34,7 @@ add_executable(${PROJECT_NAME}
|
||||
#treemodel.cpp
|
||||
#treeitem.h
|
||||
#treeitem.cpp
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
if(UNIX)
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <QLabel>
|
||||
#include <QLayout>
|
||||
#include <stdlib.h>
|
||||
#include <unordered_set>
|
||||
#include "graphics_scrollbar.h"
|
||||
#include "profiler/reader.h"
|
||||
#include "common_types.h"
|
||||
@ -79,12 +80,13 @@ class EasyGraphicsItem : public QGraphicsItem
|
||||
Sublevels m_levels; ///< Arrays of items for each level
|
||||
|
||||
QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem)
|
||||
QString m_threadName; ///<
|
||||
const ::profiler::BlocksTreeRoot* m_pRoot; ///< Pointer to the root profiler block (thread block). Used by ProfTreeWidget to restore hierarchy.
|
||||
uint8_t m_index; ///< This item's index in the list of items of EasyGraphicsView
|
||||
|
||||
public:
|
||||
|
||||
EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot* _root);
|
||||
explicit EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root);
|
||||
virtual ~EasyGraphicsItem();
|
||||
|
||||
// Public virtual methods
|
||||
@ -97,6 +99,9 @@ public:
|
||||
|
||||
// Public non-virtual methods
|
||||
|
||||
const ::profiler::BlocksTreeRoot* root() const;
|
||||
const QString& threadName() const;
|
||||
|
||||
QRect getRect() const;
|
||||
|
||||
void setBoundingRect(qreal x, qreal y, qreal w, qreal h);
|
||||
@ -156,6 +161,7 @@ public:
|
||||
void getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const;
|
||||
|
||||
const ::profiler_gui::EasyBlockItem* intersect(const QPointF& _pos) const;
|
||||
const ::profiler_gui::EasyBlock* intersectEvent(const QPointF& _pos) const;
|
||||
|
||||
private:
|
||||
|
||||
@ -242,6 +248,7 @@ private:
|
||||
|
||||
///< Returns pointer to the EasyGraphicsView widget.
|
||||
const EasyGraphicsView* view() const;
|
||||
EasyGraphicsView* view();
|
||||
|
||||
}; // END of class EasyChronometerItem.
|
||||
|
||||
@ -266,34 +273,44 @@ EASY_QGRAPHICSITEM(EasyTimelineIndicatorItem);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class QGraphicsProxyWidget;
|
||||
|
||||
class EasyGraphicsView : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
typedef QGraphicsView Parent;
|
||||
typedef EasyGraphicsView This;
|
||||
typedef ::std::vector<EasyGraphicsItem*> Items;
|
||||
typedef ::std::unordered_set<int, ::profiler_gui::do_no_hash<int>::hasher_t> Keys;
|
||||
|
||||
Items m_items; ///< Array of all EasyGraphicsItem items
|
||||
::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem)
|
||||
QTimer m_flickerTimer; ///< Timer for flicking behavior
|
||||
QRectF m_visibleSceneRect; ///< Visible scene rectangle
|
||||
::profiler::timestamp_t m_beginTime; ///< Begin time of profiler session. Used to reduce values of all begin and end times of profiler blocks.
|
||||
qreal m_scale; ///< Current scale
|
||||
qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :(
|
||||
qreal m_timelineStep; ///<
|
||||
QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent)
|
||||
QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button
|
||||
Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons
|
||||
EasyGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget
|
||||
EasyChronometerItem* m_chronometerItem; ///< Pointer to the EasyChronometerItem which is displayed when you press right mouse button and move mouse left or right. This item is used to select blocks to display in tree widget.
|
||||
EasyChronometerItem* m_chronometerItemAux; ///< Pointer to the EasyChronometerItem which is displayed when you double click left mouse button and move mouse left or right. This item is used only to measure time.
|
||||
int m_flickerSpeedX; ///< Current flicking speed x
|
||||
int m_flickerSpeedY; ///< Current flicking speed y
|
||||
bool m_bDoubleClick; ///< Is mouse buttons double clicked
|
||||
bool m_bUpdatingRect; ///< Stub flag which is used to avoid excess calculations on some scene update (flicking, scaling and so on)
|
||||
bool m_bEmpty; ///< Indicates whether scene is empty and has no items
|
||||
Items m_items; ///< Array of all EasyGraphicsItem items
|
||||
Keys m_keys; ///< Pressed keys
|
||||
::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem)
|
||||
QTimer m_flickerTimer; ///< Timer for flicking behavior
|
||||
QTimer m_idleTimer; ///<
|
||||
QRectF m_visibleSceneRect; ///< Visible scene rectangle
|
||||
::profiler::timestamp_t m_beginTime; ///< Begin time of profiler session. Used to reduce values of all begin and end times of profiler blocks.
|
||||
qreal m_scale; ///< Current scale
|
||||
qreal m_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :(
|
||||
qreal m_timelineStep; ///<
|
||||
uint64_t m_idleTime; ///<
|
||||
QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent)
|
||||
QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button
|
||||
Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons
|
||||
EasyGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget
|
||||
EasyChronometerItem* m_chronometerItem; ///< Pointer to the EasyChronometerItem which is displayed when you press right mouse button and move mouse left or right. This item is used to select blocks to display in tree widget.
|
||||
EasyChronometerItem* m_chronometerItemAux; ///< Pointer to the EasyChronometerItem which is displayed when you double click left mouse button and move mouse left or right. This item is used only to measure time.
|
||||
QGraphicsProxyWidget* m_csInfoWidget; ///<
|
||||
int m_flickerSpeedX; ///< Current flicking speed x
|
||||
int m_flickerSpeedY; ///< Current flicking speed y
|
||||
int m_flickerCounterX;
|
||||
int m_flickerCounterY;
|
||||
bool m_bDoubleClick; ///< Is mouse buttons double clicked
|
||||
bool m_bUpdatingRect; ///< Stub flag which is used to avoid excess calculations on some scene update (flicking, scaling and so on)
|
||||
bool m_bEmpty; ///< Indicates whether scene is empty and has no items
|
||||
|
||||
public:
|
||||
|
||||
@ -307,6 +324,8 @@ public:
|
||||
void mouseDoubleClickEvent(QMouseEvent* _event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* _event) override;
|
||||
void mouseMoveEvent(QMouseEvent* _event) override;
|
||||
void keyPressEvent(QKeyEvent* _event) override;
|
||||
void keyReleaseEvent(QKeyEvent* _event) override;
|
||||
void resizeEvent(QResizeEvent* _event) override;
|
||||
|
||||
public:
|
||||
@ -324,6 +343,8 @@ signals:
|
||||
|
||||
// Signals
|
||||
|
||||
void sceneUpdated();
|
||||
void treeChanged();
|
||||
void intervalChanged(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _session_begin_time, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict);
|
||||
|
||||
private:
|
||||
@ -343,11 +364,12 @@ private slots:
|
||||
|
||||
// Private Slots
|
||||
|
||||
void updateScene();
|
||||
void repaintScene();
|
||||
void onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta);
|
||||
void onScrollbarValueChange(int);
|
||||
void onGraphicsScrollbarValueChange(qreal);
|
||||
void onFlickerTimeout();
|
||||
void onIdleTimeout();
|
||||
void onSelectedThreadChange(::profiler::thread_id_t _id);
|
||||
void onSelectedBlockChange(unsigned int _block_index);
|
||||
void onItemsEspandStateChange();
|
||||
@ -376,6 +398,16 @@ public:
|
||||
return m_timelineStep;
|
||||
}
|
||||
|
||||
inline qreal chronoTime() const
|
||||
{
|
||||
return m_chronometerItem->width();
|
||||
}
|
||||
|
||||
inline qreal chronoTimeAux() const
|
||||
{
|
||||
return m_chronometerItemAux->width();
|
||||
}
|
||||
|
||||
//private:
|
||||
|
||||
// Private inline methods
|
||||
@ -396,23 +428,67 @@ public:
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class EasyThreadViewWidget : public QWidget
|
||||
class EasyThreadNameItem : public QGraphicsItem
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
EasyGraphicsView* m_view;
|
||||
QLabel* m_label;
|
||||
typedef EasyThreadViewWidget This;
|
||||
|
||||
QHBoxLayout *m_layout;
|
||||
QRectF m_boundingRect; ///< boundingRect (see QGraphicsItem)
|
||||
|
||||
public:
|
||||
EasyThreadViewWidget(QWidget *parent, EasyGraphicsView* view);
|
||||
virtual ~EasyThreadViewWidget();
|
||||
public slots:
|
||||
void onSelectedThreadChange(::profiler::thread_id_t _id);
|
||||
|
||||
explicit EasyThreadNameItem();
|
||||
virtual ~EasyThreadNameItem();
|
||||
|
||||
// Public virtual methods
|
||||
|
||||
QRectF boundingRect() const override;
|
||||
|
||||
void paint(QPainter* _painter, const QStyleOptionGraphicsItem* _option, QWidget* _widget = nullptr) override;
|
||||
|
||||
public:
|
||||
|
||||
// Public non-virtual methods
|
||||
|
||||
void setBoundingRect(const QRectF& _rect);
|
||||
|
||||
};
|
||||
|
||||
class EasyThreadNamesWidget : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
typedef QGraphicsView Parent;
|
||||
typedef EasyThreadNamesWidget This;
|
||||
|
||||
EasyGraphicsView* m_view;
|
||||
const int m_additionalHeight;
|
||||
|
||||
public:
|
||||
|
||||
explicit EasyThreadNamesWidget(EasyGraphicsView* _view, int _additionalHeight, QWidget* _parent = nullptr);
|
||||
virtual ~EasyThreadNamesWidget();
|
||||
|
||||
void mousePressEvent(QMouseEvent* _event) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent* _event) override;
|
||||
void mouseReleaseEvent(QMouseEvent* _event) override;
|
||||
void mouseMoveEvent(QMouseEvent* _event) override;
|
||||
void keyPressEvent(QKeyEvent* _event) override;
|
||||
void keyReleaseEvent(QKeyEvent* _event) override;
|
||||
|
||||
const EasyGraphicsView* view() const
|
||||
{
|
||||
return m_view;
|
||||
}
|
||||
|
||||
private slots:
|
||||
|
||||
void setVerticalScrollbarRange(int _minValue, int _maxValue);
|
||||
void onTreeChange();
|
||||
void onSelectedThreadChange(::profiler::thread_id_t _id);
|
||||
void repaintScene();
|
||||
|
||||
}; // END of class EasyThreadNamesWidget.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class EasyGraphicsViewWidget : public QWidget
|
||||
@ -421,9 +497,9 @@ class EasyGraphicsViewWidget : public QWidget
|
||||
|
||||
private:
|
||||
|
||||
EasyGraphicsView* m_view;
|
||||
EasyGraphicsScrollbar* m_scrollbar;
|
||||
//EasyThreadViewWidget* m_threadWidget;
|
||||
EasyGraphicsScrollbar* m_scrollbar;
|
||||
EasyGraphicsView* m_view;
|
||||
EasyThreadNamesWidget* m_threadNamesWidget;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
************************************************************************/
|
||||
|
||||
#include <QMenu>
|
||||
#include <QHeaderView>
|
||||
#include <QContextMenuEvent>
|
||||
#include <QSignalBlocker>
|
||||
#include <QSettings>
|
||||
@ -78,44 +79,73 @@ EasyTreeWidget::EasyTreeWidget(QWidget* _parent)
|
||||
setSortingEnabled(false);
|
||||
setColumnCount(COL_COLUMNS_NUMBER);
|
||||
|
||||
auto header = new QTreeWidgetItem();
|
||||
auto header_item = new QTreeWidgetItem();
|
||||
auto f = header()->font();
|
||||
f.setBold(true);
|
||||
header()->setFont(f);// ::profiler_gui::EFont("Helvetica", 9, QFont::Bold));
|
||||
|
||||
header->setText(COL_NAME, "Name");
|
||||
header_item->setText(COL_NAME, "Name");
|
||||
|
||||
header->setText(COL_BEGIN, "Begin, ms");
|
||||
header_item->setText(COL_BEGIN, "Begin, ms");
|
||||
|
||||
header->setText(COL_DURATION, "Duration");
|
||||
header->setText(COL_SELF_DURATION, "Self Dur.");
|
||||
//header->setToolTip(COL_SELF_DURATION, "");
|
||||
header->setText(COL_DURATION_SUM_PER_PARENT, "Tot. Dur./Parent");
|
||||
header->setText(COL_DURATION_SUM_PER_FRAME, "Tot. Dur./Frame");
|
||||
header->setText(COL_DURATION_SUM_PER_THREAD, "Tot. Dur./Thread");
|
||||
header_item->setText(COL_DURATION, "Duration");
|
||||
header_item->setText(COL_SELF_DURATION, "Self Dur.");
|
||||
//header_item->setToolTip(COL_SELF_DURATION, "");
|
||||
header_item->setText(COL_DURATION_SUM_PER_PARENT, "Tot. Dur./Parent");
|
||||
header_item->setText(COL_DURATION_SUM_PER_FRAME, "Tot. Dur./Frame");
|
||||
header_item->setText(COL_DURATION_SUM_PER_THREAD, "Tot. Dur./Thread");
|
||||
|
||||
header->setText(COL_SELF_DURATION_PERCENT, "Self %");
|
||||
header->setText(COL_PERCENT_PER_PARENT, "% / Parent");
|
||||
header->setText(COL_PERCENT_PER_FRAME, "% / Frame");
|
||||
header->setText(COL_PERCENT_SUM_PER_FRAME, "Tot. % / Frame");
|
||||
header->setText(COL_PERCENT_SUM_PER_PARENT, "Tot. % / Parent");
|
||||
header->setText(COL_PERCENT_SUM_PER_THREAD, "Tot. % / Thread");
|
||||
header_item->setText(COL_SELF_DURATION_PERCENT, "Self %");
|
||||
header_item->setText(COL_PERCENT_PER_PARENT, "% / Parent");
|
||||
header_item->setText(COL_PERCENT_PER_FRAME, "% / Frame");
|
||||
header_item->setText(COL_PERCENT_SUM_PER_FRAME, "Tot. % / Frame");
|
||||
header_item->setText(COL_PERCENT_SUM_PER_PARENT, "Tot. % / Parent");
|
||||
header_item->setText(COL_PERCENT_SUM_PER_THREAD, "Tot. % / Thread");
|
||||
|
||||
header->setText(COL_END, "End, ms");
|
||||
header_item->setText(COL_END, "End, ms");
|
||||
|
||||
header->setText(COL_MIN_PER_FRAME, "Min dur./Frame");
|
||||
header->setText(COL_MAX_PER_FRAME, "Max dur./Frame");
|
||||
header->setText(COL_AVERAGE_PER_FRAME, "Average dur./Frame");
|
||||
header->setText(COL_NCALLS_PER_FRAME, "N Calls/Frame");
|
||||
header_item->setText(COL_MIN_PER_FRAME, "Min dur./Frame");
|
||||
header_item->setText(COL_MAX_PER_FRAME, "Max dur./Frame");
|
||||
header_item->setText(COL_AVERAGE_PER_FRAME, "Average dur./Frame");
|
||||
header_item->setText(COL_NCALLS_PER_FRAME, "N Calls/Frame");
|
||||
|
||||
header->setText(COL_MIN_PER_PARENT, "Min dur./Parent");
|
||||
header->setText(COL_MAX_PER_PARENT, "Max dur./Parent");
|
||||
header->setText(COL_AVERAGE_PER_PARENT, "Average dur./Parent");
|
||||
header->setText(COL_NCALLS_PER_PARENT, "N Calls/Parent");
|
||||
header_item->setText(COL_MIN_PER_PARENT, "Min dur./Parent");
|
||||
header_item->setText(COL_MAX_PER_PARENT, "Max dur./Parent");
|
||||
header_item->setText(COL_AVERAGE_PER_PARENT, "Average dur./Parent");
|
||||
header_item->setText(COL_NCALLS_PER_PARENT, "N Calls/Parent");
|
||||
|
||||
header->setText(COL_MIN_PER_THREAD, "Min dur./Thread");
|
||||
header->setText(COL_MAX_PER_THREAD, "Max dur./Thread");
|
||||
header->setText(COL_AVERAGE_PER_THREAD, "Average dur./Thread");
|
||||
header->setText(COL_NCALLS_PER_THREAD, "N Calls/Thread");
|
||||
header_item->setText(COL_MIN_PER_THREAD, "Min dur./Thread");
|
||||
header_item->setText(COL_MAX_PER_THREAD, "Max dur./Thread");
|
||||
header_item->setText(COL_AVERAGE_PER_THREAD, "Average dur./Thread");
|
||||
header_item->setText(COL_NCALLS_PER_THREAD, "N Calls/Thread");
|
||||
|
||||
setHeaderItem(header);
|
||||
auto color = QColor::fromRgb(::profiler::colors::DeepOrange900);
|
||||
header_item->setForeground(COL_MIN_PER_THREAD, color);
|
||||
header_item->setForeground(COL_MAX_PER_THREAD, color);
|
||||
header_item->setForeground(COL_AVERAGE_PER_THREAD, color);
|
||||
header_item->setForeground(COL_NCALLS_PER_THREAD, color);
|
||||
header_item->setForeground(COL_PERCENT_SUM_PER_THREAD, color);
|
||||
header_item->setForeground(COL_DURATION_SUM_PER_THREAD, color);
|
||||
|
||||
color = QColor::fromRgb(::profiler::colors::Blue900);
|
||||
header_item->setForeground(COL_MIN_PER_FRAME, color);
|
||||
header_item->setForeground(COL_MAX_PER_FRAME, color);
|
||||
header_item->setForeground(COL_AVERAGE_PER_FRAME, color);
|
||||
header_item->setForeground(COL_NCALLS_PER_FRAME, color);
|
||||
header_item->setForeground(COL_PERCENT_SUM_PER_FRAME, color);
|
||||
header_item->setForeground(COL_DURATION_SUM_PER_FRAME, color);
|
||||
header_item->setForeground(COL_PERCENT_PER_FRAME, color);
|
||||
|
||||
color = QColor::fromRgb(::profiler::colors::Teal900);
|
||||
header_item->setForeground(COL_MIN_PER_PARENT, color);
|
||||
header_item->setForeground(COL_MAX_PER_PARENT, color);
|
||||
header_item->setForeground(COL_AVERAGE_PER_PARENT, color);
|
||||
header_item->setForeground(COL_NCALLS_PER_PARENT, color);
|
||||
header_item->setForeground(COL_PERCENT_SUM_PER_PARENT, color);
|
||||
header_item->setForeground(COL_DURATION_SUM_PER_PARENT, color);
|
||||
header_item->setForeground(COL_PERCENT_PER_PARENT, color);
|
||||
|
||||
setHeaderItem(header_item);
|
||||
|
||||
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange);
|
||||
connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::selectedBlockChanged, this, &This::onSelectedBlockChange);
|
||||
@ -329,9 +359,11 @@ void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
{
|
||||
action = menu.addAction("Expand all");
|
||||
connect(action, &QAction::triggered, this, &This::onExpandAllClicked);
|
||||
{ QIcon icon(":/Expand"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
action = menu.addAction("Collapse all");
|
||||
connect(action, &QAction::triggered, this, &This::onCollapseAllClicked);
|
||||
{ QIcon icon(":/Collapse"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
if (item != nullptr && col >= 0)
|
||||
{
|
||||
@ -339,9 +371,11 @@ void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
|
||||
action = menu.addAction("Expand all children");
|
||||
connect(action, &QAction::triggered, this, &This::onExpandAllChildrenClicked);
|
||||
{ QIcon icon(":/Expand"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
action = menu.addAction("Collapse all children");
|
||||
connect(action, &QAction::triggered, this, &This::onCollapseAllChildrenClicked);
|
||||
{ QIcon icon(":/Collapse"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
@ -351,6 +385,13 @@ void EasyTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
action->setCheckable(true);
|
||||
action->setChecked(m_bColorRows);
|
||||
connect(action, &QAction::triggered, this, &This::onColorizeRowsTriggered);
|
||||
if (m_bColorRows) {
|
||||
auto f = action->font();
|
||||
f.setBold(true);
|
||||
action->setFont(f);
|
||||
{ QIcon icon(":/Color"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
}
|
||||
else { QIcon icon(":/NoColor"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
if (item != nullptr && item->parent() != nullptr)
|
||||
{
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <unordered_map>
|
||||
#include <QRgb>
|
||||
#include <QString>
|
||||
#include <QFont>
|
||||
#include "profiler/reader.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -101,24 +102,33 @@ struct do_no_hash {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const QRgb DEFAULT_COLOR = profiler::DefaultBlockColor;// 0x00d4b494;
|
||||
|
||||
inline QRgb toRgb(unsigned int _red, unsigned int _green, unsigned int _blue)
|
||||
inline QRgb toRgb(uint32_t _red, uint32_t _green, uint32_t _blue)
|
||||
{
|
||||
return (_red << 16) + (_green << 8) + _blue;
|
||||
}
|
||||
|
||||
inline QRgb fromProfilerRgb(unsigned int _red, unsigned int _green, unsigned int _blue)
|
||||
inline QRgb fromProfilerRgb(uint32_t _red, uint32_t _green, uint32_t _blue)
|
||||
{
|
||||
if (_red == 0 && _green == 0 && _blue == 0)
|
||||
return DEFAULT_COLOR;
|
||||
return ::profiler::colors::Default;
|
||||
return toRgb(_red, _green, _blue) | 0x00141414;
|
||||
}
|
||||
|
||||
inline QRgb textColorForRgb(QRgb _color)
|
||||
inline bool isLightColor(::profiler::color_t _color)
|
||||
{
|
||||
const QRgb sum = 0xff - ((_color & 0xff000000) >> 24) + ((_color & 0x00ff0000) >> 16) + ((_color & 0x0000ff00) >> 8) + (_color & 0x000000ff);
|
||||
return sum > 0x215 ? ::profiler::colors::Black : ::profiler::colors::White;
|
||||
const auto sum = 255. - (((_color & 0x00ff0000) >> 16) * 0.299 + ((_color & 0x0000ff00) >> 8) * 0.587 + (_color & 0x000000ff) * 0.114);
|
||||
return sum < 76.5 || ((_color & 0xff000000) >> 24) < 0x80;
|
||||
}
|
||||
|
||||
inline bool isLightColor(::profiler::color_t _color, qreal _maxSum)
|
||||
{
|
||||
const auto sum = 255. - (((_color & 0x00ff0000) >> 16) * 0.299 + ((_color & 0x0000ff00) >> 8) * 0.587 + (_color & 0x000000ff) * 0.114);
|
||||
return sum < _maxSum || ((_color & 0xff000000) >> 24) < 0x80;
|
||||
}
|
||||
|
||||
inline ::profiler::color_t textColorForRgb(::profiler::color_t _color)
|
||||
{
|
||||
return isLightColor(_color) ? ::profiler::colors::Dark : ::profiler::colors::CreamWhite;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -129,7 +139,6 @@ struct EasyBlockItem final
|
||||
//const ::profiler::BlocksTree* block; ///< Pointer to profiler block
|
||||
qreal x; ///< x coordinate of the item (this is made qreal=double to avoid mistakes on very wide scene)
|
||||
float w; ///< Width of the item
|
||||
QRgb color; ///< Background color of the item
|
||||
::profiler::block_index_t block; ///< Index of profiler block
|
||||
uint32_t children_begin; ///< Index of first child item on the next sublevel
|
||||
uint16_t totalHeight; ///< Total height of the item including heights of all it's children
|
||||
@ -218,7 +227,7 @@ inline qreal timeFactor(qreal _interval)
|
||||
inline QString timeStringReal(qreal _interval, int _precision = 1)
|
||||
{
|
||||
if (_interval < 1) // interval in nanoseconds
|
||||
return QString("%1 ns").arg(_interval * 1e3, 0, 'f', _precision);
|
||||
return QString("%1 ns").arg(static_cast<quint32>(_interval * 1e3));
|
||||
|
||||
if (_interval < 1e3) // interval in microseconds
|
||||
return QString("%1 us").arg(_interval, 0, 'f', _precision);
|
||||
@ -261,6 +270,35 @@ template <class T> inline void set_max(T& _value) {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline double percentReal(::profiler::timestamp_t _partial, ::profiler::timestamp_t _total)
|
||||
{
|
||||
return 100. * static_cast<double>(_partial) / static_cast<double>(_total);
|
||||
}
|
||||
|
||||
inline int percent(::profiler::timestamp_t _partial, ::profiler::timestamp_t _total)
|
||||
{
|
||||
return static_cast<int>(0.5 + percentReal(_partial, _total));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
inline QFont EFont(QFont::StyleHint _hint, const char* _family, int _size, int _weight = -1)
|
||||
{
|
||||
QFont f;
|
||||
f.setStyleHint(_hint, QFont::PreferMatch);
|
||||
f.setFamily(_family);
|
||||
f.setPointSize(_size);
|
||||
f.setWeight(_weight);
|
||||
return f;
|
||||
}
|
||||
|
||||
inline QFont EFont(const char* _family, int _size, int _weight = -1)
|
||||
{
|
||||
return EFont(QFont::Helvetica, _family, _size, _weight);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
} // END of namespace profiler_gui.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -47,6 +47,7 @@ namespace profiler_gui {
|
||||
: selected_thread(0)
|
||||
, selected_block(::profiler_gui::numeric_max<decltype(selected_block)>())
|
||||
, chrono_text_position(ChronoTextPosition_Center)
|
||||
, enable_statistics(true)
|
||||
, draw_graphics_items_borders(true)
|
||||
, display_only_relevant_stats(true)
|
||||
, collapse_items_on_tree_close(false)
|
||||
|
@ -46,7 +46,7 @@ namespace profiler_gui {
|
||||
const QString ORGANAZATION_NAME = "EasyProfiler";
|
||||
const QString APPLICATION_NAME = "Easy profiler gui application";
|
||||
|
||||
const QColor CHRONOMETER_COLOR = QColor::fromRgba(0x402020c0);
|
||||
const QColor CHRONOMETER_COLOR = QColor::fromRgba(0x40000000 | (::profiler::colors::RichBlue & 0x00ffffff));// 0x402020c0);
|
||||
const QRgb SELECTED_THREAD_BACKGROUND = 0x00e0e060;
|
||||
const QRgb SELECTED_THREAD_FOREGROUND = 0x00ffffff - SELECTED_THREAD_BACKGROUND;
|
||||
|
||||
@ -84,6 +84,7 @@ namespace profiler_gui {
|
||||
::profiler::thread_id_t selected_thread; ///< Current selected thread id
|
||||
unsigned int selected_block; ///< Current selected profiler block index
|
||||
ChronometerTextPosition chrono_text_position; ///<
|
||||
bool enable_statistics; ///< Enable gathering and using statistics (Disable if you want to consume less memory)
|
||||
bool draw_graphics_items_borders; ///< Draw borders for graphics blocks or not
|
||||
bool display_only_relevant_stats; ///< Display only relevant information in ProfTreeWidget (excludes min, max, average times if there are only 1 calls number)
|
||||
bool collapse_items_on_tree_close; ///< Collapse all items which were displayed in the hierarchy tree after tree close/reset
|
||||
|
@ -40,8 +40,6 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const qreal SCALING_COEFFICIENT = 1.25;
|
||||
//const qreal SCALING_COEFFICIENT_INV = 1.0 / SCALING_COEFFICIENT;
|
||||
const int DEFAULT_TOP = -40;
|
||||
const int DEFAULT_HEIGHT = 80;
|
||||
const int INDICATOR_SIZE = 8;
|
||||
@ -515,14 +513,10 @@ void EasyGraphicsScrollbar::contextMenuEvent(QContextMenuEvent* _event)
|
||||
for (const auto& it : EASY_GLOBALS.profiler_blocks)
|
||||
{
|
||||
QString label;
|
||||
if (it.second.thread_name && it.second.thread_name[0] != 0)
|
||||
{
|
||||
label = ::std::move(QString("%1 Thread %2").arg(it.second.thread_name).arg(it.first));
|
||||
}
|
||||
if (it.second.got_name())
|
||||
label = ::std::move(QString("%1 Thread %2").arg(it.second.name()).arg(it.first));
|
||||
else
|
||||
{
|
||||
label = ::std::move(QString("Thread %1").arg(it.first));
|
||||
}
|
||||
|
||||
auto action = new QAction(label, nullptr);
|
||||
action->setData(it.first);
|
||||
@ -546,7 +540,7 @@ void EasyGraphicsScrollbar::onThreadActionClicked(bool)
|
||||
return;
|
||||
|
||||
const auto thread_id = action->data().toUInt();
|
||||
if (thread_id != m_minimap->threadId())
|
||||
if (thread_id != EASY_GLOBALS.selected_thread)
|
||||
{
|
||||
EASY_GLOBALS.selected_thread = thread_id;
|
||||
emit EASY_GLOBALS.events.selectedThreadChanged(thread_id);
|
||||
|
13
profiler_gui/icons/attribution.txt
Normal file
@ -0,0 +1,13 @@
|
||||
logo.svg - Icon made by Freepik from www.flaticon.com
|
||||
off.svg - Icon made by Freepik from www.flaticon.com
|
||||
open-folder.svg - Icon made by Freepik from www.flaticon.com
|
||||
reload.svg - Icon made by Freepik from www.flaticon.com
|
||||
expand.svg - Icon made by Freepik from www.flaticon.com
|
||||
collapse.svg - Icon made by Freepik from www.flaticon.com
|
||||
colors.svg - Icon made by Freepik from www.flaticon.com
|
||||
colors-black.svg - Icon made by Freepik from www.flaticon.com
|
||||
save.svg - Icon made by Freepik from www.flaticon.com
|
||||
statistics.svg - Icon made by Freepik from www.flaticon.com
|
||||
statistics2.svg - Icon made by Freepik from www.flaticon.com
|
||||
lan.svg - Icon made by Freepik from www.flaticon.com
|
||||
wifi.svg - Icon made by Freepik from www.flaticon.com
|
41
profiler_gui/icons/collapse.svg
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 210.002 210.002" style="enable-background:new 0 0 210.002 210.002;" xml:space="preserve">
|
||||
<path style="fill:#020202;" d="M195.765,76.815l-77.424,11.118l11.119-77.422l19.932,19.931l30.445-30.441l26.439,26.44
|
||||
l-30.441,30.444L195.765,76.815z M34.168,153.117L3.725,183.563l26.439,26.439l30.445-30.441l19.932,19.93l11.119-77.422
|
||||
l-77.422,11.119L34.168,153.117z M60.61,30.441L30.164,0L3.725,26.44l30.443,30.444l-19.93,19.931L91.66,87.933L80.541,10.511
|
||||
L60.61,30.441z M195.765,133.188l-77.424-11.119l11.119,77.422l19.932-19.93l30.445,30.441l26.439-26.439l-30.441-30.445
|
||||
L195.765,133.188z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
59
profiler_gui/icons/colors-black.svg
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 441.902 441.902" style="enable-background:new 0 0 441.902 441.902;" xml:space="preserve">
|
||||
<path d="M372.231,170.869c-4.097-2.365-8.303-4.509-12.595-6.438c0.471-4.651,0.713-9.369,0.713-14.143
|
||||
c0-76.864-62.534-139.397-139.397-139.397S81.553,73.425,81.553,150.289c0,4.773,0.242,9.492,0.713,14.143
|
||||
c-4.293,1.929-8.499,4.073-12.595,6.438C3.105,209.302-19.783,294.724,18.648,361.29c24.829,43.005,71.133,69.721,120.843,69.721
|
||||
c24.332,0,48.392-6.466,69.578-18.698c4.114-2.375,8.076-4.939,11.882-7.675c3.806,2.736,7.768,5.3,11.882,7.675
|
||||
c21.187,12.232,45.246,18.698,69.578,18.698c49.709,0,96.014-26.715,120.843-69.721
|
||||
C461.685,294.724,438.797,209.302,372.231,170.869z M101.553,150.289c0-65.836,53.562-119.397,119.397-119.397
|
||||
s119.397,53.562,119.397,119.397c0,2.377-0.078,4.735-0.216,7.078c-12.175-3.419-24.788-5.194-37.48-5.194
|
||||
c-29.746,0-58.267,9.573-81.702,26.471c-23.435-16.898-51.956-26.471-81.702-26.471c-12.692,0-25.306,1.775-37.48,5.194
|
||||
C101.631,155.024,101.553,152.665,101.553,150.289z M254.699,322.493c-5.869,21.903-17.558,41.245-33.749,56.329
|
||||
c-16.191-15.084-27.88-34.426-33.749-56.329c-3.367-12.566-4.654-25.361-3.917-37.981c11.986,3.368,24.618,5.174,37.666,5.174
|
||||
s25.68-1.807,37.666-5.174C259.353,297.133,258.066,309.927,254.699,322.493z M220.951,269.687c-12.088,0-23.76-1.812-34.765-5.168
|
||||
c2.646-11.307,6.974-22.286,12.945-32.627c5.947-10.3,13.335-19.508,21.82-27.431c8.486,7.923,15.873,17.13,21.82,27.431
|
||||
c5.97,10.34,10.298,21.32,12.945,32.627C244.711,267.875,233.039,269.687,220.951,269.687z M167.433,256.999
|
||||
c-31.244-15.733-54.677-44.784-62.787-79.655c11.197-3.412,22.864-5.173,34.603-5.173c23.75,0,46.588,7.124,65.837,19.809
|
||||
c-8.984,8.764-16.853,18.787-23.276,29.911C175.367,233.053,170.564,244.851,167.433,256.999z M260.092,221.892
|
||||
c-6.423-11.124-14.292-21.147-23.276-29.911c19.249-12.685,42.087-19.809,65.837-19.809c11.738,0,23.405,1.761,34.603,5.173
|
||||
c-8.109,34.87-31.543,63.921-62.787,79.655C271.338,244.851,266.535,233.053,260.092,221.892z M199.069,394.992
|
||||
c-18.15,10.479-38.752,16.018-59.578,16.018c-42.587,0-82.254-22.884-103.522-59.721c-32.917-57.015-13.313-130.182,43.703-163.1
|
||||
c2.042-1.179,4.12-2.286,6.221-3.34c10.581,41.328,39.68,75.319,77.896,92.57c-1.692,16.636-0.362,33.614,4.096,50.25
|
||||
c6.578,24.55,19.418,46.345,37.159,63.631C203.094,392.581,201.106,393.817,199.069,394.992z M405.933,351.29
|
||||
c-21.268,36.837-60.936,59.721-103.522,59.721c-20.826,0-41.428-5.539-59.578-16.018c-2.036-1.176-4.024-2.411-5.974-3.691
|
||||
c17.741-17.286,30.581-39.081,37.159-63.631c4.458-16.636,5.788-33.614,4.096-50.25c38.216-17.251,67.315-51.242,77.896-92.57
|
||||
c2.101,1.053,4.179,2.16,6.221,3.34C419.246,221.108,438.851,294.275,405.933,351.29z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.2 KiB |
73
profiler_gui/icons/colors.svg
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 297 297" style="enable-background:new 0 0 297 297;" xml:space="preserve">
|
||||
<g id="XMLID_28_">
|
||||
<g>
|
||||
<path d="M233.51,115.94c36.58,10.3,63.49,43.94,63.49,83.78c0,47.99-39.04,87.04-87.04,87.04c-23.97,0-45.7-9.74-61.46-25.47
|
||||
c-15.75,15.73-37.49,25.47-61.46,25.47C39.05,286.76,0,247.71,0,199.72c0-39.84,26.91-73.48,63.5-83.78
|
||||
c-1.32-6.01-2.04-12.25-2.04-18.66c0-47.99,39.05-87.04,87.04-87.04c48,0,87.04,39.05,87.04,87.04
|
||||
C235.54,103.69,234.83,109.93,233.51,115.94z M286.82,199.72c0-35.18-23.77-64.89-56.08-73.98c-9.28,26.75-31.26,47.6-58.69,55.32
|
||||
c1.32,6.01,2.03,12.25,2.03,18.66c0,20.36-7.04,39.09-18.8,53.93c13.94,14.14,33.3,22.92,54.68,22.92
|
||||
C252.34,276.57,286.82,242.1,286.82,199.72z M223.56,113.75c1.17-5.31,1.8-10.81,1.8-16.47c0-42.38-34.48-76.85-76.86-76.85
|
||||
S71.65,54.9,71.65,97.28c0,5.66,0.63,11.16,1.79,16.47c4.44-0.7,8.98-1.07,13.6-1.07c23.97,0,45.71,9.74,61.46,25.47
|
||||
c15.76-15.73,37.49-25.47,61.46-25.47C214.59,112.68,219.13,113.05,223.56,113.75z M220.71,123.63c-3.52-0.5-7.1-0.77-10.75-0.77
|
||||
c-21.38,0-40.74,8.78-54.68,22.92c6,7.58,10.77,16.17,14,25.48C193.1,164.56,212.27,146.67,220.71,123.63z M163.9,199.72
|
||||
c0-5.66-0.63-11.16-1.8-16.47c-4.43,0.7-8.97,1.07-13.6,1.07c-4.62,0-9.16-0.37-13.6-1.07c-1.16,5.31-1.79,10.81-1.79,16.47
|
||||
c0,17.27,5.73,33.24,15.39,46.09C158.16,232.96,163.9,216.99,163.9,199.72z M159.24,173.37c-2.61-7.13-6.25-13.77-10.74-19.74
|
||||
c-4.49,5.97-8.13,12.61-10.74,19.74c3.51,0.5,7.1,0.77,10.74,0.77C152.15,174.14,155.73,173.87,159.24,173.37z M127.73,171.26
|
||||
c3.23-9.31,7.99-17.9,14-25.48c-13.95-14.14-33.31-22.92-54.69-22.92c-3.64,0-7.23,0.27-10.74,0.77
|
||||
C84.73,146.67,103.91,164.56,127.73,171.26z M141.73,253.65c-11.76-14.84-18.81-33.57-18.81-53.93c0-6.41,0.72-12.65,2.04-18.66
|
||||
c-27.44-7.72-49.41-28.57-58.69-55.32c-32.32,9.09-56.08,38.8-56.08,73.98c0,42.38,34.47,76.85,76.85,76.85
|
||||
C108.42,276.57,127.78,267.79,141.73,253.65z"/>
|
||||
<path style="fill:#3498DB;" d="M230.74,125.74c32.31,9.09,56.08,38.8,56.08,73.98c0,42.38-34.48,76.85-76.86,76.85
|
||||
c-21.38,0-40.74-8.78-54.68-22.92c11.76-14.84,18.8-33.57,18.8-53.93c0-6.41-0.71-12.65-2.03-18.66
|
||||
C199.48,173.34,221.46,152.49,230.74,125.74z"/>
|
||||
<path style="fill:#D35400;" d="M225.36,97.28c0,5.66-0.63,11.16-1.8,16.47c-4.43-0.7-8.97-1.07-13.6-1.07
|
||||
c-23.97,0-45.7,9.74-61.46,25.47c-15.75-15.73-37.49-25.47-61.46-25.47c-4.62,0-9.16,0.37-13.6,1.07
|
||||
c-1.16-5.31-1.79-10.81-1.79-16.47c0-42.38,34.47-76.85,76.85-76.85S225.36,54.9,225.36,97.28z"/>
|
||||
<path style="fill:#5E345E;" d="M209.96,122.86c3.65,0,7.23,0.27,10.75,0.77c-8.44,23.04-27.61,40.93-51.43,47.63
|
||||
c-3.23-9.31-8-17.9-14-25.48C169.22,131.64,188.58,122.86,209.96,122.86z"/>
|
||||
<path style="fill:#1ABC9C;" d="M162.1,183.25c1.17,5.31,1.8,10.81,1.8,16.47c0,17.27-5.74,33.24-15.4,46.09
|
||||
c-9.66-12.85-15.39-28.82-15.39-46.09c0-5.66,0.63-11.16,1.79-16.47c4.44,0.7,8.98,1.07,13.6,1.07
|
||||
C153.13,184.32,157.67,183.95,162.1,183.25z"/>
|
||||
<path style="fill:#503B2C;" d="M148.5,153.63c4.49,5.97,8.13,12.61,10.74,19.74c-3.51,0.5-7.09,0.77-10.74,0.77
|
||||
c-3.64,0-7.23-0.27-10.74-0.77C140.37,166.24,144.01,159.6,148.5,153.63z"/>
|
||||
<path style="fill:#FFA800;" d="M141.73,145.78c-6.01,7.58-10.77,16.17-14,25.48c-23.82-6.7-43-24.59-51.43-47.63
|
||||
c3.51-0.5,7.1-0.77,10.74-0.77C108.42,122.86,127.78,131.64,141.73,145.78z"/>
|
||||
<path style="fill:#FFCD02;" d="M122.92,199.72c0,20.36,7.05,39.09,18.81,53.93c-13.95,14.14-33.31,22.92-54.69,22.92
|
||||
c-42.38,0-76.85-34.47-76.85-76.85c0-35.18,23.76-64.89,56.08-73.98c9.28,26.75,31.25,47.6,58.69,55.32
|
||||
C123.64,187.07,122.92,193.31,122.92,199.72z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.0 KiB |
41
profiler_gui/icons/expand.svg
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 210 210" style="enable-background:new 0 0 210 210;" xml:space="preserve">
|
||||
<path style="fill:#020202;" d="M34.774,152.509l26.441,26.442l19.93,19.931L3.724,210l11.119-77.422L34.774,152.509z
|
||||
M175.226,152.509l-30.445-30.441l-26.44,26.439l30.441,30.444l-19.93,19.931L206.276,210l-11.119-77.422L175.226,152.509z
|
||||
M65.22,122.067l26.439,26.439l-30.443,30.444l-26.441-26.442L65.22,122.067z M61.216,31.049l30.443,30.444L65.22,87.933
|
||||
L34.774,57.491L14.843,77.422L3.724,0l77.422,11.118L61.216,31.049z M118.341,61.493l30.441-30.444l-19.93-19.931L206.276,0
|
||||
l-11.119,77.422l-19.932-19.931L144.78,87.933L118.341,61.493z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
58
profiler_gui/icons/lan.svg
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 94.414 94.414" style="enable-background:new 0 0 94.414 94.414;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#010002;" d="M86.522,60.468v-3.16V38.34V19.372c0-1.736-1.421-3.16-3.164-3.16H36.25v2.827h43.068
|
||||
c1.514,0,2.759,1.242,2.759,2.759v33.069c0,1.525-1.245,2.759-2.759,2.759H24.179c-0.229,0-0.433-0.075-0.648-0.132v17.196h66.144
|
||||
c2.616,0,4.738-2.126,4.738-4.746L86.522,60.468z M58.07,73.112H45.426c-0.437,0-0.791-0.351-0.791-0.791
|
||||
c0-0.44,0.354-0.791,0.791-0.791H58.07c0.44,0,0.791,0.351,0.791,0.791C58.861,72.762,58.511,73.112,58.07,73.112z M38.716,69.952
|
||||
l2.412-2.895h21.237l2.416,2.895H38.716z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#010002;" d="M31.92,42.935H0V14.258c0-1.793,1.453-3.246,3.246-3.246c1.793,0,3.246,1.453,3.246,3.246v22.178
|
||||
h18.936V14.258c0-1.793,1.453-3.246,3.246-3.246c1.789,0,3.246,1.453,3.246,3.246C31.92,14.258,31.92,42.935,31.92,42.935z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path style="fill:#010002;" d="M12.712,31.784V14.262c0-1.793,1.453-3.246,3.246-3.246s3.246,1.453,3.246,3.246v17.522H12.712z"
|
||||
/>
|
||||
</g>
|
||||
<g>
|
||||
<path style="fill:#010002;" d="M15.962,83.402c-1.793,0-3.246-1.446-3.246-3.242V47.595h6.492v32.564
|
||||
C19.208,81.949,17.755,83.402,15.962,83.402z"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
51
profiler_gui/icons/logo.svg
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="705.299px" height="705.299px" viewBox="0 0 705.299 705.299" style="enable-background:new 0 0 705.299 705.299;"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M395.233,117.99V91.598l64.906,0.023v-5.55C460.151,38.549,421.636,0,374.08,0h-62.632
|
||||
c-47.511,0-86.06,38.549-86.06,86.071v5.515l66.343,0.023v26.163C152.565,141.993,46.651,263.051,46.651,409.157
|
||||
c0,163.594,132.571,296.142,296.107,296.142c163.537,0,296.107-132.548,296.107-296.142
|
||||
C638.876,263.557,533.698,142.786,395.233,117.99z M342.758,637.52c-125.907,0-228.339-102.433-228.339-228.362
|
||||
c0-125.896,102.433-228.305,228.339-228.305c125.895,0,228.339,102.41,228.339,228.305
|
||||
C571.097,535.087,468.665,637.52,342.758,637.52z"/>
|
||||
<path d="M651.987,153.333l-48.017-48.028c-4.274-4.286-10.065-6.688-16.098-6.688s-11.823,2.401-16.097,6.665l-38.929,38.939
|
||||
l80.246,80.2l38.894-38.917C660.869,176.612,660.869,162.227,651.987,153.333z"/>
|
||||
<path d="M341.724,195.237c-117.714,0.54-212.966,96.125-212.966,213.92c0,118.231,95.815,214.022,214.012,214.022
|
||||
c118.185,0,213.989-95.769,214-214H341.724V195.237z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
46
profiler_gui/icons/off.svg
Normal file
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 26.009 26.009" style="enable-background:new 0 0 26.009 26.009;" xml:space="preserve">
|
||||
<g>
|
||||
<g id="c192_off">
|
||||
<path d="M17.284,2.033v3.3c3.006,1.551,5.065,4.683,5.065,8.299c0,5.161-4.184,9.344-9.343,9.344c-5.16,0-9.346-4.183-9.346-9.344
|
||||
c0-3.316,1.735-6.223,4.34-7.881V2.32C3.662,4.242,0.63,8.581,0.63,13.632c0,6.834,5.539,12.377,12.374,12.377
|
||||
c6.834,0,12.374-5.543,12.374-12.377C25.378,8.304,22.003,3.779,17.284,2.033z"/>
|
||||
<path d="M12.766,15.229c1.359,0,2.457-0.803,2.457-1.79V1.795C15.224,0.804,14.125,0,12.766,0c-1.357,0-2.456,0.804-2.456,1.795
|
||||
V13.44C10.311,14.427,11.41,15.229,12.766,15.229z"/>
|
||||
</g>
|
||||
<g id="Capa_1_192_">
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
41
profiler_gui/icons/open-folder.svg
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 17.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 250.604 250.604" style="enable-background:new 0 0 250.604 250.604;" xml:space="preserve">
|
||||
<path d="M66.34,115.302h173.065c4.148,0,6.846,2.708,8.084,4.322c2.898,3.776,3.847,8.949,2.537,13.839l-23.92,89.297
|
||||
c-1.528,5.707-5.796,9.542-10.619,9.542H42.422c-4.148,0-6.846-2.708-8.084-4.322c-2.898-3.776-3.847-8.949-2.537-13.839
|
||||
l23.919-89.297C57.249,119.137,61.517,115.302,66.34,115.302z M17.312,210.26l23.919-89.297
|
||||
c3.31-12.358,13.399-20.661,25.108-20.661H229v-42c0-4.143-2.524-7-6.667-7H130v-2.633c0-17.021-13.014-30.367-30.033-30.367H31.2
|
||||
C14.18,18.302,0,31.649,0,48.669v9.633v33v115.5c0,4.143,3.69,7.5,7.833,7.5h8.684C16.697,212.947,16.954,211.597,17.312,210.26z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
75
profiler_gui/icons/reload.svg
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 502.707 502.707" style="enable-background:new 0 0 502.707 502.707;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path style="fill:#010002;" d="M203.758,153.541l37.576,73.686L297.03,55.674L125.456,0.022l37.274,73.125
|
||||
C75.002,104.467,12.166,188.054,12.166,286.525c0,101.383,66.546,187.17,158.351,216.161
|
||||
c-55.307-34.902-92.15-96.378-92.15-166.526C78.345,252.788,130.525,182.208,203.758,153.541z"/>
|
||||
<path style="fill:#010002;" d="M298.993,349.209l-37.598-73.707l-55.631,171.531l171.509,55.674l-37.296-73.147
|
||||
c87.75-31.278,150.564-114.886,150.564-213.4C490.541,114.8,424.017,29.013,332.212,0c55.286,34.902,92.172,96.357,92.172,166.526
|
||||
C424.405,249.941,372.225,320.477,298.993,349.209z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
43
profiler_gui/icons/save.svg
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 284.515 284.515" style="enable-background:new 0 0 284.515 284.515;" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M282.166,27.382L259.88,2.937C258.174,1.066,255.76,0,253.229,0h-39.936H71.221H9C4.03,0,0,4.029,0,9v266.515
|
||||
c0,4.971,4.029,9,9,9h266.514c4.971,0,9-4.029,9-9V33.446C284.514,31.203,283.676,29.04,282.166,27.382z M204.293,18v69.443
|
||||
h-35.951V18H204.293z M150.342,18v69.443H80.221V18H150.342z M220.581,266.515H63.934V159.44h156.646V266.515z M266.514,266.515
|
||||
h-27.934V150.44c0-4.971-4.029-9-9-9H54.934c-4.971,0-9,4.029-9,9v116.074H18V18h44.221v78.443c0,4.971,4.029,9,9,9h142.072
|
||||
c4.971,0,9-4.029,9-9V18h26.962l17.259,18.933V266.515z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
47
profiler_gui/icons/statistics.svg
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="372.465px" height="372.465px" viewBox="0 0 372.465 372.465" style="enable-background:new 0 0 372.465 372.465;"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M101.98,186.441H30.484c-5.523,0-10,4.477-10,10v166.021c0,5.523,4.477,10,10,10h71.496c5.523,0,10-4.477,10-10V196.441
|
||||
C111.98,190.919,107.503,186.441,101.98,186.441z"/>
|
||||
<path d="M221.98,0h-71.496c-5.523,0-10,4.477-10,10v352.465c0,5.521,4.477,10,10,10h71.496c5.523,0,10-4.479,10-10V10
|
||||
C231.98,4.477,227.504,0,221.98,0z"/>
|
||||
<path d="M341.98,115.257h-71.496c-5.523,0-10,4.477-10,10v237.208c0,5.521,4.477,10,10,10h71.496c5.523,0,10-4.479,10-10V125.257
|
||||
C351.98,119.734,347.504,115.257,341.98,115.257z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
48
profiler_gui/icons/statistics2.svg
Normal file
@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 482 482" style="enable-background:new 0 0 482 482;" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M123.7,216.8H13.5c-7.5,0-13.5,6-13.5,13.5v225.4c0,7.5,6,13.5,13.5,13.5h110.2c7.5,0,13.5-6,13.5-13.5V230.3
|
||||
C137.2,222.8,131.2,216.8,123.7,216.8z M110.2,442.2H27V243.8h83.2V442.2z"/>
|
||||
<path d="M296.1,12.8H185.9c-7.5,0-13.5,6-13.5,13.5v429.4c0,7.5,6,13.5,13.5,13.5h110.2c7.5,0,13.5-6,13.5-13.5V26.3
|
||||
C309.6,18.9,303.6,12.8,296.1,12.8z M199.4,39.8h25.3l-25.3,25.3V39.8z M282.6,442.2h-22.7l22.7-22.7V442.2z M282.6,381.3
|
||||
c-0.1,0.1-0.1,0.1-0.2,0.2l-60.7,60.7h-22.3v-37.5l83.2-83.2L282.6,381.3L282.6,381.3z M282.6,283.4c-0.1,0.1-0.1,0.1-0.2,0.2
|
||||
l-83,83v-67.7l83.2-83.2L282.6,283.4L282.6,283.4z M282.6,177.5c-0.1,0.1-0.1,0.1-0.2,0.2l-83,83v-57.5l83.2-83.2L282.6,177.5
|
||||
L282.6,177.5z M282.6,81.8c-0.1,0.1-0.1,0.1-0.2,0.2l-83,83v-61.7l63.1-63.1c0.1-0.1,0.2-0.3,0.4-0.4h19.7V81.8z"/>
|
||||
<path d="M468.5,153H358.3c-7.5,0-13.5,6-13.5,13.5v289.2c0,7.5,6,13.5,13.5,13.5h110.2c7.5,0,13.5-6,13.5-13.5V166.5
|
||||
C482,159,475.9,153,468.5,153z M455,442.2h-83.2V180H455V442.2z"/>
|
||||
</g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.5 KiB |
43
profiler_gui/icons/wifi.svg
Normal file
@ -0,0 +1,43 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 233.576 233.576" style="enable-background:new 0 0 233.576 233.576;" xml:space="preserve">
|
||||
<path d="M176.982,129.274c-16.066-16.113-37.442-24.986-60.193-24.986s-44.128,8.873-60.193,24.986l-21.244-21.182
|
||||
c21.735-21.799,50.657-33.805,81.438-33.805c30.781,0,59.703,12.005,81.438,33.805L176.982,129.274z M116.788,54.288
|
||||
c36.109,0,70.045,14.076,95.554,39.636l21.234-21.192c-31.178-31.239-72.654-48.444-116.788-48.444
|
||||
C72.653,24.288,31.178,41.493,0,72.732l21.234,21.192C46.743,68.364,80.678,54.288,116.788,54.288z M162.885,143.465
|
||||
c-12.293-12.367-28.664-19.177-46.097-19.177c-17.432,0-33.803,6.811-46.097,19.177l21.275,21.151
|
||||
c6.621-6.66,15.437-10.328,24.821-10.328c9.386,0,18.2,3.667,24.819,10.327L162.885,143.465z M116.788,169.288
|
||||
c-11.046,0-20,8.954-20,20s8.954,20,20,20c11.044,0,20-8.954,20-20S127.832,169.288,116.788,169.288z"/>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -44,6 +44,7 @@
|
||||
#include <QCloseEvent>
|
||||
#include <QSettings>
|
||||
#include <QTextCodec>
|
||||
#include <QFont>
|
||||
#include <QProgressDialog>
|
||||
#include <QSignalBlocker>
|
||||
#include <QDebug>
|
||||
@ -65,6 +66,10 @@
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
|
||||
#include "profiler/easy_socket.h"
|
||||
#undef max
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const int LOADER_TIMER_INTERVAL = 40;
|
||||
@ -74,9 +79,11 @@ const int LOADER_TIMER_INTERVAL = 40;
|
||||
EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsView(nullptr), m_progress(nullptr)
|
||||
{
|
||||
setObjectName("ProfilerGUI_MainWindow");
|
||||
setWindowTitle("EasyProfiler Reader v0.2.0");
|
||||
setWindowTitle("EasyProfiler Reader beta");
|
||||
setDockNestingEnabled(true);
|
||||
resize(800, 600);
|
||||
|
||||
{ QIcon icon(":/logo"); if (!icon.isNull()) setWindowIcon(icon); }
|
||||
|
||||
setStatusBar(new QStatusBar());
|
||||
|
||||
@ -97,6 +104,8 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsVi
|
||||
|
||||
QToolBar *fileToolBar = addToolBar(tr("File"));
|
||||
QAction *connectAct = new QAction(tr("&Connect"), this);
|
||||
{ QIcon icon(":/WiFi"); if (!icon.isNull()) connectAct->setIcon(icon); }
|
||||
|
||||
QAction *newAct = new QAction(tr("&Capture"), this);
|
||||
fileToolBar->addAction(connectAct);
|
||||
fileToolBar->addAction(newAct);
|
||||
@ -171,13 +180,16 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsVi
|
||||
|
||||
auto action = menu->addAction("&Open");
|
||||
connect(action, &QAction::triggered, this, &This::onOpenFileClicked);
|
||||
{ QIcon icon(":/Open"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
action = menu->addAction("&Reload");
|
||||
connect(action, &QAction::triggered, this, &This::onReloadFileClicked);
|
||||
{ QIcon icon(":/Reload"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
menu->addSeparator();
|
||||
action = menu->addAction("&Exit");
|
||||
connect(action, &QAction::triggered, this, &This::onExitClicked);
|
||||
{ QIcon icon(":/Exit"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
menuBar()->addMenu(menu);
|
||||
|
||||
@ -187,9 +199,11 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsVi
|
||||
|
||||
action = menu->addAction("Expand all");
|
||||
connect(action, &QAction::triggered, this, &This::onExpandAllClicked);
|
||||
{ QIcon icon(":/Expand"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
action = menu->addAction("Collapse all");
|
||||
connect(action, &QAction::triggered, this, &This::onCollapseAllClicked);
|
||||
{ QIcon icon(":/Collapse"); if (!icon.isNull()) action->setIcon(icon); }
|
||||
|
||||
menu->addSeparator();
|
||||
action = menu->addAction("Draw items' borders");
|
||||
@ -246,6 +260,28 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsVi
|
||||
|
||||
|
||||
menu = new QMenu("&Settings");
|
||||
action = new QAction("Statistics enabled", nullptr);
|
||||
action->setCheckable(true);
|
||||
action->setChecked(EASY_GLOBALS.enable_statistics);
|
||||
connect(action, &QAction::triggered, this, &This::onEnableDisableStatistics);
|
||||
if (EASY_GLOBALS.enable_statistics)
|
||||
{
|
||||
auto f = action->font();
|
||||
f.setBold(true);
|
||||
action->setFont(f);
|
||||
QIcon icon(":/Stats");
|
||||
if (!icon.isNull())
|
||||
action->setIcon(icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
action->setText("Statistics disabled");
|
||||
QIcon icon(":/Stats-off");
|
||||
if (!icon.isNull())
|
||||
action->setIcon(icon);
|
||||
}
|
||||
menu->addAction(action);
|
||||
|
||||
submenu = menu->addMenu("&Encoding");
|
||||
actionGroup = new QActionGroup(this);
|
||||
actionGroup->setExclusive(true);
|
||||
@ -288,8 +324,6 @@ EasyMainWindow::EasyMainWindow() : Parent(), m_treeWidget(nullptr), m_graphicsVi
|
||||
}
|
||||
}
|
||||
|
||||
#include "../src/easy_socket.h"
|
||||
#undef max
|
||||
void EasyMainWindow::listen()
|
||||
{
|
||||
EasySocket socket;
|
||||
@ -518,6 +552,34 @@ void EasyMainWindow::onChronoTextPosChanged(bool)
|
||||
emit EASY_GLOBALS.events.chronoPositionChanged();
|
||||
}
|
||||
|
||||
void EasyMainWindow::onEnableDisableStatistics(bool _checked)
|
||||
{
|
||||
EASY_GLOBALS.enable_statistics = _checked;
|
||||
|
||||
auto action = qobject_cast<QAction*>(sender());
|
||||
if (action != nullptr)
|
||||
{
|
||||
auto f = action->font();
|
||||
f.setBold(_checked);
|
||||
action->setFont(f);
|
||||
|
||||
if (_checked)
|
||||
{
|
||||
action->setText("Statistics enabled");
|
||||
QIcon icon(":/Stats");
|
||||
if (!icon.isNull())
|
||||
action->setIcon(icon);
|
||||
}
|
||||
else
|
||||
{
|
||||
action->setText("Statistics disabled");
|
||||
QIcon icon(":/Stats-off");
|
||||
if (!icon.isNull())
|
||||
action->setIcon(icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EasyMainWindow::onDrawBordersChanged(bool _checked)
|
||||
{
|
||||
EASY_GLOBALS.draw_graphics_items_borders = _checked;
|
||||
@ -876,6 +938,12 @@ void EasyMainWindow::loadSettings()
|
||||
EASY_GLOBALS.bind_scene_and_tree_expand_status = flag.toBool();
|
||||
}
|
||||
|
||||
flag = settings.value("enable_statistics");
|
||||
if (!flag.isNull())
|
||||
{
|
||||
EASY_GLOBALS.enable_statistics = flag.toBool();
|
||||
}
|
||||
|
||||
QString encoding = settings.value("encoding", "UTF-8").toString();
|
||||
auto default_codec_mib = QTextCodec::codecForName(encoding.toStdString().c_str())->mibEnum();
|
||||
auto default_codec = QTextCodec::codecForMib(default_codec_mib);
|
||||
@ -906,6 +974,7 @@ void EasyMainWindow::saveSettingsAndGeometry()
|
||||
settings.setValue("collapse_items_on_tree_close", EASY_GLOBALS.collapse_items_on_tree_close);
|
||||
settings.setValue("all_items_expanded_by_default", EASY_GLOBALS.all_items_expanded_by_default);
|
||||
settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status);
|
||||
settings.setValue("enable_statistics", EASY_GLOBALS.enable_statistics);
|
||||
settings.setValue("encoding", QTextCodec::codecForLocale()->name());
|
||||
|
||||
settings.endGroup();
|
||||
@ -1021,11 +1090,11 @@ void EasyFileReader::load(const QString& _filename)
|
||||
interrupt();
|
||||
|
||||
m_filename = _filename;
|
||||
m_thread = ::std::move(::std::thread([this]() {
|
||||
m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors, m_descriptors, m_blocks, m_blocksTree, true));
|
||||
m_thread = ::std::move(::std::thread([this](bool _enableStatistics) {
|
||||
m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors, m_descriptors, m_blocks, m_blocksTree, _enableStatistics));
|
||||
m_progress.store(100);
|
||||
m_bDone.store(true);
|
||||
}));
|
||||
}, EASY_GLOBALS.enable_statistics));
|
||||
}
|
||||
|
||||
void EasyFileReader::interrupt()
|
||||
|
@ -145,6 +145,7 @@ protected slots:
|
||||
void onExitClicked(bool);
|
||||
void onEncodingChanged(bool);
|
||||
void onChronoTextPosChanged(bool);
|
||||
void onEnableDisableStatistics(bool);
|
||||
void onDrawBordersChanged(bool);
|
||||
void onCollapseItemsAfterCloseChanged(bool);
|
||||
void onAllItemsExpandedByDefaultChange(bool);
|
||||
|
17
profiler_gui/resources.qrc
Normal file
@ -0,0 +1,17 @@
|
||||
<!DOCTYPE RCC><RCC version="1.0">
|
||||
<qresource>
|
||||
<file alias="logo">icons/logo.svg</file>
|
||||
<file alias="Exit">icons/off.svg</file>
|
||||
<file alias="Open">icons/open-folder.svg</file>
|
||||
<file alias="Reload">icons/reload.svg</file>
|
||||
<file alias="Expand">icons/expand.svg</file>
|
||||
<file alias="Collapse">icons/collapse.svg</file>
|
||||
<file alias="Color">icons/colors.svg</file>
|
||||
<file alias="NoColor">icons/colors-black.svg</file>
|
||||
<file alias="Save">icons/save.svg</file>
|
||||
<file alias="Stats">icons/statistics.svg</file>
|
||||
<file alias="Stats-off">icons/statistics2.svg</file>
|
||||
<file alias="LAN">icons/lan.svg</file>
|
||||
<file alias="WiFi">icons/wifi.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -34,6 +34,10 @@
|
||||
#include "tree_widget_item.h"
|
||||
#include "globals.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
@ -111,6 +115,11 @@ void EasyTreeWidgetLoader::interrupt()
|
||||
for (auto item : _items)
|
||||
delete item.second;
|
||||
}, ::std::move(m_topLevelItems));
|
||||
|
||||
#ifdef _WIN32
|
||||
SetThreadPriority(deleter_thread.native_handle(), THREAD_PRIORITY_LOWEST);
|
||||
#endif
|
||||
|
||||
deleter_thread.detach();
|
||||
|
||||
m_items.clear();
|
||||
@ -161,20 +170,14 @@ void FillTreeClass<T>::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI
|
||||
const auto& root = threadTree.second;
|
||||
auto item = new EasyTreeWidgetItem();
|
||||
|
||||
if (root.thread_name && root.thread_name[0] != 0)
|
||||
{
|
||||
item->setText(COL_NAME, QString("%1 Thread %2").arg(root.thread_name).arg(root.thread_id));
|
||||
}
|
||||
if (root.got_name())
|
||||
item->setText(COL_NAME, QString("%1 Thread %2").arg(root.name()).arg(root.thread_id));
|
||||
else
|
||||
{
|
||||
item->setText(COL_NAME, QString("Thread %1").arg(root.thread_id));
|
||||
}
|
||||
|
||||
::profiler::timestamp_t duration = 0;
|
||||
if (!root.children.empty())
|
||||
{
|
||||
duration = blocksTree(root.children.back()).node->end() - blocksTree(root.children.front()).node->begin();
|
||||
}
|
||||
|
||||
item->setTimeSmart(COL_DURATION, duration);
|
||||
item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
|
||||
@ -182,13 +185,9 @@ void FillTreeClass<T>::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI
|
||||
|
||||
//_items.push_back(item);
|
||||
|
||||
// TODO: Optimize children duration calculation (it must be calculated before setTreeInternal now)
|
||||
::profiler::timestamp_t children_duration = 0;
|
||||
for (auto i : root.children)
|
||||
children_duration += blocksTree(i).node->duration();
|
||||
item->setTimeSmart(COL_SELF_DURATION, children_duration);
|
||||
item->setTimeSmart(COL_SELF_DURATION, root.active_time);
|
||||
|
||||
children_duration = 0;
|
||||
::profiler::timestamp_t children_duration = 0;
|
||||
const auto children_items_number = FillTreeClass<T>::setTreeInternal(_safelocker, _items, _beginTime, root.children, item, nullptr, item, _beginTime, finishtime + 1000000000ULL, false, children_duration, _colorizeRows);
|
||||
|
||||
if (children_items_number > 0)
|
||||
@ -259,29 +258,20 @@ void FillTreeClass<T>::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI
|
||||
{
|
||||
thread_item = new EasyTreeWidgetItem();
|
||||
|
||||
if (block.root->thread_name && block.root->thread_name[0] != 0)
|
||||
{
|
||||
thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->thread_name).arg(block.root->thread_id));
|
||||
}
|
||||
if (block.root->got_name())
|
||||
thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->name()).arg(block.root->thread_id));
|
||||
else
|
||||
{
|
||||
thread_item->setText(COL_NAME, QString("Thread %1").arg(block.root->thread_id));
|
||||
}
|
||||
|
||||
if (!block.root->children.empty())
|
||||
{
|
||||
duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin();
|
||||
}
|
||||
|
||||
thread_item->setTimeSmart(COL_DURATION, duration);
|
||||
thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
|
||||
thread_item->setTextColor(::profiler_gui::SELECTED_THREAD_FOREGROUND);
|
||||
|
||||
// Calculate clean duration (sum of all children durations)
|
||||
::profiler::timestamp_t children_duration = 0;
|
||||
for (auto i : block.root->children)
|
||||
children_duration += blocksTree(i).node->duration();
|
||||
thread_item->setTimeSmart(COL_SELF_DURATION, children_duration);
|
||||
// Sum of all children durations:
|
||||
thread_item->setTimeSmart(COL_SELF_DURATION, block.root->active_time);
|
||||
|
||||
threadsMap.insert(::std::make_pair(block.root->thread_id, thread_item));
|
||||
}
|
||||
@ -319,7 +309,7 @@ void FillTreeClass<T>::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI
|
||||
item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
|
||||
item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
|
||||
|
||||
auto percentage_per_thread = static_cast<int>(0.5 + 100. * static_cast<double>(per_thread_stats->total_duration) / static_cast<double>(thread_item->selfDuration()));
|
||||
auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, block.root->active_time);
|
||||
item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
|
||||
item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
|
||||
|
||||
@ -467,8 +457,8 @@ size_t FillTreeClass<T>::setTreeInternal(T& _safelocker, Items& _items, const ::
|
||||
const auto& per_parent_stats = child.per_parent_stats;
|
||||
const auto& per_frame_stats = child.per_frame_stats;
|
||||
|
||||
auto percentage = duration == 0 ? 0 : static_cast<int>(0.5 + 100. * static_cast<double>(duration) / static_cast<double>(_parent->duration()));
|
||||
auto percentage_sum = static_cast<int>(0.5 + 100. * static_cast<double>(per_parent_stats->total_duration) / static_cast<double>(_parent->duration()));
|
||||
auto percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, _parent->duration());
|
||||
auto percentage_sum = ::profiler_gui::percent(per_parent_stats->total_duration, _parent->duration());
|
||||
item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage);
|
||||
item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage));
|
||||
item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, percentage_sum);
|
||||
@ -478,8 +468,8 @@ size_t FillTreeClass<T>::setTreeInternal(T& _safelocker, Items& _items, const ::
|
||||
{
|
||||
if (_parent != _frame)
|
||||
{
|
||||
percentage = duration == 0 ? 0 : static_cast<int>(0.5 + 100. * static_cast<double>(duration) / static_cast<double>(_frame->duration()));
|
||||
percentage_sum = static_cast<int>(0.5 + 100. * static_cast<double>(per_frame_stats->total_duration) / static_cast<double>(_frame->duration()));
|
||||
percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, _frame->duration());
|
||||
percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, _frame->duration());
|
||||
}
|
||||
|
||||
item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage);
|
||||
@ -509,7 +499,7 @@ size_t FillTreeClass<T>::setTreeInternal(T& _safelocker, Items& _items, const ::
|
||||
|
||||
if (_thread)
|
||||
{
|
||||
auto percentage_per_thread = static_cast<int>(0.5 + 100. * static_cast<double>(per_thread_stats->total_duration) / static_cast<double>(_thread->selfDuration()));
|
||||
auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _thread->selfDuration());
|
||||
item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
|
||||
item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
|
||||
}
|
||||
@ -570,7 +560,7 @@ size_t FillTreeClass<T>::setTreeInternal(T& _safelocker, Items& _items, const ::
|
||||
auto self_duration = duration - children_duration;
|
||||
if (children_duration > 0 && duration > 0)
|
||||
{
|
||||
percentage = static_cast<int>(0.5 + 100. * static_cast<double>(self_duration) / static_cast<double>(duration));
|
||||
percentage = ::profiler_gui::percent(self_duration, duration);
|
||||
}
|
||||
|
||||
item->setTimeSmart(COL_SELF_DURATION, self_duration);
|
||||
|
104
sample/main.cpp
@ -14,7 +14,7 @@ std::mutex cv_m;
|
||||
int g_i = 0;
|
||||
|
||||
int OBJECTS = 500;
|
||||
int RENDER_SPEPS = 1600;
|
||||
int RENDER_STEPS = 1600;
|
||||
int MODELLING_STEPS = 1000;
|
||||
int RESOURCE_LOADING_COUNT = 50;
|
||||
|
||||
@ -32,7 +32,7 @@ void loadingResources(){
|
||||
}
|
||||
|
||||
void prepareMath(){
|
||||
EASY_FUNCTION(profiler::colors::Blue);
|
||||
EASY_FUNCTION(profiler::colors::Green);
|
||||
int* intarray = new int[OBJECTS];
|
||||
for (int i = 0; i < OBJECTS; ++i)
|
||||
intarray[i] = i * i;
|
||||
@ -41,7 +41,7 @@ void prepareMath(){
|
||||
}
|
||||
|
||||
void calcIntersect(){
|
||||
EASY_FUNCTION(profiler::colors::Blue);
|
||||
EASY_FUNCTION(profiler::colors::Gold);
|
||||
//int* intarray = new int[OBJECTS * OBJECTS];
|
||||
int* intarray = new int[OBJECTS];
|
||||
for (int i = 0; i < OBJECTS; ++i)
|
||||
@ -56,12 +56,12 @@ void calcIntersect(){
|
||||
|
||||
double multModel(double i)
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Blue);
|
||||
EASY_FUNCTION(profiler::colors::PaleGold);
|
||||
return i * sin(i) * cos(i);
|
||||
}
|
||||
|
||||
void calcPhys(){
|
||||
EASY_FUNCTION(profiler::colors::Blue);
|
||||
EASY_FUNCTION(profiler::colors::Amber);
|
||||
double* intarray = new double[OBJECTS];
|
||||
for (int i = 0; i < OBJECTS; ++i)
|
||||
intarray[i] = multModel(double(i)) + double(i / 3) - double((OBJECTS - i) / 2);
|
||||
@ -71,12 +71,12 @@ void calcPhys(){
|
||||
|
||||
double calcSubbrain(int i)
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Blue);
|
||||
EASY_FUNCTION(profiler::colors::Navy);
|
||||
return i * i * i - i / 10 + (OBJECTS - i) * 7 ;
|
||||
}
|
||||
|
||||
void calcBrain(){
|
||||
EASY_FUNCTION(profiler::colors::Blue);
|
||||
EASY_FUNCTION(profiler::colors::LightBlue);
|
||||
double* intarray = new double[OBJECTS];
|
||||
for (int i = 0; i < OBJECTS; ++i)
|
||||
intarray[i] = calcSubbrain(i) + double(i * 180 / 3);
|
||||
@ -85,19 +85,19 @@ void calcBrain(){
|
||||
}
|
||||
|
||||
void calculateBehavior(){
|
||||
EASY_FUNCTION(profiler::colors::DarkBlue);
|
||||
EASY_FUNCTION(profiler::colors::Blue);
|
||||
calcPhys();
|
||||
calcBrain();
|
||||
}
|
||||
|
||||
void modellingStep(){
|
||||
EASY_FUNCTION(profiler::colors::Navy);
|
||||
EASY_FUNCTION();
|
||||
prepareMath();
|
||||
calculateBehavior();
|
||||
}
|
||||
|
||||
void prepareRender(){
|
||||
EASY_FUNCTION(profiler::colors::DarkRed);
|
||||
EASY_FUNCTION(profiler::colors::Brick);
|
||||
localSleep();
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(8));
|
||||
|
||||
@ -105,7 +105,7 @@ void prepareRender(){
|
||||
|
||||
int multPhys(int i)
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red);
|
||||
EASY_FUNCTION(profiler::colors::Red700, profiler::DISABLED);
|
||||
return i * i * i * i / 100;
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ void loadingResourcesThread(){
|
||||
//std::unique_lock<std::mutex> lk(cv_m);
|
||||
//cv.wait(lk, []{return g_i == 1; });
|
||||
EASY_THREAD("Resource loading");
|
||||
for (int i = 0; /*i < RESOURCE_LOADING_COUNT */ ; i++){
|
||||
for(int i = 0; i < RESOURCE_LOADING_COUNT; i++){
|
||||
loadingResources();
|
||||
EASY_EVENT("Resources Loading!", profiler::colors::Cyan);
|
||||
localSleep(1200000);
|
||||
@ -146,7 +146,7 @@ void modellingThread(){
|
||||
//std::unique_lock<std::mutex> lk(cv_m);
|
||||
//cv.wait(lk, []{return g_i == 1; });
|
||||
EASY_THREAD("Modelling");
|
||||
for (int i = 0; /*i < RENDER_SPEPS */ ; i++){
|
||||
for (int i = 0; i < RENDER_STEPS; i++){
|
||||
modellingStep();
|
||||
localSleep(1200000);
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
@ -157,67 +157,14 @@ void renderThread(){
|
||||
//std::unique_lock<std::mutex> lk(cv_m);
|
||||
//cv.wait(lk, []{return g_i == 1; });
|
||||
EASY_THREAD("Render");
|
||||
for (int i = 0; /*i < MODELLING_STEPS*/; i++){
|
||||
for (int i = 0; i < MODELLING_STEPS; i++){
|
||||
frame();
|
||||
localSleep(1200000);
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
}
|
||||
|
||||
void four()
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(37));
|
||||
}
|
||||
|
||||
void five()
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
}
|
||||
void six()
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(42));
|
||||
}
|
||||
|
||||
void three()
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red);
|
||||
four();
|
||||
five();
|
||||
six();
|
||||
}
|
||||
|
||||
void seven()
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(147));
|
||||
}
|
||||
|
||||
void two()
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(26));
|
||||
}
|
||||
|
||||
void one()
|
||||
{
|
||||
EASY_FUNCTION(profiler::colors::Red);
|
||||
two();
|
||||
three();
|
||||
seven();
|
||||
}
|
||||
|
||||
/*
|
||||
one
|
||||
two
|
||||
three
|
||||
four
|
||||
five
|
||||
six
|
||||
seven
|
||||
*/
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
@ -225,7 +172,7 @@ int main(int argc, char* argv[])
|
||||
OBJECTS = std::atoi(argv[1]);
|
||||
}
|
||||
if (argc > 2 && argv[2]){
|
||||
RENDER_SPEPS = std::atoi(argv[2]);
|
||||
RENDER_STEPS = std::atoi(argv[2]);
|
||||
}
|
||||
if (argc > 3 && argv[3]){
|
||||
MODELLING_STEPS = std::atoi(argv[3]);
|
||||
@ -235,12 +182,12 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
std::cout << "Objects count: " << OBJECTS << std::endl;
|
||||
std::cout << "Render steps: " << RENDER_SPEPS << std::endl;
|
||||
std::cout << "Render steps: " << RENDER_STEPS << std::endl;
|
||||
std::cout << "Modelling steps: " << MODELLING_STEPS << std::endl;
|
||||
std::cout << "Resource loading count: " << RESOURCE_LOADING_COUNT << std::endl;
|
||||
|
||||
auto start = std::chrono::system_clock::now();
|
||||
//EASY_PROFILER_ENABLE;
|
||||
EASY_PROFILER_ENABLE;
|
||||
EASY_MAIN_THREAD;
|
||||
profiler::startListenSignalToCapture();
|
||||
//one();
|
||||
@ -252,27 +199,26 @@ int main(int argc, char* argv[])
|
||||
std::thread modelling = std::thread(modellingThread);
|
||||
|
||||
|
||||
for(int i=0; i < 0; i++){
|
||||
for(int i=0; i < 3; i++){
|
||||
threads.emplace_back(std::thread(loadingResourcesThread));
|
||||
threads.emplace_back(std::thread(renderThread));
|
||||
threads.emplace_back(std::thread(modellingThread));
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(cv_m);
|
||||
g_i = 1;
|
||||
}
|
||||
|
||||
cv_m.lock();
|
||||
g_i = 1;
|
||||
cv_m.unlock();
|
||||
cv.notify_all();
|
||||
|
||||
for (int i = 0; i < RENDER_SPEPS; ++i) {
|
||||
for (int i = 0; i < RENDER_STEPS; ++i) {
|
||||
modellingStep();
|
||||
localSleep(1200000);
|
||||
}
|
||||
|
||||
render.join();
|
||||
modelling.join();
|
||||
for(auto& t : threads){
|
||||
for(auto& t : threads)
|
||||
t.join();
|
||||
}
|
||||
/**/
|
||||
|
||||
auto end = std::chrono::system_clock::now();
|
||||
|
@ -17,7 +17,7 @@ echo "Blocks count, dT prof enabled usec, dT prof disabled usec" > $RESULT_FILE
|
||||
|
||||
for i in {1..9}
|
||||
do
|
||||
OBJECTS_COUNT=$(($i*10))
|
||||
OBJECTS_COUNT=$(($i*100))
|
||||
for j in {10..15}
|
||||
do
|
||||
RENDER_COUNT=$(($j*100))
|
||||
|
@ -11,12 +11,11 @@ set(CPP_FILES
|
||||
set(H_FILES
|
||||
${ROOT}/include/profiler/profiler.h
|
||||
${ROOT}/include/profiler/reader.h
|
||||
${ROOT}/include/profiler/event_trace_status.h
|
||||
${ROOT}/include/profiler/easy_net.h
|
||||
${ROOT}/include/profiler/easy_socket.h
|
||||
profile_manager.h
|
||||
spin_lock.h
|
||||
event_trace_win.h
|
||||
easy_socket.h
|
||||
)
|
||||
|
||||
set(SOURCES
|
||||
|
@ -63,23 +63,33 @@ BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id)
|
||||
Block::Block(Block&& that)
|
||||
: BaseBlockData(that.m_begin, that.m_id)
|
||||
, m_name(that.m_name)
|
||||
, m_enabled(that.m_enabled)
|
||||
{
|
||||
m_end = that.m_end;
|
||||
}
|
||||
|
||||
Block::Block(block_type_t _block_type, block_id_t _descriptor_id, const char* _name)
|
||||
: Block(getCurrentTime(), _block_type, _descriptor_id, _name)
|
||||
Block::Block(const BaseBlockDescriptor& _descriptor, const char* _runtimeName)
|
||||
: BaseBlockData(_descriptor.enabled() ? getCurrentTime() : 1ULL, _descriptor.id())
|
||||
, m_name(_runtimeName)
|
||||
, m_enabled(_descriptor.enabled())
|
||||
{
|
||||
}
|
||||
|
||||
Block::Block(timestamp_t _begin_time, block_type_t _block_type, block_id_t _descriptor_id, const char* _name)
|
||||
Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, const char* _runtimeName)
|
||||
: BaseBlockData(_begin_time, _descriptor_id)
|
||||
, m_name(_name)
|
||||
, m_name(_runtimeName)
|
||||
, m_enabled(true)
|
||||
{
|
||||
if (static_cast<uint8_t>(_block_type) < BLOCK_TYPE_BLOCK)
|
||||
{
|
||||
m_end = m_begin;
|
||||
}
|
||||
}
|
||||
|
||||
void Block::start()
|
||||
{
|
||||
m_begin = getCurrentTime();
|
||||
}
|
||||
|
||||
void Block::start(timestamp_t _time)
|
||||
{
|
||||
m_begin = _time;
|
||||
}
|
||||
|
||||
void Block::finish()
|
||||
@ -87,13 +97,13 @@ 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()
|
||||
{
|
||||
if (!isFinished())
|
||||
if (!finished())
|
||||
::profiler::endBlock();
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ 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 "easy_socket.h"
|
||||
#include "profiler/easy_socket.h"
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -1,181 +1,281 @@
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <memory.h>
|
||||
#include <chrono>
|
||||
|
||||
#include "profiler/profiler.h"
|
||||
#include "profile_manager.h"
|
||||
|
||||
#include "event_trace_win.h"
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//extern ProfileManager& MANAGER;
|
||||
#define MANAGER ProfileManager::instance()
|
||||
|
||||
namespace profiler {
|
||||
|
||||
// CSwitch class
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx
|
||||
// EventType = 36
|
||||
struct CSwitch final
|
||||
{
|
||||
uint32_t NewThreadId;
|
||||
uint32_t OldThreadId;
|
||||
int8_t NewThreadPriority;
|
||||
int8_t OldThreadPriority;
|
||||
uint8_t PreviousCState;
|
||||
int8_t SpareByte;
|
||||
int8_t OldThreadWaitReason;
|
||||
int8_t OldThreadWaitMode;
|
||||
int8_t OldThreadState;
|
||||
int8_t OldThreadWaitIdealProcessor;
|
||||
uint32_t NewThreadWaitTime;
|
||||
uint32_t Reserved;
|
||||
};
|
||||
|
||||
void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent)
|
||||
{
|
||||
static const decltype(_traceEvent->EventHeader.EventDescriptor.Opcode) SWITCH_CONTEXT_OPCODE = 36;
|
||||
if (_traceEvent->EventHeader.EventDescriptor.Opcode != SWITCH_CONTEXT_OPCODE)
|
||||
return;
|
||||
|
||||
if (sizeof(CSwitch) != _traceEvent->UserDataLength)
|
||||
return;
|
||||
|
||||
//EASY_FUNCTION(::profiler::colors::Red);
|
||||
|
||||
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);
|
||||
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc);
|
||||
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EasyEventTracer& EasyEventTracer::instance()
|
||||
{
|
||||
static EasyEventTracer tracer;
|
||||
return tracer;
|
||||
}
|
||||
|
||||
EasyEventTracer::EasyEventTracer()
|
||||
{
|
||||
}
|
||||
|
||||
EasyEventTracer::~EasyEventTracer()
|
||||
{
|
||||
disable();
|
||||
}
|
||||
|
||||
::profiler::EventTracingEnableStatus EasyEventTracer::startTrace(bool _force, int _step)
|
||||
{
|
||||
auto startTraceResult = StartTrace(&m_sessionHandle, KERNEL_LOGGER_NAME, props());
|
||||
switch (startTraceResult)
|
||||
{
|
||||
case ERROR_SUCCESS:
|
||||
printf("EVENT_TRACING_LAUNCHED_SUCCESSFULLY\n");
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
{
|
||||
if (_force)
|
||||
{
|
||||
// Try to stop another event tracing session to force launch self session.
|
||||
|
||||
if ((_step == 0 && 32 < (int)ShellExecute(NULL, NULL, "logman", "stop \"" KERNEL_LOGGER_NAME "\" -ets", NULL, SW_HIDE)) || (_step > 0 && _step < 6))
|
||||
{
|
||||
// Command executed successfully. Wait for a few time until tracing session finish.
|
||||
::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
|
||||
return startTrace(true, ++_step);
|
||||
}
|
||||
}
|
||||
printf("EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE\n");
|
||||
return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE;
|
||||
}
|
||||
|
||||
case ERROR_ACCESS_DENIED:
|
||||
printf("EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS\n");
|
||||
return EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS;
|
||||
|
||||
case ERROR_BAD_LENGTH:
|
||||
printf("EVENT_TRACING_BAD_PROPERTIES_SIZE\n");
|
||||
return EVENT_TRACING_BAD_PROPERTIES_SIZE;
|
||||
}
|
||||
|
||||
return EVENT_TRACING_MISTERIOUS_ERROR;
|
||||
}
|
||||
|
||||
::profiler::EventTracingEnableStatus EasyEventTracer::enable(bool _force)
|
||||
{
|
||||
static const decltype(m_properties.base.Wnode.ClientContext) RAW_TIMESTAMP_TIME_TYPE = 1;
|
||||
|
||||
if (m_bEnabled)
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
|
||||
// Clear properties
|
||||
memset(&m_properties, 0, sizeof(m_properties));
|
||||
m_properties.base.Wnode.BufferSize = sizeof(m_properties);
|
||||
m_properties.base.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
m_properties.base.Wnode.ClientContext = RAW_TIMESTAMP_TIME_TYPE;
|
||||
m_properties.base.Wnode.Guid = SystemTraceControlGuid;
|
||||
m_properties.base.LoggerNameOffset = sizeof(m_properties.base);
|
||||
m_properties.base.EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
|
||||
m_properties.base.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
||||
|
||||
auto res = startTrace(_force);
|
||||
if (res != EVENT_TRACING_LAUNCHED_SUCCESSFULLY)
|
||||
return res;
|
||||
|
||||
memset(&m_trace, 0, sizeof(m_trace));
|
||||
m_trace.LoggerName = KERNEL_LOGGER_NAME;
|
||||
m_trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
|
||||
m_trace.EventRecordCallback = ::profiler::processTraceEvent;
|
||||
|
||||
m_openedHandle = OpenTrace(&m_trace);
|
||||
if (m_openedHandle == INVALID_PROCESSTRACE_HANDLE)
|
||||
return EVENT_TRACING_OPEN_TRACE_ERROR;
|
||||
|
||||
// Have to launch a thread to process events because according to MSDN documentation:
|
||||
//
|
||||
// The ProcessTrace function blocks the thread until it delivers all events, the BufferCallback function returns FALSE,
|
||||
// or you call CloseTrace. If the consumer is consuming events in real time, the ProcessTrace function returns after
|
||||
// the controller stops the trace session. (Note that there may be a several-second delay before the function returns.)
|
||||
//
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx
|
||||
m_stubThread = ::std::move(::std::thread([this]()
|
||||
{
|
||||
EASY_THREAD("EasyProfiler.EventTracing");
|
||||
//EASY_BLOCK("ProcessTrace()", ::profiler::colors::Red);
|
||||
ProcessTrace(&m_openedHandle, 1, 0, 0);
|
||||
}));
|
||||
|
||||
m_bEnabled = true;
|
||||
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
}
|
||||
|
||||
void EasyEventTracer::disable()
|
||||
{
|
||||
if (!m_bEnabled)
|
||||
return;
|
||||
|
||||
ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP);
|
||||
CloseTrace(m_openedHandle);
|
||||
|
||||
// Wait for ProcessThread to finish
|
||||
if (m_stubThread.joinable())
|
||||
m_stubThread.join();
|
||||
|
||||
m_bEnabled = false;
|
||||
}
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <memory.h>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
#include "profiler/profiler.h"
|
||||
#include "profile_manager.h"
|
||||
|
||||
|
||||
#include "event_trace_win.h"
|
||||
#include "Psapi.h"
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//extern ProfileManager& MANAGER;
|
||||
#define MANAGER ProfileManager::instance()
|
||||
|
||||
namespace profiler {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct ProcessInfo final {
|
||||
std::string name;
|
||||
uint32_t id = 0;
|
||||
int8_t valid = 0;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// CSwitch class
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx
|
||||
// EventType = 36
|
||||
struct CSwitch final
|
||||
{
|
||||
uint32_t NewThreadId;
|
||||
uint32_t OldThreadId;
|
||||
int8_t NewThreadPriority;
|
||||
int8_t OldThreadPriority;
|
||||
uint8_t PreviousCState;
|
||||
int8_t SpareByte;
|
||||
int8_t OldThreadWaitReason;
|
||||
int8_t OldThreadWaitMode;
|
||||
int8_t OldThreadState;
|
||||
int8_t OldThreadWaitIdealProcessor;
|
||||
uint32_t NewThreadWaitTime;
|
||||
uint32_t Reserved;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef ::std::unordered_map<decltype(CSwitch::NewThreadId), ProcessInfo*, ::profiler::do_not_calc_hash> thread_process_info_map;
|
||||
typedef ::std::unordered_map<uint32_t, ProcessInfo, ::profiler::do_not_calc_hash> process_info_map;
|
||||
|
||||
// Using static is safe because processTraceEvent() is called from one thread
|
||||
static process_info_map PROCESS_INFO_TABLE;
|
||||
static thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent)
|
||||
{
|
||||
static const decltype(_traceEvent->EventHeader.EventDescriptor.Opcode) SWITCH_CONTEXT_OPCODE = 36;
|
||||
if (_traceEvent->EventHeader.EventDescriptor.Opcode != SWITCH_CONTEXT_OPCODE)
|
||||
return;
|
||||
|
||||
if (sizeof(CSwitch) != _traceEvent->UserDataLength)
|
||||
return;
|
||||
|
||||
EASY_FUNCTION(::profiler::colors::White, ::profiler::DISABLED);
|
||||
|
||||
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
|
||||
const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
|
||||
|
||||
ProcessInfo* pinfo = nullptr;
|
||||
auto it = THREAD_PROCESS_INFO_TABLE.find(_contextSwitchEvent->NewThreadId);
|
||||
if (it == THREAD_PROCESS_INFO_TABLE.end())
|
||||
{
|
||||
auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _contextSwitchEvent->NewThreadId);
|
||||
if (hThread != nullptr)
|
||||
{
|
||||
auto pid = GetProcessIdOfThread(hThread);
|
||||
pinfo = &PROCESS_INFO_TABLE[pid];
|
||||
|
||||
if (pinfo->valid == 0)
|
||||
{
|
||||
// According to documentation, using GetModuleBaseName() requires
|
||||
// PROCESS_QUERY_INFORMATION | PROCESS_VM_READ access rights.
|
||||
// But it works fine with PROCESS_QUERY_LIMITED_INFORMATION instead of PROCESS_QUERY_INFORMATION.
|
||||
//
|
||||
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683196(v=vs.85).aspx
|
||||
auto hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid);
|
||||
if (hProc != nullptr)
|
||||
{
|
||||
static TCHAR buf[MAX_PATH] = {}; // Using static is safe because processTraceEvent() is called from one thread
|
||||
auto success = GetModuleBaseName(hProc, 0, buf, MAX_PATH);
|
||||
|
||||
if (pinfo->name.empty())
|
||||
{
|
||||
static char numbuf[128] = {};
|
||||
sprintf(numbuf, "%u ", pid);
|
||||
pinfo->name = numbuf;
|
||||
pinfo->id = pid;
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
pinfo->name += buf;
|
||||
pinfo->valid = 1;
|
||||
//printf("PROCESS %u is %s\n", pid, buf);
|
||||
}
|
||||
|
||||
CloseHandle(hProc);
|
||||
}
|
||||
else
|
||||
{
|
||||
pinfo->valid = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (pinfo->valid > 0)
|
||||
{
|
||||
THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = pinfo;
|
||||
}
|
||||
else if (pinfo->valid < 0)
|
||||
{
|
||||
pinfo = nullptr;
|
||||
THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr;
|
||||
}
|
||||
|
||||
CloseHandle(hThread);
|
||||
}
|
||||
else
|
||||
{
|
||||
THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pinfo = it->second;
|
||||
if (pinfo != nullptr && pinfo->valid < 0)
|
||||
pinfo = nullptr;
|
||||
}
|
||||
|
||||
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, _contextSwitchEvent->NewThreadId, pinfo ? pinfo->name.c_str() : "");
|
||||
MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EasyEventTracer& EasyEventTracer::instance()
|
||||
{
|
||||
static EasyEventTracer tracer;
|
||||
return tracer;
|
||||
}
|
||||
|
||||
EasyEventTracer::EasyEventTracer()
|
||||
{
|
||||
}
|
||||
|
||||
EasyEventTracer::~EasyEventTracer()
|
||||
{
|
||||
disable();
|
||||
}
|
||||
|
||||
::profiler::EventTracingEnableStatus EasyEventTracer::startTrace(bool _force, int _step)
|
||||
{
|
||||
auto startTraceResult = StartTrace(&m_sessionHandle, KERNEL_LOGGER_NAME, props());
|
||||
switch (startTraceResult)
|
||||
{
|
||||
case ERROR_SUCCESS:
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
|
||||
case ERROR_ALREADY_EXISTS:
|
||||
{
|
||||
if (_force)
|
||||
{
|
||||
// Try to stop another event tracing session to force launch self session.
|
||||
|
||||
if ((_step == 0 && 32 < (int)ShellExecute(NULL, NULL, "logman", "stop \"" KERNEL_LOGGER_NAME "\" -ets", NULL, SW_HIDE)) || (_step > 0 && _step < 6))
|
||||
{
|
||||
// Command executed successfully. Wait for a few time until tracing session finish.
|
||||
::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
|
||||
return startTrace(true, ++_step);
|
||||
}
|
||||
}
|
||||
|
||||
return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE;
|
||||
}
|
||||
|
||||
case ERROR_ACCESS_DENIED:
|
||||
return EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS;
|
||||
|
||||
case ERROR_BAD_LENGTH:
|
||||
return EVENT_TRACING_BAD_PROPERTIES_SIZE;
|
||||
}
|
||||
|
||||
return EVENT_TRACING_MISTERIOUS_ERROR;
|
||||
}
|
||||
|
||||
::profiler::EventTracingEnableStatus EasyEventTracer::enable(bool _force)
|
||||
{
|
||||
static const decltype(m_properties.base.Wnode.ClientContext) RAW_TIMESTAMP_TIME_TYPE = 1;
|
||||
|
||||
profiler::guard_lock<profiler::spin_lock> lock(m_spin);
|
||||
if (m_bEnabled)
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
|
||||
// Clear properties
|
||||
memset(&m_properties, 0, sizeof(m_properties));
|
||||
m_properties.base.Wnode.BufferSize = sizeof(m_properties);
|
||||
m_properties.base.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
|
||||
m_properties.base.Wnode.ClientContext = RAW_TIMESTAMP_TIME_TYPE;
|
||||
m_properties.base.Wnode.Guid = SystemTraceControlGuid;
|
||||
m_properties.base.LoggerNameOffset = sizeof(m_properties.base);
|
||||
m_properties.base.EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
|
||||
m_properties.base.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
|
||||
|
||||
auto res = startTrace(_force);
|
||||
if (res != EVENT_TRACING_LAUNCHED_SUCCESSFULLY)
|
||||
return res;
|
||||
|
||||
memset(&m_trace, 0, sizeof(m_trace));
|
||||
m_trace.LoggerName = KERNEL_LOGGER_NAME;
|
||||
m_trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
|
||||
m_trace.EventRecordCallback = ::profiler::processTraceEvent;
|
||||
|
||||
m_openedHandle = OpenTrace(&m_trace);
|
||||
if (m_openedHandle == INVALID_PROCESSTRACE_HANDLE)
|
||||
return EVENT_TRACING_OPEN_TRACE_ERROR;
|
||||
|
||||
// Have to launch a thread to process events because according to MSDN documentation:
|
||||
//
|
||||
// The ProcessTrace function blocks the thread until it delivers all events, the BufferCallback function returns FALSE,
|
||||
// or you call CloseTrace. If the consumer is consuming events in real time, the ProcessTrace function returns after
|
||||
// the controller stops the trace session. (Note that there may be a several-second delay before the function returns.)
|
||||
//
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx
|
||||
m_processThread = ::std::move(::std::thread([this]()
|
||||
{
|
||||
EASY_THREAD("EasyProfiler.ETW");
|
||||
ProcessTrace(&m_openedHandle, 1, 0, 0);
|
||||
}));
|
||||
|
||||
// Set low priority for event tracing thread
|
||||
SetThreadPriority(m_processThread.native_handle(), THREAD_PRIORITY_LOWEST);
|
||||
|
||||
m_bEnabled = true;
|
||||
|
||||
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
|
||||
}
|
||||
|
||||
void EasyEventTracer::disable()
|
||||
{
|
||||
profiler::guard_lock<profiler::spin_lock> lock(m_spin);
|
||||
if (!m_bEnabled)
|
||||
return;
|
||||
|
||||
ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP);
|
||||
CloseTrace(m_openedHandle);
|
||||
|
||||
// Wait for ProcessTrace to finish to make sure no processTraceEvent() will be called later.
|
||||
if (m_processThread.joinable())
|
||||
m_processThread.join();
|
||||
|
||||
m_bEnabled = false;
|
||||
|
||||
// processTraceEvent() is not called anymore. Clean static maps is safe.
|
||||
PROCESS_INFO_TABLE.clear();
|
||||
THREAD_PROCESS_INFO_TABLE.clear();
|
||||
THREAD_PROCESS_INFO_TABLE[0U] = nullptr;
|
||||
}
|
||||
|
||||
} // END of namespace profiler.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif // _WIN32
|
||||
|
@ -13,7 +13,8 @@
|
||||
#include <evntrace.h>
|
||||
#include <evntcons.h>
|
||||
#include <thread>
|
||||
#include "profiler/event_trace_status.h"
|
||||
#include "event_trace_status.h"
|
||||
#include "spin_lock.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -30,12 +31,13 @@ namespace profiler {
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
::std::thread m_stubThread;
|
||||
Properties m_properties;
|
||||
EVENT_TRACE_LOGFILE m_trace;
|
||||
TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE;
|
||||
TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE;
|
||||
bool m_bEnabled = false;
|
||||
::std::thread m_processThread;
|
||||
Properties m_properties;
|
||||
EVENT_TRACE_LOGFILE m_trace;
|
||||
profiler::spin_lock m_spin;
|
||||
TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE;
|
||||
TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE;
|
||||
bool m_bEnabled = false;
|
||||
|
||||
public:
|
||||
|
||||
|
273
src/hashed_cstr.h
Normal file
@ -0,0 +1,273 @@
|
||||
/************************************************************************
|
||||
* 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:
|
||||
|
||||
cstring(const char* _str) : m_str(_str), m_len(strlen(_str))
|
||||
{
|
||||
}
|
||||
|
||||
cstring(const char* _str, size_t _len) : m_str(_str), m_len(_len)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
inline const char* c_str() const
|
||||
{
|
||||
return m_str;
|
||||
}
|
||||
|
||||
inline size_t size() const
|
||||
{
|
||||
return 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:
|
||||
|
||||
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 char* _str, size_t _hash_code) : Parent(_str), m_hash(_hash_code)
|
||||
{
|
||||
}
|
||||
|
||||
hashed_cstr(const char* _str, size_t _len, size_t _hash_code) : Parent(_str, _len), m_hash(_hash_code)
|
||||
{
|
||||
}
|
||||
|
||||
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 char* _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code)
|
||||
{
|
||||
}
|
||||
|
||||
hashed_stdstring(const ::std::string& _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code)
|
||||
{
|
||||
}
|
||||
|
||||
hashed_stdstring(::std::string&& _str, size_t _hash_code) : m_str(::std::forward<::std::string&&>(_str)), m_hash(_hash_code)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
inline const char* c_str() const
|
||||
{
|
||||
return m_str.c_str();
|
||||
}
|
||||
|
||||
inline size_t size() const
|
||||
{
|
||||
return m_str.size();
|
||||
}
|
||||
|
||||
}; // 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
@ -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_
|
@ -27,11 +27,12 @@
|
||||
#include "profiler/serialized_block.h"
|
||||
#include "profiler/easy_net.h"
|
||||
|
||||
#include "easy_socket.h"
|
||||
#include "profiler/easy_socket.h"
|
||||
#include "event_trace_win.h"
|
||||
|
||||
#include <thread>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
@ -44,14 +45,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 block_id_t 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* _autogenUniqueId, 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, _autogenUniqueId, _name, _filename, _line, _block_type, _color);
|
||||
}
|
||||
|
||||
PROFILER_API void endBlock()
|
||||
@ -62,12 +65,11 @@ extern "C" {
|
||||
PROFILER_API void setEnabled(bool isEnable)
|
||||
{
|
||||
MANAGER.setEnabled(isEnable);
|
||||
#ifdef _WIN32
|
||||
if (isEnable)
|
||||
EasyEventTracer::instance().enable(true);
|
||||
else
|
||||
EasyEventTracer::instance().disable();
|
||||
#endif
|
||||
}
|
||||
|
||||
PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName)
|
||||
{
|
||||
MANAGER.storeBlock(_desc, _runtimeName);
|
||||
}
|
||||
|
||||
PROFILER_API void beginBlock(Block& _block)
|
||||
@ -80,11 +82,12 @@ extern "C" {
|
||||
return MANAGER.dumpBlocksToFile(filename);
|
||||
}
|
||||
|
||||
PROFILER_API const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
||||
PROFILER_API const char* registerThread(const char* name)//, const char* filename, const char* _funcname, int line)
|
||||
{
|
||||
return MANAGER.setThreadName(name, filename, _funcname, line);
|
||||
return MANAGER.registerThread(name);// , filename, _funcname, line);
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
PROFILER_API void setContextSwitchLogFilename(const char* name)
|
||||
{
|
||||
return MANAGER.setContextSwitchLogFilename(name);
|
||||
@ -94,6 +97,7 @@ extern "C" {
|
||||
{
|
||||
return MANAGER.getContextSwitchLogFilename();
|
||||
}
|
||||
#endif
|
||||
|
||||
PROFILER_API void startListenSignalToCapture()
|
||||
{
|
||||
@ -117,42 +121,86 @@ SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
BaseBlockDescriptor::BaseBlockDescriptor(int _line, block_type_t _block_type, color_t _color)
|
||||
: m_line(_line)
|
||||
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(_enabled)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
|
||||
: BaseBlockDescriptor(_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)
|
||||
, m_pEnable(nullptr)
|
||||
, m_expired(false)
|
||||
{
|
||||
_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)
|
||||
, m_pEnable(nullptr)
|
||||
, m_expired(false)
|
||||
{
|
||||
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
|
||||
}
|
||||
|
||||
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, EASY_UNIQUE_LINE_ID, "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()
|
||||
@ -165,35 +213,25 @@ 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_isEnabled = ATOMIC_VAR_INIT(false);
|
||||
m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_EVENT_TRACING_ENABLED);
|
||||
m_stopListen = ATOMIC_VAR_INIT(false);
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager()
|
||||
{
|
||||
stopListenSignalToCapture();
|
||||
if(m_listenThread.joinable()){
|
||||
m_listenThread.join();
|
||||
}
|
||||
|
||||
stopListenSignalToCapture();
|
||||
if(m_listenThread.joinable()){
|
||||
m_listenThread.join();
|
||||
}
|
||||
for (auto desc : m_descriptors)
|
||||
{
|
||||
if (desc != nullptr)
|
||||
delete desc;
|
||||
}
|
||||
}
|
||||
|
||||
ProfileManager& ProfileManager::instance()
|
||||
@ -204,6 +242,41 @@ 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_descriptors[_id]->m_expired = true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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())
|
||||
return;
|
||||
|
||||
profiler::Block b(_desc, _runtimeName);
|
||||
b.finish(b.begin());
|
||||
|
||||
if (THREAD_STORAGE == nullptr)
|
||||
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
||||
|
||||
THREAD_STORAGE->storeBlock(b);
|
||||
}
|
||||
|
||||
void ProfileManager::beginBlock(Block& _block)
|
||||
{
|
||||
if (!m_isEnabled)
|
||||
@ -212,27 +285,26 @@ void ProfileManager::beginBlock(Block& _block)
|
||||
if (THREAD_STORAGE == nullptr)
|
||||
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
||||
|
||||
if (!_block.isFinished())
|
||||
THREAD_STORAGE->blocks.openedList.emplace(_block);
|
||||
else
|
||||
THREAD_STORAGE->storeBlock(_block);
|
||||
THREAD_STORAGE->blocks.openedList.emplace(_block);
|
||||
}
|
||||
|
||||
void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id)
|
||||
void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin)
|
||||
{
|
||||
auto ts = findThreadStorage(_thread_id);
|
||||
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
|
||||
if (ts != nullptr)
|
||||
ts->sync.openedList.emplace(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
|
||||
// Dirty hack: _target_thread_id will be written to the field "block_id_t m_id"
|
||||
// and will be available calling method id().
|
||||
ts->sync.openedList.emplace(_time, _target_thread_id, _target_process);
|
||||
}
|
||||
|
||||
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::thread_id_t _target_thread_id, bool _lockSpin)
|
||||
{
|
||||
auto ts = findThreadStorage(_thread_id);
|
||||
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
|
||||
if (ts != nullptr)
|
||||
{
|
||||
profiler::Block lastBlock(_time, profiler::BLOCK_TYPE_CONTEXT_SWITCH, _id, "");
|
||||
lastBlock.finish(_time);
|
||||
ts->storeCSwitch(lastBlock);
|
||||
profiler::Block b(_time, _target_thread_id, "");
|
||||
b.finish(_time);
|
||||
ts->storeCSwitch(b);
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,16 +317,19 @@ void ProfileManager::endBlock()
|
||||
return;
|
||||
|
||||
Block& lastBlock = THREAD_STORAGE->blocks.openedList.top();
|
||||
if (!lastBlock.isFinished())
|
||||
lastBlock.finish();
|
||||
if (lastBlock.enabled())
|
||||
{
|
||||
if (!lastBlock.finished())
|
||||
lastBlock.finish();
|
||||
THREAD_STORAGE->storeBlock(lastBlock);
|
||||
}
|
||||
|
||||
THREAD_STORAGE->storeBlock(lastBlock);
|
||||
THREAD_STORAGE->blocks.openedList.pop();
|
||||
}
|
||||
|
||||
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime)
|
||||
void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin)
|
||||
{
|
||||
auto ts = findThreadStorage(_thread_id);
|
||||
auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
|
||||
if (ts == nullptr || ts->sync.openedList.empty())
|
||||
return;
|
||||
|
||||
@ -265,12 +340,191 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler
|
||||
ts->sync.openedList.pop();
|
||||
}
|
||||
|
||||
void ProfileManager::setEnabled(bool isEnable)
|
||||
{
|
||||
m_isEnabled = isEnable;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (isEnable) {
|
||||
if (m_isEventTracingEnabled)
|
||||
EasyEventTracer::instance().enable(true);
|
||||
} else {
|
||||
EasyEventTracer::instance().disable();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
|
||||
{
|
||||
const bool wasEnabled = m_isEnabled;
|
||||
const bool eventTracingEnabled = m_isEventTracingEnabled;
|
||||
if (wasEnabled)
|
||||
::profiler::setEnabled(false);
|
||||
|
||||
|
||||
// This is to make sure that no new descriptors or new threads will be
|
||||
// added until we finish sending data.
|
||||
guard_lock_t lock1(m_storedSpin);
|
||||
guard_lock_t lock2(m_spin);
|
||||
// This is the only place using both spins, so no dead-lock will occur
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
if (eventTracingEnabled)
|
||||
{
|
||||
// Read thread context switch events from temporary file
|
||||
|
||||
uint64_t timestamp = 0;
|
||||
uint32_t thread_from = 0, thread_to = 0;
|
||||
|
||||
std::ifstream infile(m_csInfoFilename.c_str());
|
||||
if(infile.is_open()) {
|
||||
while (infile >> timestamp >> thread_from >> thread_to) {
|
||||
beginContextSwitch(thread_from, timestamp, thread_to, "", false);
|
||||
endContextSwitch(thread_to, timestamp, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
const auto& t = thread_storage.second;
|
||||
usedMemorySize += t.blocks.usedMemorySize + t.sync.usedMemorySize;
|
||||
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
|
||||
_outputStream.write(CPU_FREQUENCY);
|
||||
#else
|
||||
_outputStream.write(0LL);
|
||||
#endif
|
||||
|
||||
// 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)
|
||||
{
|
||||
if (descriptor == nullptr)
|
||||
continue;
|
||||
|
||||
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);
|
||||
|
||||
_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;
|
||||
|
||||
_outputStream.write(thread_storage.first);
|
||||
|
||||
const auto name_size = static_cast<uint16_t>(t.name.size() + 1);
|
||||
_outputStream.write(name_size);
|
||||
_outputStream.write(name_size > 1 ? t.name.c_str() : "", name_size);
|
||||
|
||||
_outputStream.write(t.sync.closedList.size());
|
||||
t.sync.closedList.serialize(_outputStream);
|
||||
|
||||
_outputStream.write(t.blocks.closedList.size());
|
||||
if (!t.blocks.closedList.empty())
|
||||
t.blocks.closedList.serialize(_outputStream);
|
||||
|
||||
t.clearClosed();
|
||||
t.blocks.openedList.clear();
|
||||
t.sync.openedList.clear();
|
||||
}
|
||||
|
||||
// Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution)
|
||||
for (auto& desc : m_descriptors)
|
||||
{
|
||||
if (desc == nullptr || !desc->m_expired)
|
||||
continue;
|
||||
|
||||
delete desc;
|
||||
desc = nullptr;
|
||||
}
|
||||
|
||||
//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::registerThread(const char* name)
|
||||
{
|
||||
if (THREAD_STORAGE == nullptr)
|
||||
{
|
||||
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
||||
}
|
||||
|
||||
if (!THREAD_STORAGE->named)
|
||||
{
|
||||
THREAD_STORAGE->named = true;
|
||||
THREAD_STORAGE->name = name;
|
||||
}
|
||||
|
||||
return THREAD_STORAGE->name.c_str();
|
||||
}
|
||||
|
||||
void ProfileManager::setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled)
|
||||
{
|
||||
guard_lock_t lock(m_storedSpin);
|
||||
|
||||
auto desc = m_descriptors[_id];
|
||||
if (desc != nullptr)
|
||||
{
|
||||
lock.unlock();
|
||||
|
||||
*desc->m_pEnable = _enabled;
|
||||
desc->m_enabled = _enabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WIN32
|
||||
blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode());
|
||||
m_blocksEnableStatus[key] = _enabled;
|
||||
#else
|
||||
m_blocksEnableStatus[_key] = _enabled;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileManager::startListenSignalToCapture()
|
||||
{
|
||||
if(!m_isAlreadyListened)
|
||||
if (!m_isAlreadyListened)
|
||||
{
|
||||
m_stopListen.store(false);
|
||||
m_listenThread = std::thread(&ProfileManager::startListen, this);
|
||||
m_listenThread = std::thread(&ProfileManager::startListen, this);
|
||||
m_isAlreadyListened = true;
|
||||
|
||||
}
|
||||
@ -282,172 +536,8 @@ void ProfileManager::stopListenSignalToCapture()
|
||||
m_isAlreadyListened = false;
|
||||
}
|
||||
|
||||
void ProfileManager::setEnabled(bool isEnable)
|
||||
{
|
||||
m_isEnabled = isEnable;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define STORE_CSWITCHES_SEPARATELY
|
||||
|
||||
uint32_t ProfileManager::dumpBlocksToStream(StreamWriter& of)
|
||||
{
|
||||
const bool wasEnabled = m_isEnabled;
|
||||
if (wasEnabled)
|
||||
::profiler::setEnabled(false);
|
||||
|
||||
#ifndef _WIN32
|
||||
uint64_t timestamp;
|
||||
uint32_t thread_from, thread_to;
|
||||
|
||||
std::ifstream infile(m_csInfoFilename.c_str());
|
||||
|
||||
if(infile.is_open())
|
||||
{
|
||||
static const auto desc = addBlockDescriptor("OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
while (infile >> timestamp >> thread_from >> thread_to)
|
||||
{
|
||||
beginContextSwitch(thread_from, timestamp, desc);
|
||||
endContextSwitch(thread_to, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
uint64_t usedMemorySize = 0;
|
||||
uint32_t blocks_number = 0;
|
||||
for (const auto& thread_storage : m_threads)
|
||||
{
|
||||
const auto& t = thread_storage.second;
|
||||
usedMemorySize += t.blocks.usedMemorySize + t.sync.usedMemorySize;
|
||||
blocks_number += static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size());
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
of.write(CPU_FREQUENCY);
|
||||
#else
|
||||
of.write(0LL);
|
||||
#endif
|
||||
|
||||
of.write(blocks_number);
|
||||
of.write(usedMemorySize);
|
||||
of.write(static_cast<uint32_t>(m_descriptors.size()));
|
||||
of.write(m_usedMemorySize);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
for (auto& thread_storage : m_threads)
|
||||
{
|
||||
auto& t = thread_storage.second;
|
||||
|
||||
of.write(thread_storage.first);
|
||||
#ifdef STORE_CSWITCHES_SEPARATELY
|
||||
of.write(static_cast<uint32_t>(t.blocks.closedList.size()));
|
||||
#else
|
||||
of.write(static_cast<uint32_t>(t.blocks.closedList.size()) + static_cast<uint32_t>(t.sync.closedList.size()));
|
||||
uint32_t i = 0;
|
||||
#endif
|
||||
|
||||
for (auto b : t.blocks.closedList)
|
||||
{
|
||||
#ifndef STORE_CSWITCHES_SEPARATELY
|
||||
if (i < t.sync.closedList.size())
|
||||
{
|
||||
auto s = t.sync.closedList[i];
|
||||
if (s->end() <= b->end())// || s->begin() >= b->begin())
|
||||
//if (((s->end() <= b->end() && s->end() >= b->begin()) || (s->begin() >= b->begin() && s->begin() <= b->end())))
|
||||
{
|
||||
if (s->m_begin < b->m_begin)
|
||||
s->m_begin = b->m_begin;
|
||||
if (s->m_end > b->m_end)
|
||||
s->m_end = b->m_end;
|
||||
of.writeBlock(s);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
of.writeBlock(b);
|
||||
}
|
||||
|
||||
#ifdef STORE_CSWITCHES_SEPARATELY
|
||||
of.write(static_cast<uint32_t>(t.sync.closedList.size()));
|
||||
for (auto b : t.sync.closedList)
|
||||
{
|
||||
#else
|
||||
for (; i < t.sync.closedList.size(); ++i)
|
||||
{
|
||||
auto b = t.sync.closedList[i];
|
||||
#endif
|
||||
|
||||
of.writeBlock(b);
|
||||
}
|
||||
|
||||
t.clearClosed();
|
||||
}
|
||||
|
||||
// if (wasEnabled)
|
||||
// ::profiler::setEnabled(true);
|
||||
|
||||
return blocks_number;
|
||||
}
|
||||
|
||||
uint32_t ProfileManager::dumpBlocksToFile(const char* filename)
|
||||
{
|
||||
StreamWriter os;
|
||||
auto res = dumpBlocksToStream(os);
|
||||
|
||||
|
||||
|
||||
std::ofstream of(filename, std::fstream::binary);
|
||||
of << os.stream().str();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line)
|
||||
{
|
||||
if (THREAD_STORAGE == nullptr)
|
||||
{
|
||||
THREAD_STORAGE = &threadStorage(getCurrentThreadId());
|
||||
}
|
||||
|
||||
if (!THREAD_STORAGE->named)
|
||||
{
|
||||
const auto id = addBlockDescriptor(_funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black);
|
||||
THREAD_STORAGE->storeBlock(profiler::Block(profiler::BLOCK_TYPE_THREAD_SIGN, id, name));
|
||||
THREAD_STORAGE->name = name;
|
||||
THREAD_STORAGE->named = true;
|
||||
}
|
||||
|
||||
return THREAD_STORAGE->name.c_str();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
*/
|
||||
|
||||
void ProfileManager::startListen()
|
||||
{
|
||||
|
||||
@ -514,7 +604,7 @@ void ProfileManager::startListen()
|
||||
//if connection aborted - ignore this part
|
||||
|
||||
profiler::net::DataMessage dm;
|
||||
StreamWriter os;
|
||||
profiler::OStream os;
|
||||
dumpBlocksToStream(os);
|
||||
dm.size = (uint32_t)os.stream().str().length();
|
||||
|
||||
@ -558,3 +648,5 @@ void ProfileManager::startListen()
|
||||
}
|
||||
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -16,27 +16,26 @@ 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 "profiler/serialized_block.h"
|
||||
|
||||
#include "easy_socket.h"
|
||||
#include "profiler/easy_socket.h"
|
||||
#include "spin_lock.h"
|
||||
|
||||
#include <stack>
|
||||
#include "outstream.h"
|
||||
#include "hashed_cstr.h"
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef _WIN32
|
||||
@ -59,50 +58,182 @@ inline uint32_t getCurrentThreadId()
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace profiler { class SerializedBlock; }
|
||||
namespace profiler {
|
||||
|
||||
class SerializedBlock;
|
||||
|
||||
struct do_not_calc_hash {
|
||||
template <class T> inline size_t operator()(T _value) const {
|
||||
return static_cast<size_t>(_value);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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 + 1 < 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;
|
||||
}
|
||||
|
||||
inline bool empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
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 + 1 < 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;
|
||||
|
||||
@ -125,6 +256,11 @@ struct BlocksList final
|
||||
m_stack.emplace_back(_block);
|
||||
}
|
||||
|
||||
inline void emplace(profiler::Block&& _block) {
|
||||
//m_stack.emplace(_block);
|
||||
m_stack.emplace_back(std::forward<profiler::Block&&>(_block));
|
||||
}
|
||||
|
||||
template <class ... TArgs> inline void emplace(TArgs ... _args) {
|
||||
//m_stack.emplace(_args);
|
||||
m_stack.emplace_back(_args...);
|
||||
@ -141,14 +277,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;
|
||||
}
|
||||
};
|
||||
@ -158,8 +292,8 @@ class ThreadStorage final
|
||||
{
|
||||
public:
|
||||
|
||||
BlocksList<std::reference_wrapper<profiler::Block>, SIZEOF_CSWITCH * (uint16_t)1024U> blocks;
|
||||
BlocksList<profiler::Block, SIZEOF_CSWITCH * (uint16_t)128U> sync;
|
||||
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;
|
||||
|
||||
@ -170,51 +304,42 @@ public:
|
||||
ThreadStorage() = default;
|
||||
};
|
||||
|
||||
class StreamWriter final
|
||||
{
|
||||
std::stringstream m_stream;
|
||||
|
||||
public:
|
||||
|
||||
explicit StreamWriter() : 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));
|
||||
}
|
||||
|
||||
void writeBlock(const profiler::SerializedBlock* _block)
|
||||
{
|
||||
auto sz = static_cast<uint16_t>(sizeof(profiler::BaseBlockData) + strlen(_block->name()) + 1);
|
||||
write(sz);
|
||||
write(_block->data(), sz);
|
||||
}
|
||||
const std::stringstream& stream() const {return m_stream;}
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class ProfileManager final
|
||||
{
|
||||
friend profiler::BlockDescRef;
|
||||
|
||||
ProfileManager();
|
||||
ProfileManager(const ProfileManager& p) = delete;
|
||||
ProfileManager& operator=(const ProfileManager&) = delete;
|
||||
|
||||
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::BlockDescriptor*> block_descriptors_t;
|
||||
|
||||
map_of_threads_stacks m_threads;
|
||||
block_descriptors_t m_descriptors;
|
||||
uint64_t m_usedMemorySize = 0;
|
||||
profiler::spin_lock m_spin;
|
||||
profiler::spin_lock m_storedSpin;
|
||||
bool m_isEnabled = false;
|
||||
#ifdef _WIN32
|
||||
typedef std::unordered_map<profiler::hashed_cstr, bool> blocks_enable_status_t;
|
||||
#else
|
||||
typedef std::unordered_map<profiler::hashed_stdstring, bool> blocks_enable_status_t;
|
||||
#endif
|
||||
|
||||
map_of_threads_stacks m_threads;
|
||||
block_descriptors_t m_descriptors;
|
||||
blocks_enable_status_t m_blocksEnableStatus;
|
||||
uint64_t m_usedMemorySize = 0;
|
||||
profiler::spin_lock m_spin;
|
||||
profiler::spin_lock m_storedSpin;
|
||||
profiler::block_id_t m_idCounter = 0;
|
||||
std::atomic_bool m_isEnabled;
|
||||
std::atomic_bool m_isEventTracingEnabled;
|
||||
|
||||
#ifndef _WIN32
|
||||
std::string m_csInfoFilename = "/tmp/cs_profiling_info.log";
|
||||
#endif
|
||||
|
||||
uint32_t dumpBlocksToStream(profiler::OStream& _outputStream);
|
||||
void setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled);
|
||||
|
||||
std::thread m_listenThread;
|
||||
bool m_isAlreadyListened = false;
|
||||
@ -222,7 +347,6 @@ class ProfileManager final
|
||||
|
||||
int m_socket = 0;//TODO crossplatform
|
||||
|
||||
uint32_t dumpBlocksToStream(StreamWriter& _stream);
|
||||
std::atomic_bool m_stopListen;
|
||||
public:
|
||||
|
||||
@ -230,20 +354,37 @@ public:
|
||||
~ProfileManager();
|
||||
|
||||
template <class ... TArgs>
|
||||
uint32_t addBlockDescriptor(TArgs ... _args)
|
||||
const profiler::BaseBlockDescriptor* addBlockDescriptor(bool _enabledByDefault, const char* _autogenUniqueId, TArgs ... _args)
|
||||
{
|
||||
auto desc = new profiler::BlockDescriptor(m_usedMemorySize, _enabledByDefault, _args...);
|
||||
|
||||
guard_lock_t lock(m_storedSpin);
|
||||
const auto id = static_cast<uint32_t>(m_descriptors.size());
|
||||
m_descriptors.emplace_back(m_usedMemorySize, _args...);
|
||||
return id;
|
||||
desc->m_id = m_idCounter++;
|
||||
m_descriptors.emplace_back(desc);
|
||||
|
||||
blocks_enable_status_t::key_type key(_autogenUniqueId);
|
||||
auto it = m_blocksEnableStatus.find(key);
|
||||
if (it != m_blocksEnableStatus.end())
|
||||
{
|
||||
desc->m_enabled = it->second;
|
||||
desc->m_pEnable = &it->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
desc->m_pEnable = &m_blocksEnableStatus.emplace(key, desc->enabled()).first->second;
|
||||
}
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
void storeBlock(const profiler::BaseBlockDescriptor& _desc, const char* _runtimeName);
|
||||
void beginBlock(profiler::Block& _block);
|
||||
void endBlock();
|
||||
void setEnabled(bool isEnable);
|
||||
uint32_t dumpBlocksToFile(const char* filename);
|
||||
const char* setThreadName(const char* name, const char* filename, const char* _funcname, int line);
|
||||
const char* registerThread(const char* name);// , const char* filename, const char* _funcname, int line);
|
||||
|
||||
#ifndef _WIN32
|
||||
void setContextSwitchLogFilename(const char* name)
|
||||
{
|
||||
m_csInfoFilename = name;
|
||||
@ -253,28 +394,24 @@ public:
|
||||
{
|
||||
return m_csInfoFilename.c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id);
|
||||
void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id);
|
||||
void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime);
|
||||
|
||||
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin = true);
|
||||
void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin = true);
|
||||
void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin = true);
|
||||
void startListenSignalToCapture();
|
||||
void stopListenSignalToCapture();
|
||||
|
||||
private:
|
||||
|
||||
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
return m_threads[_thread_id];
|
||||
}
|
||||
void markExpired(profiler::block_id_t _id);
|
||||
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id);
|
||||
ThreadStorage* _findThreadStorage(profiler::thread_id_t _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;
|
||||
return _findThreadStorage(_thread_id);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif // EASY_PROFILER____MANAGER____H______
|
||||
|