0
0
mirror of https://github.com/yse/easy_profiler.git synced 2024-12-28 01:04:41 +08:00

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

(profiler_core) Fixed bug with memory corruption in chunk_allocator;
(profiler_core) Event tracing now gathering info about process which owns thread;
(profiler_core) Thread names are stored before other blocks now;
(profiler_core) Removed unused block types: context switch and thread sign;
(profiler_core) Added some description for API;
(profiler_gui) Context switch events now painted in different colors: red = switch to another process, coral = switch to registered thread in the same process, black = switch to kernel mode (thread id == 0).
(profiler_gui) Added popup windows with information about context-switch or block under mouse cursor;
This commit is contained in:
Victor Zarubkin 2016-09-13 23:03:01 +03:00
parent f490641165
commit 1b33ad788d
15 changed files with 1008 additions and 466 deletions

View File

@ -29,7 +29,7 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#ifndef FULL_DISABLE_PROFILER #ifndef FULL_DISABLE_PROFILER
/** /**
\defgroup profiler Profiler \defgroup profiler EasyProfiler
*/ */
/** If != 0 then EasyProfiler will measure time for blocks storage expansion. /** If != 0 then EasyProfiler will measure time for blocks storage expansion.
@ -50,7 +50,16 @@ If false then you need to enable these events via GUI if you'll want to see them
*/ */
# define EASY_STORAGE_EXPAND_ENABLED true # define EASY_STORAGE_EXPAND_ENABLED true
/** Macro of beginning of block with custom name and color. /** 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 \code
#include "profiler/profiler.h" #include "profiler/profiler.h"
@ -80,11 +89,11 @@ Block will be automatically completed by destructor.
*/ */
# define EASY_BLOCK(name, ...)\ # define EASY_BLOCK(name, ...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
EASY_COMPILETIME_NAME(name), __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ 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::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
/** Macro of beginning of block with function name and custom color. /** Macro for beginning of a block with function name and custom color.
\code \code
#include "profiler/profiler.h" #include "profiler/profiler.h"
@ -110,11 +119,11 @@ Name of the block automatically created with function name.
*/ */
# define EASY_FUNCTION(...)\ # define EASY_FUNCTION(...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__),\
__func__, __FILE__, __LINE__, ::profiler::BLOCK_TYPE_BLOCK, ::profiler::extract_color(__VA_ARGS__)));\ 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::Block EASY_UNIQUE_BLOCK(__LINE__)(EASY_UNIQUE_DESC(__LINE__), "");\
::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable ::profiler::beginBlock(EASY_UNIQUE_BLOCK(__LINE__)); // this is to avoid compiler warning about unused variable
/** Macro of completion of last nearest open block. /** Macro for completion of last opened block.
\code \code
#include "profiler/profiler.h" #include "profiler/profiler.h"
@ -139,7 +148,7 @@ int foo()
*/ */
# 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. Event is a block with zero duration and special type.
@ -150,44 +159,61 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
*/ */
# define EASY_EVENT(name, ...)\ # define EASY_EVENT(name, ...)\
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(\ static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(\
::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\ ::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\
::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\ __FILE__, __LINE__, ::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\
::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name)); ::profiler::storeEvent(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));
/** Macro enabling profiler /** Macro for enabling profiler.
\ingroup profiler \ingroup profiler
*/ */
# define EASY_PROFILER_ENABLE ::profiler::setEnabled(true); # define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
/** Macro disabling profiler /** Macro for disabling profiler.
\ingroup profiler \ingroup profiler
*/ */
# define EASY_PROFILER_DISABLE ::profiler::setEnabled(false); # define EASY_PROFILER_DISABLE ::profiler::setEnabled(false);
/** Macro of naming current thread. /** Macro for current thread registration.
If this thread has been already named then nothing changes. \note If this thread has been already registered then nothing happens.
\ingroup profiler \ingroup profiler
*/ */
# define EASY_THREAD(name)\ # define EASY_THREAD(name)\
EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\ EASY_THREAD_LOCAL static const char* EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = nullptr;\
if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\ if (EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) == nullptr)\
EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::setThreadName(name, __FILE__, __func__, __LINE__); EASY_TOKEN_CONCATENATE(unique_profiler_thread_name, __LINE__) = ::profiler::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 \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 #else
# define EASY_MEASURE_STORAGE_EXPAND 0 # define EASY_MEASURE_STORAGE_EXPAND 0
# define EASY_STORAGE_EXPAND_ENABLED false # define EASY_STORAGE_EXPAND_ENABLED false
# define EASY_EVENT_TRACING_ENABLED false
# define EASY_BLOCK(...) # define EASY_BLOCK(...)
# define EASY_FUNCTION(...) # define EASY_FUNCTION(...)
# define EASY_END_BLOCK # define EASY_END_BLOCK
@ -196,6 +222,12 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe
# define EASY_EVENT(...) # define EASY_EVENT(...)
# define EASY_THREAD(...) # define EASY_THREAD(...)
# define EASY_MAIN_THREAD # define EASY_MAIN_THREAD
# ifndef _WIN32
# define EASY_EVENT_TRACING_SET_LOG(filename)
# define EASY_EVENT_TRACING_LOG ""
# endif
#endif #endif
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -216,9 +248,7 @@ namespace profiler {
enum BlockType : uint8_t enum BlockType : uint8_t
{ {
BLOCK_TYPE_EVENT = 0, BLOCK_TYPE_EVENT = 0,
BLOCK_TYPE_THREAD_SIGN,
BLOCK_TYPE_BLOCK, BLOCK_TYPE_BLOCK,
BLOCK_TYPE_CONTEXT_SWITCH,
BLOCK_TYPES_NUMBER BLOCK_TYPES_NUMBER
}; };
@ -290,9 +320,10 @@ namespace profiler {
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_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 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); BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color);
void setId(block_id_t _id);
public: public:
@ -336,6 +367,11 @@ namespace profiler {
inline const char* name() const { return m_name; } inline const char* name() const { return m_name; }
private:
Block(const Block&) = delete;
Block& operator = (const Block&) = delete;
}; // END of class Block. }; // END of class Block.
//*********************************************** //***********************************************
@ -360,17 +396,80 @@ namespace profiler {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// Core API // Core API
// Note: it is better to use macros defined above than a direct calls to API.
extern "C" { extern "C" {
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color);
PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName); /** 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); PROFILER_API void beginBlock(Block& _block);
/** Ends last started block.
\ingroup profiler
*/
PROFILER_API void endBlock(); PROFILER_API void endBlock();
/** Enable or disable profiler.
\ingroup profiler
*/
PROFILER_API void setEnabled(bool _isEnable); 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); PROFILER_API uint32_t dumpBlocksToFile(const char* _filename);
PROFILER_API const char* setThreadName(const char* _name, const char* _filename, const char* _funcname, int _line);
/** 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); 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(); PROFILER_API const char* getContextSwitchLogFilename();
#endif
} }
inline void setEnabled(::profiler::EasyEnableFlag _isEnable) { inline void setEnabled(::profiler::EasyEnableFlag _isEnable) {

View File

@ -86,6 +86,12 @@ namespace profiler {
ENABLED = 1 ENABLED = 1
}; };
struct passthrough_hash final {
template <class T> inline size_t operator () (T _value) const {
return static_cast<size_t>(_value);
}
};
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -95,6 +101,7 @@ namespace profiler {
#include <type_traits> #include <type_traits>
# define EASY_STRINGIFY(a) #a # define EASY_STRINGIFY(a) #a
# define EASY_STRINGIFICATION(a) EASY_STRINGIFY(a)
# define EASY_TOKEN_JOIN(x, y) x ## y # define EASY_TOKEN_JOIN(x, y) x ## y
# define EASY_TOKEN_CONCATENATE(x, y) EASY_TOKEN_JOIN(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_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x)
@ -165,7 +172,7 @@ namespace profiler {
} // END of namespace profiler. } // END of namespace profiler.
# define EASY_UNIQUE_LINE_ID __FILE__ ":" EASY_STRINGIFY(__LINE__) # 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_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) # define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::runtime_name(name)

View File

@ -21,8 +21,9 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <map> #include <unordered_map>
#include <vector> #include <vector>
#include <string>
#include <atomic> #include <atomic>
#include "profiler/profiler.h" #include "profiler/profiler.h"
#include "profiler/serialized_block.h" #include "profiler/serialized_block.h"
@ -175,7 +176,7 @@ namespace profiler {
BlocksTree::children_t children; BlocksTree::children_t children;
BlocksTree::children_t sync; BlocksTree::children_t sync;
const char* thread_name; std::string thread_name;
::profiler::thread_id_t thread_id; ::profiler::thread_id_t thread_id;
uint16_t depth; uint16_t depth;
@ -186,7 +187,7 @@ namespace profiler {
BlocksTreeRoot(This&& that) BlocksTreeRoot(This&& that)
: children(::std::move(that.children)) : children(::std::move(that.children))
, sync(::std::move(that.sync)) , sync(::std::move(that.sync))
, thread_name(that.thread_name) , thread_name(::std::move(that.thread_name))
, thread_id(that.thread_id) , thread_id(that.thread_id)
, depth(that.depth) , depth(that.depth)
{ {
@ -196,12 +197,24 @@ namespace profiler {
{ {
children = ::std::move(that.children); children = ::std::move(that.children);
sync = ::std::move(that.sync); sync = ::std::move(that.sync);
thread_name = that.thread_name; thread_name = ::std::move(that.thread_name);
thread_id = that.thread_id; thread_id = that.thread_id;
depth = that.depth; depth = that.depth;
return *this; return *this;
} }
inline bool gotName() 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 bool operator < (const This& other) const
{ {
return thread_id < other.thread_id; return thread_id < other.thread_id;
@ -215,7 +228,7 @@ namespace profiler {
}; // END of class BlocksTreeRoot. }; // END of class BlocksTreeRoot.
typedef ::profiler::BlocksTree::blocks_t blocks_t; 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;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -39,6 +39,9 @@
************************************************************************/ ************************************************************************/
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QGraphicsProxyWidget>
#include <QFormLayout>
#include <QLabel>
#include <QWheelEvent> #include <QWheelEvent>
#include <QMouseEvent> #include <QMouseEvent>
#include <QKeyEvent> #include <QKeyEvent>
@ -93,7 +96,11 @@ inline QRgb selectedItemBorderColor(::profiler::color_t _color)
//const unsigned int TEST_PROGRESSION_BASE = 4; //const unsigned int TEST_PROGRESSION_BASE = 4;
const int FLICKER_INTERVAL = 16; // 60Hz const int IDLE_TIMER_INTERVAL = 200; // 5Hz
const uint64_t IDLE_TIME = 400;
const int FLICKER_INTERVAL = 10; // 100Hz
const qreal FLICKER_FACTOR = 16.0 / FLICKER_INTERVAL;
QFont EFont(const char* _family, int _size, int _weight = -1) QFont EFont(const char* _family, int _size, int _weight = -1)
{ {
@ -143,7 +150,7 @@ inline T logn(T _value)
EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root) EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root)
: QGraphicsItem(nullptr) : QGraphicsItem(nullptr)
, m_threadName(*_root.thread_name != 0 ? QString("%1 Thread %2").arg(_root.thread_name).arg(_root.thread_id) : QString("Thread %1").arg(_root.thread_id)) , m_threadName(_root.gotName() ? QString("%1 Thread %2").arg(_root.name()).arg(_root.thread_id) : QString("Thread %1").arg(_root.thread_id))
, m_pRoot(&_root) , m_pRoot(&_root)
, m_index(_index) , m_index(_index)
{ {
@ -171,13 +178,17 @@ QRectF EasyGraphicsItem::boundingRect() const
void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*) void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*, QWidget*)
{ {
if (m_levels.empty() || m_levels.front().empty()) const bool gotItems = !m_levels.empty() && !m_levels.front().empty();
const bool gotSync = !m_pRoot->sync.empty();
if (!gotItems && !gotSync)
{ {
return; return;
} }
const auto sceneView = view(); const auto sceneView = view();
const auto visibleSceneRect = sceneView->visibleSceneRect(); // Current visible scene rect const auto visibleSceneRect = sceneView->visibleSceneRect(); // Current visible scene rect
const auto visibleBottom = visibleSceneRect.bottom() - 1;
const auto currentScale = sceneView->scale(); // Current GraphicsView scale const auto currentScale = sceneView->scale(); // Current GraphicsView scale
const auto offset = sceneView->offset(); const auto offset = sceneView->offset();
const auto sceneLeft = offset, sceneRight = offset + visibleSceneRect.width() / currentScale; const auto sceneLeft = offset, sceneRight = offset + visibleSceneRect.width() / currentScale;
@ -199,6 +210,8 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
// Search for first visible top-level item // Search for first visible top-level item
if (gotItems)
{
auto& level0 = m_levels.front(); auto& level0 = m_levels.front();
auto first = ::std::lower_bound(level0.begin(), level0.end(), sceneLeft, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value) auto first = ::std::lower_bound(level0.begin(), level0.end(), sceneLeft, [](const ::profiler_gui::EasyBlockItem& _item, qreal _value)
{ {
@ -215,23 +228,8 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
{ {
m_levelsIndexes[0] = static_cast<unsigned int>(level0.size() - 1); m_levelsIndexes[0] = static_cast<unsigned int>(level0.size() - 1);
} }
auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value)
{
return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value;
});
if (firstSync != m_pRoot->sync.end())
{
if (firstSync != m_pRoot->sync.begin())
--firstSync;
} }
else if (!m_pRoot->sync.empty())
{
firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1;
}
firstSync = m_pRoot->sync.begin();
// This is to make _painter->drawText() work properly // This is to make _painter->drawText() work properly
@ -259,6 +257,10 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
} }
// Iterate through layers and draw visible items
if (gotItems)
{
static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max<decltype(::profiler_gui::EasyBlockItem::children_begin)>(); static const auto MAX_CHILD_INDEX = ::profiler_gui::numeric_max<decltype(::profiler_gui::EasyBlockItem::children_begin)>();
auto const skip_children = [this, &levelsNumber](short next_level, decltype(::profiler_gui::EasyBlockItem::children_begin) children_begin) auto const skip_children = [this, &levelsNumber](short next_level, decltype(::profiler_gui::EasyBlockItem::children_begin) children_begin)
{ {
@ -267,10 +269,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
m_levels[next_level][children_begin].state = BLOCK_ITEM_DO_NOT_PAINT; m_levels[next_level][children_begin].state = BLOCK_ITEM_DO_NOT_PAINT;
}; };
// Iterate through layers and draw visible items
bool selectedItemsWasPainted = false; bool selectedItemsWasPainted = false;
const auto visibleBottom = visibleSceneRect.bottom() - 1;
for (uint8_t l = 0; l < levelsNumber; ++l) for (uint8_t l = 0; l < levelsNumber; ++l)
{ {
auto& level = m_levels[l]; auto& level = m_levels[l];
@ -309,6 +308,8 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
} }
const auto& itemBlock = easyBlock(item.block); const auto& itemBlock = easyBlock(item.block);
const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id());
int h = 0, flags = 0; int h = 0, flags = 0;
if (w < 20 || !itemBlock.expanded) if (w < 20 || !itemBlock.expanded)
{ {
@ -323,11 +324,11 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
if (item.block == EASY_GLOBALS.selected_block) if (item.block == EASY_GLOBALS.selected_block)
selectedItemsWasPainted = true; selectedItemsWasPainted = true;
const bool colorChange = (previousColor != item.color); const bool colorChange = (previousColor != itemDesc.color());
if (colorChange) if (colorChange)
{ {
// Set background color brush for rectangle // Set background color brush for rectangle
previousColor = item.color; previousColor = itemDesc.color();
inverseColor = 0xffffffff - previousColor; inverseColor = 0xffffffff - previousColor;
textColor = ::profiler_gui::textColorForRgb(previousColor); textColor = ::profiler_gui::textColorForRgb(previousColor);
brush.setColor(previousColor); brush.setColor(previousColor);
@ -374,11 +375,11 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
if (item.block == EASY_GLOBALS.selected_block) if (item.block == EASY_GLOBALS.selected_block)
selectedItemsWasPainted = true; selectedItemsWasPainted = true;
const bool colorChange = (previousColor != item.color); const bool colorChange = (previousColor != itemDesc.color());
if (colorChange) if (colorChange)
{ {
// Set background color brush for rectangle // Set background color brush for rectangle
previousColor = item.color; previousColor = itemDesc.color();
inverseColor = 0xffffffff - previousColor; inverseColor = 0xffffffff - previousColor;
textColor = ::profiler_gui::textColorForRgb(previousColor); textColor = ::profiler_gui::textColorForRgb(previousColor);
brush.setColor(previousColor); brush.setColor(previousColor);
@ -435,7 +436,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
_painter->setFont(SELECTED_ITEM_FONT); _painter->setFont(SELECTED_ITEM_FONT);
// drawing text // drawing text
auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : itemDesc.name();
_painter->drawText(rect, flags, ::profiler_gui::toUnicode(name)); _painter->drawText(rect, flags, ::profiler_gui::toUnicode(name));
// restore previous pen color // restore previous pen color
@ -469,15 +470,18 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
if (dh > 0) if (dh > 0)
h -= dh; h -= dh;
const auto& itemBlock = easyBlock(item.block);
const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id());
QPen pen(Qt::SolidLine); QPen pen(Qt::SolidLine);
pen.setJoinStyle(Qt::MiterJoin); pen.setJoinStyle(Qt::MiterJoin);
pen.setColor(selectedItemBorderColor(item.color));//Qt::red); pen.setColor(selectedItemBorderColor(itemDesc.color()));//Qt::red);
pen.setWidth(3); pen.setWidth(3);
_painter->setPen(pen); _painter->setPen(pen);
if (!selectedItemsWasPainted) if (!selectedItemsWasPainted)
{ {
brush.setColor(item.color);// SELECTED_ITEM_COLOR); brush.setColor(itemDesc.color());// SELECTED_ITEM_COLOR);
_painter->setBrush(brush); _painter->setBrush(brush);
} }
else else
@ -512,13 +516,12 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
// text will be painted with inverse color // text will be painted with inverse color
//auto textColor = 0x00ffffff - previousColor; //auto textColor = 0x00ffffff - previousColor;
//if (textColor == previousColor) textColor = 0; //if (textColor == previousColor) textColor = 0;
textColor = ::profiler_gui::textColorForRgb(item.color);// SELECTED_ITEM_COLOR); textColor = ::profiler_gui::textColorForRgb(itemDesc.color());// SELECTED_ITEM_COLOR);
_painter->setPen(textColor); _painter->setPen(textColor);
_painter->setFont(SELECTED_ITEM_FONT); _painter->setFont(SELECTED_ITEM_FONT);
// drawing text // drawing text
const auto& itemBlock = easyBlock(item.block);
auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name(); auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name();
_painter->drawText(rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name)); _painter->drawText(rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name));
// END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // END Draw text~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -527,49 +530,81 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
} }
} }
} }
}
if (!m_pRoot->sync.empty())
if (gotSync)
{ {
_painter->setBrush(QColor::fromRgb(::profiler::colors::Coral));// 0xfffe6030)); auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), sceneLeft, [&sceneView](::profiler::block_index_t _index, qreal _value)
_painter->setPen(QColor::fromRgb(::profiler::colors::Grey800));// 0x00505050)); {
return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value;
});
if (firstSync != m_pRoot->sync.end())
{
if (firstSync != m_pRoot->sync.begin())
--firstSync;
}
else if (!m_pRoot->sync.empty())
{
firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1;
}
//firstSync = m_pRoot->sync.begin();
previousColor = 0;
qreal prevRight = -1e100, top = y() - 4, h = 3; qreal prevRight = -1e100, top = y() - 4, h = 3;
if (top + h < visibleBottom) if (top + h < visibleBottom)
{ {
for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it) for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it)
{ {
const auto& item = easyBlock(*it).tree; const auto& item = easyBlock(*it).tree;
auto begin = sceneView->time2position(item.node->begin()); auto left = sceneView->time2position(item.node->begin());
if (begin > sceneRight) if (left > sceneRight)
break; // This is first totally invisible item. No need to check other items. break; // This is first totally invisible item. No need to check other items.
decltype(begin) width = sceneView->time2position(item.node->end()) - begin; decltype(left) width = sceneView->time2position(item.node->end()) - left;
auto r = begin + width; if (left + width < sceneLeft) // This item is not visible
if (r < sceneLeft) // This item is not visible
continue; continue;
begin *= currentScale; left *= currentScale;
begin -= dx; left -= dx;
width *= currentScale; width *= currentScale;
r = begin + width; if (left + width <= prevRight) // This item is not visible
if (r <= prevRight) // This item is not visible
continue; continue;
if (begin < prevRight) if (left < prevRight)
{ {
width -= prevRight - begin; width -= prevRight - left;
begin = prevRight; left = prevRight;
} }
if (width < 2) if (width < 2)
width = 2; width = 2;
//_painter->drawLine(QLineF(::std::max(begin, prevRight), top, begin + width, top)); const bool self_thread = item.node->id() != 0 && EASY_GLOBALS.profiler_blocks.find(item.node->id()) != EASY_GLOBALS.profiler_blocks.end();
rect.setRect(begin, top, width, h); ::profiler::color_t color = 0;
if (self_thread)
color = ::profiler::colors::Coral;
else if (item.node->id() == 0)
color = ::profiler::colors::Black;
else
color = ::profiler::colors::RedA400;
if (previousColor != color)
{
previousColor = color;
_painter->setBrush(QColor::fromRgb(color));
if (color != ::profiler::colors::Black)
_painter->setPen(QColor::fromRgb(0x00808080 & color));
else
_painter->setPen(QColor::fromRgb(::profiler::colors::Grey800));
}
rect.setRect(left, top, width, h);
_painter->drawRect(rect); _painter->drawRect(rect);
prevRight = begin + width; prevRight = left + width;
} }
} }
} }
@ -770,6 +805,56 @@ const ::profiler_gui::EasyBlockItem* EasyGraphicsItem::intersect(const QPointF&
return nullptr; return nullptr;
} }
const ::profiler_gui::EasyBlock* EasyGraphicsItem::intersectEvent(const QPointF& _pos) const
{
if (m_pRoot->sync.empty())
{
return nullptr;
}
const auto top = y() - 6;
if (top > _pos.y())
{
return nullptr;
}
const auto bottom = top + 5;
if (bottom < _pos.y())
{
return nullptr;
}
const auto sceneView = view();
const auto currentScale = view()->scale();
auto firstSync = ::std::lower_bound(m_pRoot->sync.begin(), m_pRoot->sync.end(), _pos.x(), [&sceneView](::profiler::block_index_t _index, qreal _value)
{
return sceneView->time2position(easyBlock(_index).tree.node->begin()) < _value;
});
if (firstSync == m_pRoot->sync.end())
firstSync = m_pRoot->sync.begin() + m_pRoot->sync.size() - 1;
else if (firstSync != m_pRoot->sync.begin())
--firstSync;
for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it)
{
const auto& item = easyBlock(*it);
const auto left = sceneView->time2position(item.tree.node->begin()) - 2;
if (left > _pos.x())
break;
const auto right = sceneView->time2position(item.tree.node->end()) + 2;
if (right < _pos.x())
continue;
return &item;
}
return nullptr;
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void EasyGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h) void EasyGraphicsItem::setBoundingRect(qreal x, qreal y, qreal w, qreal h)
@ -1223,12 +1308,16 @@ EasyGraphicsView::EasyGraphicsView(QWidget* _parent)
, m_scale(1) , m_scale(1)
, m_offset(0) , m_offset(0)
, m_timelineStep(0) , m_timelineStep(0)
, m_idleTime(0)
, m_mouseButtons(Qt::NoButton) , m_mouseButtons(Qt::NoButton)
, m_pScrollbar(nullptr) , m_pScrollbar(nullptr)
, m_chronometerItem(nullptr) , m_chronometerItem(nullptr)
, m_chronometerItemAux(nullptr) , m_chronometerItemAux(nullptr)
, m_csInfoWidget(nullptr)
, m_flickerSpeedX(0) , m_flickerSpeedX(0)
, m_flickerSpeedY(0) , m_flickerSpeedY(0)
, m_flickerCounterX(0)
, m_flickerCounterY(0)
, m_bDoubleClick(false) , m_bDoubleClick(false)
, m_bUpdatingRect(false) , m_bUpdatingRect(false)
, m_bEmpty(true) , m_bEmpty(true)
@ -1265,6 +1354,8 @@ void EasyGraphicsView::clearSilent()
m_flickerTimer.stop(); m_flickerTimer.stop();
m_flickerSpeedX = 0; m_flickerSpeedX = 0;
m_flickerSpeedY = 0; m_flickerSpeedY = 0;
m_flickerCounterX = 0;
m_flickerCounterY = 0;
// Clear all items // Clear all items
scene()->clear(); scene()->clear();
@ -1276,6 +1367,10 @@ void EasyGraphicsView::clearSilent()
m_timelineStep = 1; m_timelineStep = 1;
m_offset = 0; // scroll back to the beginning of the scene m_offset = 0; // scroll back to the beginning of the scene
m_idleTimer.stop();
m_idleTime = 0;
m_csInfoWidget = nullptr;
// Reset necessary flags // Reset necessary flags
//m_bTest = false; //m_bTest = false;
m_bEmpty = true; m_bEmpty = true;
@ -1307,28 +1402,30 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
{ {
const auto& t = threadTree.second; const auto& t = threadTree.second;
auto timestart = blocksTree(t.children.front()).node->begin(); auto timestart = m_beginTime;
auto timefinish = finish;
if (!t.children.empty())
timestart = blocksTree(t.children.front()).node->begin();
if (!t.sync.empty()) if (!t.sync.empty())
timestart = ::std::min(timestart, blocksTree(t.sync.front()).node->begin()); timestart = ::std::min(timestart, blocksTree(t.sync.front()).node->begin());
const auto timefinish = blocksTree(t.children.back()).node->end(); if (!t.children.empty())
timefinish = blocksTree(t.children.back()).node->end();
if (!t.sync.empty())
timefinish = ::std::max(timefinish, blocksTree(t.sync.back()).node->end());
if (m_beginTime > timestart) if (m_beginTime > timestart)
{
m_beginTime = timestart; m_beginTime = timestart;
}
if (finish < timefinish) if (finish < timefinish) {
{
finish = timefinish; finish = timefinish;
longestTree = threadTree.first; longestTree = threadTree.first;
} }
if (mainTree == 0 && !strcmp(t.thread_name, "Main")) if (mainTree == 0 && !strcmp(t.name(), "Main"))
{
mainTree = threadTree.first; mainTree = threadTree.first;
} }
}
// Filling scene with items // Filling scene with items
m_items.reserve(_blocksTree.size()); m_items.reserve(_blocksTree.size());
@ -1345,13 +1442,29 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
const auto& t = threadTree.second; const auto& t = threadTree.second;
// fill scene with new items // fill scene with new items
const auto& tree = t.children; qreal h = 0, x = 0;
qreal h = 0, x = time2position(blocksTree(tree.front()).node->begin());
if (!t.children.empty())
x = time2position(blocksTree(t.children.front()).node->begin());
else if (!t.sync.empty())
x = time2position(blocksTree(t.sync.front()).node->begin());
auto item = new EasyGraphicsItem(static_cast<uint8_t>(m_items.size()), t); auto item = new EasyGraphicsItem(static_cast<uint8_t>(m_items.size()), t);
if (t.depth)
item->setLevels(t.depth); item->setLevels(t.depth);
item->setPos(0, y); item->setPos(0, y);
const auto children_duration = setTree(item, tree, h, y, 0); qreal children_duration = 0;
if (!t.children.empty())
{
children_duration = setTree(item, t.children, h, y, 0);
}
else if (!t.sync.empty())
{
children_duration = time2position(blocksTree(t.sync.back()).node->end()) - x;
h = GRAPHICS_ROW_SIZE;
}
item->setBoundingRect(0, 0, children_duration + x, h); item->setBoundingRect(0, 0, children_duration + x, h);
m_items.push_back(item); m_items.push_back(item);
@ -1369,8 +1482,6 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
// Calculating scene rect // Calculating scene rect
const qreal endX = time2position(finish) + 1500.0; const qreal endX = time2position(finish) + 1500.0;
scene()->setSceneRect(0, 0, endX, y + TIMELINE_ROW_SIZE); scene()->setSceneRect(0, 0, endX, y + TIMELINE_ROW_SIZE);
//for (auto item : m_items)
// item->setBoundingRect(0, 0, endX, item->boundingRect().height());
// Center view on the beginning of the scene // Center view on the beginning of the scene
updateVisibleSceneRect(); updateVisibleSceneRect();
@ -1387,7 +1498,6 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
scene()->addItem(indicator); scene()->addItem(indicator);
// Setting flags // Setting flags
//m_bTest = false;
m_bEmpty = false; m_bEmpty = false;
scaleTo(BASE_SCALE); scaleTo(BASE_SCALE);
@ -1396,7 +1506,9 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
emit treeChanged(); emit treeChanged();
if (mainThreadItem != nullptr) if (mainThreadItem != nullptr)
{
longestItem = mainThreadItem; longestItem = mainThreadItem;
}
if (longestItem != nullptr) if (longestItem != nullptr)
{ {
@ -1404,6 +1516,8 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
EASY_GLOBALS.selected_thread = longestItem->threadId(); EASY_GLOBALS.selected_thread = longestItem->threadId();
emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId()); emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId());
} }
m_idleTimer.start(IDLE_TIMER_INTERVAL);
} }
const EasyGraphicsView::Items &EasyGraphicsView::getItems() const const EasyGraphicsView::Items &EasyGraphicsView::getItems() const
@ -1492,7 +1606,6 @@ qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, const ::profiler::Block
} }
b.block = child_index;// &child; b.block = child_index;// &child;
b.color = EASY_GLOBALS.descriptors[child.node->id()]->color();// ::profiler_gui::fromProfilerRgb(::profiler::colors::get_red(color), ::profiler::colors::get_green(color), ::profiler::colors::get_blue(color));
b.setPos(xbegin, duration); b.setPos(xbegin, duration);
b.totalHeight = GRAPHICS_ROW_SIZE + h; b.totalHeight = GRAPHICS_ROW_SIZE + h;
b.state = BLOCK_ITEM_UNCHANGED; b.state = BLOCK_ITEM_UNCHANGED;
@ -1600,6 +1713,8 @@ void EasyGraphicsView::scaleTo(qreal _scale)
void EasyGraphicsView::wheelEvent(QWheelEvent* _event) void EasyGraphicsView::wheelEvent(QWheelEvent* _event)
{ {
m_idleTime = 0;
if (!m_bEmpty) if (!m_bEmpty)
onWheel(mapToScene(_event->pos()).x(), _event->delta()); onWheel(mapToScene(_event->pos()).x(), _event->delta());
_event->accept(); _event->accept();
@ -1607,6 +1722,8 @@ void EasyGraphicsView::wheelEvent(QWheelEvent* _event)
void EasyGraphicsView::onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta) void EasyGraphicsView::onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta)
{ {
m_idleTime = 0;
for (auto item : m_items) for (auto item : m_items)
{ {
if (item->threadId() == EASY_GLOBALS.selected_thread) if (item->threadId() == EASY_GLOBALS.selected_thread)
@ -1657,6 +1774,8 @@ void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta)
void EasyGraphicsView::mousePressEvent(QMouseEvent* _event) void EasyGraphicsView::mousePressEvent(QMouseEvent* _event)
{ {
m_idleTime = 0;
if (m_bEmpty) if (m_bEmpty)
{ {
_event->accept(); _event->accept();
@ -1680,6 +1799,8 @@ void EasyGraphicsView::mousePressEvent(QMouseEvent* _event)
void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event) void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event)
{ {
m_idleTime = 0;
if (m_bEmpty) if (m_bEmpty)
{ {
_event->accept(); _event->accept();
@ -1706,6 +1827,8 @@ void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event)
void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event) void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
{ {
m_idleTime = 0;
if (m_bEmpty) if (m_bEmpty)
{ {
_event->accept(); _event->accept();
@ -1867,6 +1990,8 @@ bool EasyGraphicsView::moveChrono(EasyChronometerItem* _chronometerItem, qreal _
void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event) void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event)
{ {
m_idleTime = 0;
if (m_bEmpty || (m_mouseButtons == 0 && !m_chronometerItem->isVisible())) if (m_bEmpty || (m_mouseButtons == 0 && !m_chronometerItem->isVisible()))
{ {
_event->accept(); _event->accept();
@ -1922,7 +2047,7 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event)
// Update flicker speed // Update flicker speed
m_flickerSpeedX += delta.x() >> 1; m_flickerSpeedX += delta.x() >> 1;
m_flickerSpeedY += delta.y() >> 1; m_flickerSpeedY += delta.y();
if (!m_flickerTimer.isActive()) if (!m_flickerTimer.isActive())
{ {
// If flicker timer is not started, then start it // If flicker timer is not started, then start it
@ -1955,6 +2080,7 @@ void EasyGraphicsView::keyPressEvent(QKeyEvent* _event)
static const int KeyStep = 100; static const int KeyStep = 100;
const int key = _event->key(); const int key = _event->key();
m_idleTime = 0;
switch (key) switch (key)
{ {
@ -2009,6 +2135,7 @@ void EasyGraphicsView::keyPressEvent(QKeyEvent* _event)
void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event) void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event)
{ {
const int key = _event->key(); const int key = _event->key();
m_idleTime = 0;
m_keys.erase(key); m_keys.erase(key);
_event->accept(); _event->accept();
@ -2054,6 +2181,7 @@ void EasyGraphicsView::initMode()
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange); connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange);
connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout); connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout);
connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout);
auto globalSignals = &EASY_GLOBALS.events; auto globalSignals = &EASY_GLOBALS.events;
connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange); connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange);
@ -2088,6 +2216,9 @@ void EasyGraphicsView::onGraphicsScrollbarValueChange(qreal _value)
void EasyGraphicsView::onFlickerTimeout() void EasyGraphicsView::onFlickerTimeout()
{ {
++m_flickerCounterX;
++m_flickerCounterY;
if (m_mouseButtons & Qt::LeftButton) if (m_mouseButtons & Qt::LeftButton)
{ {
// Fast slow-down and stop if mouse button is pressed, no flicking. // Fast slow-down and stop if mouse button is pressed, no flicking.
@ -2112,14 +2243,108 @@ void EasyGraphicsView::onFlickerTimeout()
updateVisibleSceneRect(); // Update scene visible rect only once updateVisibleSceneRect(); // Update scene visible rect only once
repaintScene(); // repaint scene repaintScene(); // repaint scene
m_flickerSpeedX -= absmin(sign(m_flickerSpeedX), m_flickerSpeedX); const int dx = static_cast<int>(sign(m_flickerSpeedX) * m_flickerCounterX / FLICKER_FACTOR);
m_flickerSpeedY -= absmin(sign(m_flickerSpeedY), m_flickerSpeedY); const int dy = static_cast<int>(sign(m_flickerSpeedY) * m_flickerCounterY / FLICKER_FACTOR);
if (abs(dx) > 0)
{
m_flickerSpeedX -= absmin(dx, m_flickerSpeedX);
m_flickerCounterX = 0;
}
if (abs(dy) > 0)
{
m_flickerSpeedY -= absmin(dy, m_flickerSpeedY);
m_flickerCounterY = 0;
}
} }
if (m_flickerSpeedX == 0 && m_flickerSpeedY == 0) if (m_flickerSpeedX == 0 && m_flickerSpeedY == 0)
{ {
// Flicker stopped, no timer needed. // Flicker stopped, no timer needed.
m_flickerTimer.stop(); m_flickerTimer.stop();
m_flickerSpeedX = 0;
m_flickerSpeedY = 0;
m_flickerCounterX = 0;
m_flickerCounterY = 0;
}
}
//////////////////////////////////////////////////////////////////////////
void EasyGraphicsView::onIdleTimeout()
{
m_idleTime += IDLE_TIMER_INTERVAL;
if (m_idleTime > IDLE_TIME)
{
if (m_csInfoWidget != nullptr)
return;
auto scenePos = mapToScene(mapFromGlobal(QCursor::pos()));
decltype(scenePos) pos(m_offset + scenePos.x() / m_scale, scenePos.y());
// Try to select one of context switches or items
for (auto item : m_items)
{
auto block = item->intersect(pos);
if (block)
{
const auto& itemBlock = easyBlock(block->block);
auto name = *itemBlock.tree.node->name() != 0 ? itemBlock.tree.node->name() : easyDescriptor(itemBlock.tree.node->id()).name();
auto widget = new QWidget();
auto lay = new QFormLayout(widget);
lay->setLabelAlignment(Qt::AlignRight);
lay->addRow("Name:", new QLabel(name));
lay->addRow("Duration:", new QLabel(::profiler_gui::timeStringReal(units2microseconds(block->w), 3)));
m_csInfoWidget = new QGraphicsProxyWidget();
m_csInfoWidget->setWidget(widget);
break;
}
auto cse = item->intersectEvent(pos);
if (cse)
{
auto widget = new QWidget();
auto lay = new QFormLayout(widget);
lay->setLabelAlignment(Qt::AlignRight);
auto it = EASY_GLOBALS.profiler_blocks.find(cse->tree.node->id());
if (it != EASY_GLOBALS.profiler_blocks.end())
lay->addRow("Thread:", new QLabel(QString("%1 %2").arg(cse->tree.node->id()).arg(it->second.name())));
else
lay->addRow("Thread:", new QLabel(QString::number(cse->tree.node->id())));
lay->addRow("Process:", new QLabel(cse->tree.node->name()));
lay->addRow("Duration:", new QLabel(::profiler_gui::timeStringReal(PROF_MICROSECONDS(cse->tree.node->duration()), 3)));
m_csInfoWidget = new QGraphicsProxyWidget();
m_csInfoWidget->setWidget(widget);
break;
}
}
if (m_csInfoWidget != nullptr)
{
scene()->addItem(m_csInfoWidget);
auto br = m_csInfoWidget->boundingRect();
if (scenePos.y() + br.height() > m_visibleSceneRect.bottom())
scenePos.setY(scenePos.y() - br.height());
if (scenePos.x() + br.width() > m_visibleSceneRect.right())
scenePos.setX(scenePos.x() - br.width());
m_csInfoWidget->setPos(scenePos);
m_csInfoWidget->setOpacity(0.85);
}
}
else if (m_csInfoWidget != nullptr)
{
scene()->removeItem(m_csInfoWidget);
m_csInfoWidget = nullptr;
} }
} }

View File

@ -161,6 +161,7 @@ public:
void getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const; void getBlocks(qreal _left, qreal _right, ::profiler_gui::TreeBlocks& _blocks) const;
const ::profiler_gui::EasyBlockItem* intersect(const QPointF& _pos) const; const ::profiler_gui::EasyBlockItem* intersect(const QPointF& _pos) const;
const ::profiler_gui::EasyBlock* intersectEvent(const QPointF& _pos) const;
private: private:
@ -272,6 +273,8 @@ EASY_QGRAPHICSITEM(EasyTimelineIndicatorItem);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
class QGraphicsProxyWidget;
class EasyGraphicsView : public QGraphicsView class EasyGraphicsView : public QGraphicsView
{ {
Q_OBJECT Q_OBJECT
@ -287,19 +290,24 @@ private:
Keys m_keys; ///< Pressed keys Keys m_keys; ///< Pressed keys
::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem) ::profiler_gui::TreeBlocks m_selectedBlocks; ///< Array of items which were selected by selection zone (EasyChronometerItem)
QTimer m_flickerTimer; ///< Timer for flicking behavior QTimer m_flickerTimer; ///< Timer for flicking behavior
QTimer m_idleTimer; ///<
QRectF m_visibleSceneRect; ///< Visible scene rectangle 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. ::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_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_offset; ///< Have to use manual offset for all scene content instead of using scrollbars because QScrollBar::value is 32-bit integer :(
qreal m_timelineStep; ///< qreal m_timelineStep; ///<
uint64_t m_idleTime; ///<
QPoint m_mousePressPos; ///< Last mouse global position (used by mousePressEvent and mouseMoveEvent) 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 QPoint m_mouseMovePath; ///< Mouse move path between press and release of any button
Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons Qt::MouseButtons m_mouseButtons; ///< Pressed mouse buttons
EasyGraphicsScrollbar* m_pScrollbar; ///< Pointer to the graphics scrollbar widget 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_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. 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_flickerSpeedX; ///< Current flicking speed x
int m_flickerSpeedY; ///< Current flicking speed y int m_flickerSpeedY; ///< Current flicking speed y
int m_flickerCounterX;
int m_flickerCounterY;
bool m_bDoubleClick; ///< Is mouse buttons double clicked 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_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 bool m_bEmpty; ///< Indicates whether scene is empty and has no items
@ -361,6 +369,7 @@ private slots:
void onScrollbarValueChange(int); void onScrollbarValueChange(int);
void onGraphicsScrollbarValueChange(qreal); void onGraphicsScrollbarValueChange(qreal);
void onFlickerTimeout(); void onFlickerTimeout();
void onIdleTimeout();
void onSelectedThreadChange(::profiler::thread_id_t _id); void onSelectedThreadChange(::profiler::thread_id_t _id);
void onSelectedBlockChange(unsigned int _block_index); void onSelectedBlockChange(unsigned int _block_index);
void onItemsEspandStateChange(); void onItemsEspandStateChange();

View File

@ -138,7 +138,6 @@ struct EasyBlockItem final
//const ::profiler::BlocksTree* block; ///< Pointer to profiler block //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) 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 float w; ///< Width of the item
QRgb color; ///< Background color of the item
::profiler::block_index_t block; ///< Index of profiler block ::profiler::block_index_t block; ///< Index of profiler block
uint32_t children_begin; ///< Index of first child item on the next sublevel 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 uint16_t totalHeight; ///< Total height of the item including heights of all it's children

View File

@ -515,9 +515,9 @@ void EasyGraphicsScrollbar::contextMenuEvent(QContextMenuEvent* _event)
for (const auto& it : EASY_GLOBALS.profiler_blocks) for (const auto& it : EASY_GLOBALS.profiler_blocks)
{ {
QString label; QString label;
if (it.second.thread_name && it.second.thread_name[0] != 0) if (it.second.gotName())
{ {
label = ::std::move(QString("%1 Thread %2").arg(it.second.thread_name).arg(it.first)); label = ::std::move(QString("%1 Thread %2").arg(it.second.name()).arg(it.first));
} }
else else
{ {

View File

@ -161,20 +161,14 @@ void FillTreeClass<T>::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI
const auto& root = threadTree.second; const auto& root = threadTree.second;
auto item = new EasyTreeWidgetItem(); auto item = new EasyTreeWidgetItem();
if (root.thread_name && root.thread_name[0] != 0) if (root.gotName())
{ item->setText(COL_NAME, QString("%1 Thread %2").arg(root.name()).arg(root.thread_id));
item->setText(COL_NAME, QString("%1 Thread %2").arg(root.thread_name).arg(root.thread_id));
}
else else
{
item->setText(COL_NAME, QString("Thread %1").arg(root.thread_id)); item->setText(COL_NAME, QString("Thread %1").arg(root.thread_id));
}
::profiler::timestamp_t duration = 0; ::profiler::timestamp_t duration = 0;
if (!root.children.empty()) if (!root.children.empty())
{
duration = blocksTree(root.children.back()).node->end() - blocksTree(root.children.front()).node->begin(); duration = blocksTree(root.children.back()).node->end() - blocksTree(root.children.front()).node->begin();
}
item->setTimeSmart(COL_DURATION, duration); item->setTimeSmart(COL_DURATION, duration);
item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
@ -259,19 +253,13 @@ void FillTreeClass<T>::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI
{ {
thread_item = new EasyTreeWidgetItem(); thread_item = new EasyTreeWidgetItem();
if (block.root->thread_name && block.root->thread_name[0] != 0) if (block.root->gotName())
{ thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->name()).arg(block.root->thread_id));
thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->thread_name).arg(block.root->thread_id));
}
else else
{
thread_item->setText(COL_NAME, QString("Thread %1").arg(block.root->thread_id)); thread_item->setText(COL_NAME, QString("Thread %1").arg(block.root->thread_id));
}
if (!block.root->children.empty()) if (!block.root->children.empty())
{
duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin(); duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin();
}
thread_item->setTimeSmart(COL_DURATION, duration); thread_item->setTimeSmart(COL_DURATION, duration);
thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);

View File

@ -3,7 +3,9 @@
#ifdef _WIN32 #ifdef _WIN32
#include <memory.h> #include <memory.h>
#include <chrono> #include <chrono>
#include <unordered_map>
#include "event_trace_win.h" #include "event_trace_win.h"
#include "Psapi.h"
#include "profiler/profiler.h" #include "profiler/profiler.h"
#include "profile_manager.h" #include "profile_manager.h"
@ -15,6 +17,16 @@
namespace profiler { namespace profiler {
//////////////////////////////////////////////////////////////////////////
struct ProcessInfo final {
std::string name;
uint32_t id = 0;
int8_t valid = 0;
};
//////////////////////////////////////////////////////////////////////////
// CSwitch class // CSwitch class
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx
// EventType = 36 // EventType = 36
@ -34,6 +46,17 @@ namespace profiler {
uint32_t Reserved; 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) void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent)
{ {
static const decltype(_traceEvent->EventHeader.EventDescriptor.Opcode) SWITCH_CONTEXT_OPCODE = 36; static const decltype(_traceEvent->EventHeader.EventDescriptor.Opcode) SWITCH_CONTEXT_OPCODE = 36;
@ -43,13 +66,79 @@ namespace profiler {
if (sizeof(CSwitch) != _traceEvent->UserDataLength) if (sizeof(CSwitch) != _traceEvent->UserDataLength)
return; return;
//EASY_FUNCTION(::profiler::colors::White); EASY_FUNCTION(::profiler::colors::White, ::profiler::DISABLED);
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData); auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
static const auto& desc = MANAGER.addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White); ProcessInfo* pinfo = nullptr;
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id()); 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 (success)
{
static char numbuf[128] = {};
sprintf(numbuf, "%u ", pid);
pinfo->name = numbuf;
pinfo->name += buf;
pinfo->id = pid;
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); MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, time);
} }
@ -109,6 +198,7 @@ namespace profiler {
{ {
static const decltype(m_properties.base.Wnode.ClientContext) RAW_TIMESTAMP_TIME_TYPE = 1; 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) if (m_bEnabled)
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY; return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
@ -142,12 +232,15 @@ namespace profiler {
// the controller stops the trace session. (Note that there may be a several-second delay before the function returns.) // 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 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx
m_stubThread = ::std::move(::std::thread([this]() m_processThread = ::std::move(::std::thread([this]()
{ {
EASY_THREAD("EasyProfiler.ETW"); EASY_THREAD("EasyProfiler.ETW");
ProcessTrace(&m_openedHandle, 1, 0, 0); ProcessTrace(&m_openedHandle, 1, 0, 0);
})); }));
// Set low priority for event tracing thread
SetThreadPriority(m_processThread.native_handle(), THREAD_PRIORITY_LOWEST);
m_bEnabled = true; m_bEnabled = true;
return EVENT_TRACING_LAUNCHED_SUCCESSFULLY; return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
@ -155,17 +248,23 @@ namespace profiler {
void EasyEventTracer::disable() void EasyEventTracer::disable()
{ {
profiler::guard_lock<profiler::spin_lock> lock(m_spin);
if (!m_bEnabled) if (!m_bEnabled)
return; return;
ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP); ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP);
CloseTrace(m_openedHandle); CloseTrace(m_openedHandle);
// Wait for ProcessThread to finish // Wait for ProcessTrace to finish to make sure no processTraceEvent() will be called later.
if (m_stubThread.joinable()) if (m_processThread.joinable())
m_stubThread.join(); m_processThread.join();
m_bEnabled = false; 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. } // END of namespace profiler.

View File

@ -12,7 +12,8 @@
#include <evntrace.h> #include <evntrace.h>
#include <evntcons.h> #include <evntcons.h>
#include <thread> #include <thread>
#include "profiler/event_trace_status.h" #include "event_trace_status.h"
#include "spin_lock.h"
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -29,9 +30,10 @@ namespace profiler {
}; };
#pragma pack(pop) #pragma pack(pop)
::std::thread m_stubThread; ::std::thread m_processThread;
Properties m_properties; Properties m_properties;
EVENT_TRACE_LOGFILE m_trace; EVENT_TRACE_LOGFILE m_trace;
profiler::spin_lock m_spin;
TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE; TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE;
TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE; TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE;
bool m_bEnabled = false; bool m_bEnabled = false;

View File

@ -64,7 +64,11 @@ namespace profiler {
public: public:
explicit cstring(const char* _str) : m_str(_str), m_len(strlen(_str)) cstring(const char* _str) : m_str(_str), m_len(strlen(_str))
{
}
cstring(const char* _str, size_t _len) : m_str(_str), m_len(_len)
{ {
} }
@ -91,6 +95,16 @@ namespace profiler {
return m_len < _other.m_len; 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. }; // END of class cstring.
/** \brief cstring with precalculated hash. /** \brief cstring with precalculated hash.
@ -110,11 +124,19 @@ namespace profiler {
public: public:
explicit hashed_cstr(const char* _str) : Parent(_str), m_hash(0) hashed_cstr(const char* _str) : Parent(_str), m_hash(0)
{ {
m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len); 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(const hashed_cstr&) = default;
hashed_cstr& operator = (const hashed_cstr&) = default; hashed_cstr& operator = (const hashed_cstr&) = default;
@ -206,6 +228,16 @@ namespace profiler {
return m_hash; 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 class hashed_stdstring.
} // END of namespace profiler. } // END of namespace profiler.

View File

@ -47,9 +47,9 @@ extern timestamp_t getCurrentTime();
extern "C" { extern "C" {
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, 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(_enabled, _name, _filename, _line, _block_type, _color); return MANAGER.addBlockDescriptor(_enabled, _autogenUniqueId, _name, _filename, _line, _block_type, _color);
} }
PROFILER_API void endBlock() PROFILER_API void endBlock()
@ -60,15 +60,9 @@ extern "C" {
PROFILER_API void setEnabled(bool isEnable) PROFILER_API void setEnabled(bool isEnable)
{ {
MANAGER.setEnabled(isEnable); MANAGER.setEnabled(isEnable);
#ifdef _WIN32
if (isEnable)
EasyEventTracer::instance().enable(true);
else
EasyEventTracer::instance().disable();
#endif
} }
PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName) PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName)
{ {
MANAGER.storeBlock(_desc, _runtimeName); MANAGER.storeBlock(_desc, _runtimeName);
} }
@ -83,11 +77,12 @@ extern "C" {
return MANAGER.dumpBlocksToFile(filename); 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) PROFILER_API void setContextSwitchLogFilename(const char* name)
{ {
return MANAGER.setContextSwitchLogFilename(name); return MANAGER.setContextSwitchLogFilename(name);
@ -97,6 +92,7 @@ extern "C" {
{ {
return MANAGER.getContextSwitchLogFilename(); return MANAGER.getContextSwitchLogFilename();
} }
#endif
} }
@ -124,6 +120,8 @@ BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, block_id_t _id, bool _enab
: BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color) : BaseBlockDescriptor(_id, _enabled, _line, _block_type, _color)
, m_name(_name) , m_name(_name)
, m_filename(_filename) , m_filename(_filename)
, m_pEnable(nullptr)
, m_expired(false)
{ {
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
} }
@ -132,15 +130,12 @@ BlockDescriptor::BlockDescriptor(uint64_t& _used_mem, bool _enabled, const char*
: BaseBlockDescriptor(0, _enabled, _line, _block_type, _color) : BaseBlockDescriptor(0, _enabled, _line, _block_type, _color)
, m_name(_name) , m_name(_name)
, m_filename(_filename) , m_filename(_filename)
, m_pEnable(nullptr)
, m_expired(false)
{ {
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2; _used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
} }
void BlockDescriptor::setId(block_id_t _id)
{
m_id = _id;
}
BlockDescRef::~BlockDescRef() BlockDescRef::~BlockDescRef()
{ {
MANAGER.markExpired(m_desc.id()); MANAGER.markExpired(m_desc.id());
@ -151,7 +146,7 @@ BlockDescRef::~BlockDescRef()
void ThreadStorage::storeBlock(const profiler::Block& block) void ThreadStorage::storeBlock(const profiler::Block& block)
{ {
#if EASY_MEASURE_STORAGE_EXPAND != 0 #if EASY_MEASURE_STORAGE_EXPAND != 0
static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White); 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 #endif
auto name_length = static_cast<uint16_t>(strlen(block.name())); auto name_length = static_cast<uint16_t>(strlen(block.name()));
@ -205,7 +200,8 @@ EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
ProfileManager::ProfileManager() ProfileManager::ProfileManager()
{ {
m_expiredDescriptors.reserve(1024U); m_isEnabled = ATOMIC_VAR_INIT(false);
m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_EVENT_TRACING_ENABLED);
} }
ProfileManager::~ProfileManager() ProfileManager::~ProfileManager()
@ -213,6 +209,7 @@ ProfileManager::~ProfileManager()
//dumpBlocksToFile("test.prof"); //dumpBlocksToFile("test.prof");
for (auto desc : m_descriptors) for (auto desc : m_descriptors)
{ {
if (desc != nullptr)
delete desc; delete desc;
} }
} }
@ -231,7 +228,7 @@ void ProfileManager::markExpired(profiler::block_id_t _id)
// We can not delete this descriptor now, because we need to send/write all collected data first. // We can not delete this descriptor now, because we need to send/write all collected data first.
guard_lock_t lock(m_storedSpin); guard_lock_t lock(m_storedSpin);
m_expiredDescriptors.push_back(_id); m_descriptors[_id]->m_expired = true;
} }
ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id) ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id)
@ -240,9 +237,8 @@ ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _thread_id)
return m_threads[_thread_id]; return m_threads[_thread_id];
} }
ThreadStorage* ProfileManager::findThreadStorage(profiler::thread_id_t _thread_id) ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_id)
{ {
guard_lock_t lock(m_spin);
auto it = m_threads.find(_thread_id); auto it = m_threads.find(_thread_id);
return it != m_threads.end() ? &it->second : nullptr; return it != m_threads.end() ? &it->second : nullptr;
} }
@ -272,19 +268,21 @@ void ProfileManager::beginBlock(Block& _block)
THREAD_STORAGE->blocks.openedList.emplace(_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) if (ts != nullptr)
ts->sync.openedList.emplace(_time, _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) if (ts != nullptr)
{ {
profiler::Block b(_time, _id, ""); profiler::Block b(_time, _target_thread_id, "");
b.finish(_time); b.finish(_time);
ts->storeCSwitch(b); ts->storeCSwitch(b);
} }
@ -309,9 +307,9 @@ void ProfileManager::endBlock()
THREAD_STORAGE->blocks.openedList.pop(); 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()) if (ts == nullptr || ts->sync.openedList.empty())
return; return;
@ -325,6 +323,15 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler
void ProfileManager::setEnabled(bool isEnable) void ProfileManager::setEnabled(bool isEnable)
{ {
m_isEnabled = isEnable; m_isEnabled = isEnable;
#ifdef _WIN32
if (isEnable) {
if (m_isEventTracingEnabled)
EasyEventTracer::instance().enable(true);
} else {
EasyEventTracer::instance().disable();
}
#endif
} }
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -332,29 +339,37 @@ void ProfileManager::setEnabled(bool isEnable)
uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
{ {
const bool wasEnabled = m_isEnabled; const bool wasEnabled = m_isEnabled;
const bool eventTracingEnabled = m_isEventTracingEnabled;
if (wasEnabled) if (wasEnabled)
::profiler::setEnabled(false); ::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 #ifndef _WIN32
if (eventTracingEnabled)
{
// Read thread context switch events from temporary file // Read thread context switch events from temporary file
uint64_t timestamp; uint64_t timestamp = 0;
uint32_t thread_from, thread_to; uint32_t thread_from = 0, thread_to = 0;
std::ifstream infile(m_csInfoFilename.c_str()); std::ifstream infile(m_csInfoFilename.c_str());
if(infile.is_open()) {
if(infile.is_open()) while (infile >> timestamp >> thread_from >> thread_to) {
{ beginContextSwitch(thread_from, timestamp, thread_to, "", false);
static const auto& desc = addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, profiler::BLOCK_TYPE_CONTEXT_SWITCH, profiler::colors::White); endContextSwitch(thread_to, timestamp, false);
while (infile >> timestamp >> thread_from >> thread_to) }
{
beginContextSwitch(thread_from, timestamp, desc.id());
endContextSwitch(thread_to, timestamp);
} }
} }
#endif #endif
// Calculate used memory total size and total blocks number // Calculate used memory total size and total blocks number
uint64_t usedMemorySize = 0; uint64_t usedMemorySize = 0;
uint32_t blocks_number = 0; uint32_t blocks_number = 0;
@ -381,6 +396,9 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
// Write block descriptors // Write block descriptors
for (const auto descriptor : m_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 name_size = static_cast<uint16_t>(strlen(descriptor->name()) + 1);
const auto filename_size = static_cast<uint16_t>(strlen(descriptor->file()) + 1); const auto filename_size = static_cast<uint16_t>(strlen(descriptor->file()) + 1);
const auto size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); const auto size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size);
@ -399,27 +417,30 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
_outputStream.write(thread_storage.first); _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()); _outputStream.write(t.sync.closedList.size());
t.sync.closedList.serialize(_outputStream); t.sync.closedList.serialize(_outputStream);
_outputStream.write(t.blocks.closedList.size()); _outputStream.write(t.blocks.closedList.size());
if (!t.blocks.closedList.empty())
t.blocks.closedList.serialize(_outputStream); t.blocks.closedList.serialize(_outputStream);
t.clearClosed(); t.clearClosed();
t.blocks.openedList.clear();
t.sync.openedList.clear();
} }
if (!m_expiredDescriptors.empty())
{
// Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution) // 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)
std::sort(m_expiredDescriptors.begin(), m_expiredDescriptors.end());
for (auto it = m_expiredDescriptors.rbegin(), rend = m_expiredDescriptors.rend(); it != rend; ++it)
{ {
auto id = *it; if (desc == nullptr || !desc->m_expired)
delete m_descriptors[id]; continue;
m_descriptors.erase(m_descriptors.begin() + id);
} delete desc;
m_expiredDescriptors.clear(); desc = nullptr;
} }
//if (wasEnabled) //if (wasEnabled)
@ -439,7 +460,7 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
return blocksNumber; return blocksNumber;
} }
const char* ProfileManager::setThreadName(const char* name, const char* filename, const char* _funcname, int line) const char* ProfileManager::registerThread(const char* name)
{ {
if (THREAD_STORAGE == nullptr) if (THREAD_STORAGE == nullptr)
{ {
@ -448,18 +469,31 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename
if (!THREAD_STORAGE->named) if (!THREAD_STORAGE->named)
{ {
const auto& desc = addBlockDescriptor(true, _funcname, filename, line, profiler::BLOCK_TYPE_THREAD_SIGN, profiler::colors::Black);
profiler::Block b(desc, name);
b.finish(b.begin());
THREAD_STORAGE->storeBlock(b);
THREAD_STORAGE->name = name;
THREAD_STORAGE->named = true; THREAD_STORAGE->named = true;
THREAD_STORAGE->name = name;
} }
return THREAD_STORAGE->name.c_str(); 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
{
blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode());
m_blocksEnableStatus[key] = _enabled;
}
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////

View File

@ -22,12 +22,14 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
#include "profiler/profiler.h" #include "profiler/profiler.h"
#include "spin_lock.h" #include "spin_lock.h"
#include "outstream.h" #include "outstream.h"
//#include "hashed_cstr.h" #include "hashed_cstr.h"
#include <map> #include <map>
#include <list> #include <list>
#include <vector> #include <vector>
#include <unordered_map>
#include <string> #include <string>
#include <functional> #include <functional>
#include <atomic>
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -51,7 +53,17 @@ inline uint32_t getCurrentThreadId()
#endif #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);
}
};
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -149,7 +161,7 @@ public:
*(uint16_t*)data = n; *(uint16_t*)data = n;
data = data + sizeof(uint16_t); data = data + sizeof(uint16_t);
if (m_shift < N) if (m_shift + 1 < N)
*(uint16_t*)(data + n) = 0; *(uint16_t*)(data + n) = 0;
return data; return data;
@ -176,6 +188,11 @@ public:
return m_size; return m_size;
} }
inline bool empty() const
{
return m_size == 0;
}
void clear() void clear()
{ {
m_size = 0; m_size = 0;
@ -201,7 +218,7 @@ public:
_outputStream.write((const char*)data, size); _outputStream.write((const char*)data, size);
data = data + size; data = data + size;
i += size; i += size;
} while (i < N && *(uint16_t*)data != 0); } while (i + 1 < N && *(uint16_t*)data != 0);
current = current->prev; current = current->prev;
} while (current != nullptr); } while (current != nullptr);
@ -295,20 +312,24 @@ class ProfileManager final
typedef profiler::guard_lock<profiler::spin_lock> guard_lock_t; typedef profiler::guard_lock<profiler::spin_lock> guard_lock_t;
typedef std::map<profiler::thread_id_t, ThreadStorage> map_of_threads_stacks; typedef std::map<profiler::thread_id_t, ThreadStorage> map_of_threads_stacks;
typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t; typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t;
typedef std::vector<profiler::block_id_t> expired_ids_t; typedef std::unordered_map<profiler::hashed_cstr, bool> blocks_enable_status_t;
map_of_threads_stacks m_threads; map_of_threads_stacks m_threads;
block_descriptors_t m_descriptors; block_descriptors_t m_descriptors;
expired_ids_t m_expiredDescriptors; blocks_enable_status_t m_blocksEnableStatus;
uint64_t m_usedMemorySize = 0; uint64_t m_usedMemorySize = 0;
profiler::spin_lock m_spin; profiler::spin_lock m_spin;
profiler::spin_lock m_storedSpin; profiler::spin_lock m_storedSpin;
profiler::block_id_t m_idCounter = 0; profiler::block_id_t m_idCounter = 0;
bool m_isEnabled = false; std::atomic_bool m_isEnabled;
std::atomic_bool m_isEventTracingEnabled;
#ifndef _WIN32
std::string m_csInfoFilename = "/tmp/cs_profiling_info.log"; std::string m_csInfoFilename = "/tmp/cs_profiling_info.log";
#endif
uint32_t dumpBlocksToStream(profiler::OStream& _outputStream); uint32_t dumpBlocksToStream(profiler::OStream& _outputStream);
void setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled);
public: public:
@ -316,14 +337,26 @@ public:
~ProfileManager(); ~ProfileManager();
template <class ... TArgs> template <class ... TArgs>
const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, TArgs ... _args) const profiler::BaseBlockDescriptor& addBlockDescriptor(bool _enabledByDefault, const char* _autogenUniqueId, TArgs ... _args)
{ {
auto desc = new profiler::BlockDescriptor(m_usedMemorySize, _enabledByDefault, _args...); auto desc = new profiler::BlockDescriptor(m_usedMemorySize, _enabledByDefault, _args...);
guard_lock_t lock(m_storedSpin); guard_lock_t lock(m_storedSpin);
desc->setId(m_idCounter++); desc->m_id = m_idCounter++;
m_descriptors.emplace_back(desc); 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; return *desc;
} }
@ -332,8 +365,9 @@ public:
void endBlock(); void endBlock();
void setEnabled(bool isEnable); void setEnabled(bool isEnable);
uint32_t dumpBlocksToFile(const char* filename); 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) void setContextSwitchLogFilename(const char* name)
{ {
m_csInfoFilename = name; m_csInfoFilename = name;
@ -343,16 +377,23 @@ public:
{ {
return m_csInfoFilename.c_str(); return m_csInfoFilename.c_str();
} }
#endif
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::block_id_t _id); 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::block_id_t _id); 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); void endContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _endtime, bool _lockSpin = true);
private: private:
void markExpired(profiler::block_id_t _id); void markExpired(profiler::block_id_t _id);
ThreadStorage& threadStorage(profiler::thread_id_t _thread_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);
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
return _findThreadStorage(_thread_id);
}
}; };
#endif // EASY_PROFILER____MANAGER____H______ #endif // EASY_PROFILER____MANAGER____H______

View File

@ -52,14 +52,6 @@
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
struct passthrough_hash {
template <class T> inline size_t operator () (T _value) const {
return static_cast<size_t>(_value);
}
};
//////////////////////////////////////////////////////////////////////////
namespace profiler { namespace profiler {
void SerializedData::set(char* _data) void SerializedData::set(char* _data)
@ -90,7 +82,7 @@ namespace profiler {
#ifdef _WIN32 #ifdef _WIN32
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::profiler::passthrough_hash> StatsMap;
/** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map, /** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map,
which uses it as a key, exists only inside fillTreesFromFile function. */ which uses it as a key, exists only inside fillTreesFromFile function. */
@ -99,7 +91,7 @@ typedef ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t> Id
#else #else
// TODO: Create optimized version of profiler::hashed_cstr for Linux too. // TODO: Create optimized version of profiler::hashed_cstr for Linux too.
typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, passthrough_hash> StatsMap; typedef ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::profiler::passthrough_hash> StatsMap;
typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap; typedef ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t> IdMap;
#endif #endif
@ -237,10 +229,12 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
progress.store(static_cast<int>(10 * i / descriptors_memory_size)); progress.store(static_cast<int>(10 * i / descriptors_memory_size));
} }
typedef ::std::map<::profiler::thread_id_t, StatsMap> PerThreadStats; typedef ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::profiler::passthrough_hash> PerThreadStats;
PerThreadStats thread_statistics, parent_statistics, frame_statistics; PerThreadStats thread_statistics, parent_statistics, frame_statistics;
IdMap identification_table; IdMap identification_table;
::std::vector<char> name;
i = 0; i = 0;
uint32_t read_number = 0; uint32_t read_number = 0;
::profiler::block_index_t blocks_counter = 0; ::profiler::block_index_t blocks_counter = 0;
@ -254,6 +248,15 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
auto& root = threaded_trees[thread_id]; auto& root = threaded_trees[thread_id];
uint16_t name_size = 0;
inFile.read((char*)&name_size, sizeof(uint16_t));
if (name_size != 0)
{
name.resize(name_size);
inFile.read(name.data(), name_size);
root.thread_name = name.data();
}
uint32_t blocks_number_in_thread = 0; uint32_t blocks_number_in_thread = 0;
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
auto threshold = read_number + blocks_number_in_thread; auto threshold = read_number + blocks_number_in_thread;
@ -290,10 +293,6 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
tree.node = baseData; tree.node = baseData;
const auto block_index = blocks_counter++; const auto block_index = blocks_counter++;
auto descriptor = descriptors[baseData->id()];
if (descriptor->type() != ::profiler::BLOCK_TYPE_CONTEXT_SWITCH)
continue;
root.sync.emplace_back(block_index); root.sync.emplace_back(block_index);
if (progress.load() < 0) if (progress.load() < 0)
@ -338,18 +337,13 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
blocks.emplace_back(); blocks.emplace_back();
::profiler::BlocksTree& tree = blocks.back(); ::profiler::BlocksTree& tree = blocks.back();
tree.node = baseData;// new ::profiler::SerializedBlock(sz, data); tree.node = baseData;
const auto block_index = blocks_counter++; const auto block_index = blocks_counter++;
auto& per_parent_statistics = parent_statistics[thread_id]; auto& per_parent_statistics = parent_statistics[thread_id];
auto& per_thread_statistics = thread_statistics[thread_id]; auto& per_thread_statistics = thread_statistics[thread_id];
auto descriptor = descriptors[baseData->id()]; auto descriptor = descriptors[baseData->id()];
if (descriptor->type() == ::profiler::BLOCK_TYPE_THREAD_SIGN)
{
root.thread_name = tree.node->name();
}
if (*tree.node->name() != 0) if (*tree.node->name() != 0)
{ {
// If block has runtime name then generate new id for such block. // If block has runtime name then generate new id for such block.