mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-27 08:41:02 +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:
parent
f490641165
commit
1b33ad788d
@ -29,7 +29,7 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef FULL_DISABLE_PROFILER
|
||||
|
||||
/**
|
||||
\defgroup profiler Profiler
|
||||
\defgroup profiler EasyProfiler
|
||||
*/
|
||||
|
||||
/** 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
|
||||
|
||||
/** 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
|
||||
#include "profiler/profiler.h"
|
||||
@ -80,11 +89,11 @@ Block will be automatically completed by destructor.
|
||||
*/
|
||||
# define EASY_BLOCK(name, ...)\
|
||||
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::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"
|
||||
@ -110,11 +119,11 @@ Name of the block automatically created with function name.
|
||||
*/
|
||||
# define EASY_FUNCTION(...)\
|
||||
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::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"
|
||||
@ -139,7 +148,7 @@ int foo()
|
||||
*/
|
||||
# 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.
|
||||
|
||||
@ -150,44 +159,61 @@ will end previously opened EASY_BLOCK or EASY_FUNCTION.
|
||||
*/
|
||||
# define EASY_EVENT(name, ...)\
|
||||
static const ::profiler::BlockDescRef EASY_UNIQUE_DESC(__LINE__)(\
|
||||
::profiler::registerDescription(::profiler::extract_enable_flag(__VA_ARGS__), EASY_COMPILETIME_NAME(name), __FILE__, __LINE__,\
|
||||
::profiler::BLOCK_TYPE_EVENT, ::profiler::extract_color(__VA_ARGS__)));\
|
||||
::profiler::storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name));
|
||||
::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
|
||||
/** Macro for enabling profiler.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# define EASY_PROFILER_ENABLE ::profiler::setEnabled(true);
|
||||
|
||||
/** Macro disabling profiler
|
||||
/** Macro for disabling profiler.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
# 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
|
||||
*/
|
||||
# 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")
|
||||
|
||||
# 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_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
|
||||
@ -196,6 +222,12 @@ This is only for user comfort. There is no difference for EasyProfiler GUI betwe
|
||||
# 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
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -216,9 +248,7 @@ namespace profiler {
|
||||
enum BlockType : uint8_t
|
||||
{
|
||||
BLOCK_TYPE_EVENT = 0,
|
||||
BLOCK_TYPE_THREAD_SIGN,
|
||||
BLOCK_TYPE_BLOCK,
|
||||
BLOCK_TYPE_CONTEXT_SWITCH,
|
||||
|
||||
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_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);
|
||||
void setId(block_id_t _id);
|
||||
|
||||
public:
|
||||
|
||||
@ -336,6 +367,11 @@ namespace profiler {
|
||||
|
||||
inline const char* name() const { return m_name; }
|
||||
|
||||
private:
|
||||
|
||||
Block(const Block&) = delete;
|
||||
Block& operator = (const Block&) = delete;
|
||||
|
||||
}; // END of class Block.
|
||||
|
||||
//***********************************************
|
||||
@ -360,17 +396,80 @@ namespace profiler {
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Core API
|
||||
// Note: it is better to use macros defined above than a direct calls to API.
|
||||
|
||||
extern "C" {
|
||||
PROFILER_API const BaseBlockDescriptor& registerDescription(bool _enabled, const char* _compiletimeName, const char* _filename, int _line, block_type_t _block_type, color_t _color);
|
||||
PROFILER_API void storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName);
|
||||
|
||||
/** 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);
|
||||
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);
|
||||
|
||||
/** Returns current path to the temporary log-file for Unix event tracing system.
|
||||
|
||||
\ingroup profiler
|
||||
*/
|
||||
PROFILER_API const char* getContextSwitchLogFilename();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
inline void setEnabled(::profiler::EasyEnableFlag _isEnable) {
|
||||
|
@ -86,6 +86,12 @@ namespace profiler {
|
||||
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>
|
||||
|
||||
# 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)
|
||||
@ -165,7 +172,7 @@ 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_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference<decltype(name)>::value>::runtime_name(name)
|
||||
|
||||
|
@ -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"
|
||||
@ -175,7 +176,7 @@ namespace profiler {
|
||||
|
||||
BlocksTree::children_t children;
|
||||
BlocksTree::children_t sync;
|
||||
const char* thread_name;
|
||||
std::string thread_name;
|
||||
::profiler::thread_id_t thread_id;
|
||||
uint16_t depth;
|
||||
|
||||
@ -186,7 +187,7 @@ namespace profiler {
|
||||
BlocksTreeRoot(This&& that)
|
||||
: children(::std::move(that.children))
|
||||
, sync(::std::move(that.sync))
|
||||
, thread_name(that.thread_name)
|
||||
, thread_name(::std::move(that.thread_name))
|
||||
, thread_id(that.thread_id)
|
||||
, depth(that.depth)
|
||||
{
|
||||
@ -196,12 +197,24 @@ namespace profiler {
|
||||
{
|
||||
children = ::std::move(that.children);
|
||||
sync = ::std::move(that.sync);
|
||||
thread_name = that.thread_name;
|
||||
thread_name = ::std::move(that.thread_name);
|
||||
thread_id = that.thread_id;
|
||||
depth = that.depth;
|
||||
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
|
||||
{
|
||||
return thread_id < other.thread_id;
|
||||
@ -215,7 +228,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;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -39,6 +39,9 @@
|
||||
************************************************************************/
|
||||
|
||||
#include <QGraphicsScene>
|
||||
#include <QGraphicsProxyWidget>
|
||||
#include <QFormLayout>
|
||||
#include <QLabel>
|
||||
#include <QWheelEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
@ -93,7 +96,11 @@ inline QRgb selectedItemBorderColor(::profiler::color_t _color)
|
||||
|
||||
//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)
|
||||
{
|
||||
@ -143,7 +150,7 @@ inline T logn(T _value)
|
||||
|
||||
EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root)
|
||||
: 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_index(_index)
|
||||
{
|
||||
@ -171,13 +178,17 @@ QRectF EasyGraphicsItem::boundingRect() const
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const auto sceneView = view();
|
||||
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 offset = sceneView->offset();
|
||||
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
|
||||
if (gotItems)
|
||||
{
|
||||
auto& level0 = m_levels.front();
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@ -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)>();
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
// Iterate through layers and draw visible items
|
||||
bool selectedItemsWasPainted = false;
|
||||
const auto visibleBottom = visibleSceneRect.bottom() - 1;
|
||||
for (uint8_t l = 0; l < levelsNumber; ++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& itemDesc = easyDescriptor(itemBlock.tree.node->id());
|
||||
|
||||
int h = 0, flags = 0;
|
||||
if (w < 20 || !itemBlock.expanded)
|
||||
{
|
||||
@ -323,11 +324,11 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
if (item.block == EASY_GLOBALS.selected_block)
|
||||
selectedItemsWasPainted = true;
|
||||
|
||||
const bool colorChange = (previousColor != item.color);
|
||||
const bool colorChange = (previousColor != itemDesc.color());
|
||||
if (colorChange)
|
||||
{
|
||||
// Set background color brush for rectangle
|
||||
previousColor = item.color;
|
||||
previousColor = itemDesc.color();
|
||||
inverseColor = 0xffffffff - previousColor;
|
||||
textColor = ::profiler_gui::textColorForRgb(previousColor);
|
||||
brush.setColor(previousColor);
|
||||
@ -374,11 +375,11 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
if (item.block == EASY_GLOBALS.selected_block)
|
||||
selectedItemsWasPainted = true;
|
||||
|
||||
const bool colorChange = (previousColor != item.color);
|
||||
const bool colorChange = (previousColor != itemDesc.color());
|
||||
if (colorChange)
|
||||
{
|
||||
// Set background color brush for rectangle
|
||||
previousColor = item.color;
|
||||
previousColor = itemDesc.color();
|
||||
inverseColor = 0xffffffff - previousColor;
|
||||
textColor = ::profiler_gui::textColorForRgb(previousColor);
|
||||
brush.setColor(previousColor);
|
||||
@ -435,7 +436,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
_painter->setFont(SELECTED_ITEM_FONT);
|
||||
|
||||
// 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));
|
||||
|
||||
// restore previous pen color
|
||||
@ -469,15 +470,18 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
if (dh > 0)
|
||||
h -= dh;
|
||||
|
||||
const auto& itemBlock = easyBlock(item.block);
|
||||
const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id());
|
||||
|
||||
QPen pen(Qt::SolidLine);
|
||||
pen.setJoinStyle(Qt::MiterJoin);
|
||||
pen.setColor(selectedItemBorderColor(item.color));//Qt::red);
|
||||
pen.setColor(selectedItemBorderColor(itemDesc.color()));//Qt::red);
|
||||
pen.setWidth(3);
|
||||
_painter->setPen(pen);
|
||||
|
||||
if (!selectedItemsWasPainted)
|
||||
{
|
||||
brush.setColor(item.color);// SELECTED_ITEM_COLOR);
|
||||
brush.setColor(itemDesc.color());// SELECTED_ITEM_COLOR);
|
||||
_painter->setBrush(brush);
|
||||
}
|
||||
else
|
||||
@ -512,13 +516,12 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem*
|
||||
// text will be painted with inverse color
|
||||
//auto textColor = 0x00ffffff - previousColor;
|
||||
//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->setFont(SELECTED_ITEM_FONT);
|
||||
|
||||
// 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();
|
||||
_painter->drawText(rect, Qt::AlignCenter, ::profiler_gui::toUnicode(name));
|
||||
// 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));
|
||||
_painter->setPen(QColor::fromRgb(::profiler::colors::Grey800));// 0x00505050));
|
||||
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();
|
||||
|
||||
previousColor = 0;
|
||||
qreal prevRight = -1e100, top = y() - 4, h = 3;
|
||||
if (top + h < visibleBottom)
|
||||
{
|
||||
for (auto it = firstSync, end = m_pRoot->sync.end(); it != end; ++it)
|
||||
{
|
||||
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.
|
||||
|
||||
decltype(begin) width = sceneView->time2position(item.node->end()) - begin;
|
||||
auto r = begin + width;
|
||||
if (r < sceneLeft) // This item is not visible
|
||||
decltype(left) width = sceneView->time2position(item.node->end()) - left;
|
||||
if (left + width < sceneLeft) // This item is not visible
|
||||
continue;
|
||||
|
||||
begin *= currentScale;
|
||||
begin -= dx;
|
||||
left *= currentScale;
|
||||
left -= dx;
|
||||
width *= currentScale;
|
||||
r = begin + width;
|
||||
if (r <= prevRight) // This item is not visible
|
||||
if (left + width <= prevRight) // This item is not visible
|
||||
continue;
|
||||
|
||||
if (begin < prevRight)
|
||||
if (left < prevRight)
|
||||
{
|
||||
width -= prevRight - begin;
|
||||
begin = prevRight;
|
||||
width -= prevRight - left;
|
||||
left = prevRight;
|
||||
}
|
||||
|
||||
if (width < 2)
|
||||
width = 2;
|
||||
|
||||
//_painter->drawLine(QLineF(::std::max(begin, prevRight), top, begin + width, top));
|
||||
rect.setRect(begin, top, width, h);
|
||||
const bool self_thread = item.node->id() != 0 && EASY_GLOBALS.profiler_blocks.find(item.node->id()) != EASY_GLOBALS.profiler_blocks.end();
|
||||
::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);
|
||||
prevRight = begin + width;
|
||||
prevRight = left + width;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -770,6 +805,56 @@ const ::profiler_gui::EasyBlockItem* EasyGraphicsItem::intersect(const QPointF&
|
||||
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)
|
||||
@ -1223,12 +1308,16 @@ EasyGraphicsView::EasyGraphicsView(QWidget* _parent)
|
||||
, m_scale(1)
|
||||
, m_offset(0)
|
||||
, m_timelineStep(0)
|
||||
, m_idleTime(0)
|
||||
, m_mouseButtons(Qt::NoButton)
|
||||
, m_pScrollbar(nullptr)
|
||||
, m_chronometerItem(nullptr)
|
||||
, m_chronometerItemAux(nullptr)
|
||||
, m_csInfoWidget(nullptr)
|
||||
, m_flickerSpeedX(0)
|
||||
, m_flickerSpeedY(0)
|
||||
, m_flickerCounterX(0)
|
||||
, m_flickerCounterY(0)
|
||||
, m_bDoubleClick(false)
|
||||
, m_bUpdatingRect(false)
|
||||
, m_bEmpty(true)
|
||||
@ -1265,6 +1354,8 @@ void EasyGraphicsView::clearSilent()
|
||||
m_flickerTimer.stop();
|
||||
m_flickerSpeedX = 0;
|
||||
m_flickerSpeedY = 0;
|
||||
m_flickerCounterX = 0;
|
||||
m_flickerCounterY = 0;
|
||||
|
||||
// Clear all items
|
||||
scene()->clear();
|
||||
@ -1276,6 +1367,10 @@ void EasyGraphicsView::clearSilent()
|
||||
m_timelineStep = 1;
|
||||
m_offset = 0; // scroll back to the beginning of the scene
|
||||
|
||||
m_idleTimer.stop();
|
||||
m_idleTime = 0;
|
||||
m_csInfoWidget = nullptr;
|
||||
|
||||
// Reset necessary flags
|
||||
//m_bTest = false;
|
||||
m_bEmpty = true;
|
||||
@ -1307,28 +1402,30 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
|
||||
{
|
||||
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())
|
||||
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)
|
||||
{
|
||||
m_beginTime = timestart;
|
||||
}
|
||||
|
||||
if (finish < timefinish)
|
||||
{
|
||||
if (finish < timefinish) {
|
||||
finish = timefinish;
|
||||
longestTree = threadTree.first;
|
||||
}
|
||||
|
||||
if (mainTree == 0 && !strcmp(t.thread_name, "Main"))
|
||||
{
|
||||
if (mainTree == 0 && !strcmp(t.name(), "Main"))
|
||||
mainTree = threadTree.first;
|
||||
}
|
||||
}
|
||||
|
||||
// Filling scene with items
|
||||
m_items.reserve(_blocksTree.size());
|
||||
@ -1345,13 +1442,29 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
|
||||
const auto& t = threadTree.second;
|
||||
|
||||
// fill scene with new items
|
||||
const auto& tree = t.children;
|
||||
qreal h = 0, x = time2position(blocksTree(tree.front()).node->begin());
|
||||
qreal h = 0, x = 0;
|
||||
|
||||
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);
|
||||
if (t.depth)
|
||||
item->setLevels(t.depth);
|
||||
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);
|
||||
m_items.push_back(item);
|
||||
@ -1369,8 +1482,6 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
|
||||
// Calculating scene rect
|
||||
const qreal endX = time2position(finish) + 1500.0;
|
||||
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
|
||||
updateVisibleSceneRect();
|
||||
@ -1387,7 +1498,6 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
|
||||
scene()->addItem(indicator);
|
||||
|
||||
// Setting flags
|
||||
//m_bTest = false;
|
||||
m_bEmpty = false;
|
||||
|
||||
scaleTo(BASE_SCALE);
|
||||
@ -1396,7 +1506,9 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
|
||||
emit treeChanged();
|
||||
|
||||
if (mainThreadItem != nullptr)
|
||||
{
|
||||
longestItem = mainThreadItem;
|
||||
}
|
||||
|
||||
if (longestItem != nullptr)
|
||||
{
|
||||
@ -1404,6 +1516,8 @@ void EasyGraphicsView::setTree(const ::profiler::thread_blocks_tree_t& _blocksTr
|
||||
EASY_GLOBALS.selected_thread = longestItem->threadId();
|
||||
emit EASY_GLOBALS.events.selectedThreadChanged(longestItem->threadId());
|
||||
}
|
||||
|
||||
m_idleTimer.start(IDLE_TIMER_INTERVAL);
|
||||
}
|
||||
|
||||
const EasyGraphicsView::Items &EasyGraphicsView::getItems() const
|
||||
@ -1492,7 +1606,6 @@ qreal EasyGraphicsView::setTree(EasyGraphicsItem* _item, const ::profiler::Block
|
||||
}
|
||||
|
||||
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.totalHeight = GRAPHICS_ROW_SIZE + h;
|
||||
b.state = BLOCK_ITEM_UNCHANGED;
|
||||
@ -1600,6 +1713,8 @@ void EasyGraphicsView::scaleTo(qreal _scale)
|
||||
|
||||
void EasyGraphicsView::wheelEvent(QWheelEvent* _event)
|
||||
{
|
||||
m_idleTime = 0;
|
||||
|
||||
if (!m_bEmpty)
|
||||
onWheel(mapToScene(_event->pos()).x(), _event->delta());
|
||||
_event->accept();
|
||||
@ -1607,6 +1722,8 @@ void EasyGraphicsView::wheelEvent(QWheelEvent* _event)
|
||||
|
||||
void EasyGraphicsView::onGraphicsScrollbarWheel(qreal _mouseX, int _wheelDelta)
|
||||
{
|
||||
m_idleTime = 0;
|
||||
|
||||
for (auto item : m_items)
|
||||
{
|
||||
if (item->threadId() == EASY_GLOBALS.selected_thread)
|
||||
@ -1657,6 +1774,8 @@ void EasyGraphicsView::onWheel(qreal _mouseX, int _wheelDelta)
|
||||
|
||||
void EasyGraphicsView::mousePressEvent(QMouseEvent* _event)
|
||||
{
|
||||
m_idleTime = 0;
|
||||
|
||||
if (m_bEmpty)
|
||||
{
|
||||
_event->accept();
|
||||
@ -1680,6 +1799,8 @@ void EasyGraphicsView::mousePressEvent(QMouseEvent* _event)
|
||||
|
||||
void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event)
|
||||
{
|
||||
m_idleTime = 0;
|
||||
|
||||
if (m_bEmpty)
|
||||
{
|
||||
_event->accept();
|
||||
@ -1706,6 +1827,8 @@ void EasyGraphicsView::mouseDoubleClickEvent(QMouseEvent* _event)
|
||||
|
||||
void EasyGraphicsView::mouseReleaseEvent(QMouseEvent* _event)
|
||||
{
|
||||
m_idleTime = 0;
|
||||
|
||||
if (m_bEmpty)
|
||||
{
|
||||
_event->accept();
|
||||
@ -1867,6 +1990,8 @@ bool EasyGraphicsView::moveChrono(EasyChronometerItem* _chronometerItem, qreal _
|
||||
|
||||
void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event)
|
||||
{
|
||||
m_idleTime = 0;
|
||||
|
||||
if (m_bEmpty || (m_mouseButtons == 0 && !m_chronometerItem->isVisible()))
|
||||
{
|
||||
_event->accept();
|
||||
@ -1922,7 +2047,7 @@ void EasyGraphicsView::mouseMoveEvent(QMouseEvent* _event)
|
||||
|
||||
// Update flicker speed
|
||||
m_flickerSpeedX += delta.x() >> 1;
|
||||
m_flickerSpeedY += delta.y() >> 1;
|
||||
m_flickerSpeedY += delta.y();
|
||||
if (!m_flickerTimer.isActive())
|
||||
{
|
||||
// If flicker timer is not started, then start it
|
||||
@ -1955,6 +2080,7 @@ void EasyGraphicsView::keyPressEvent(QKeyEvent* _event)
|
||||
static const int KeyStep = 100;
|
||||
|
||||
const int key = _event->key();
|
||||
m_idleTime = 0;
|
||||
|
||||
switch (key)
|
||||
{
|
||||
@ -2009,6 +2135,7 @@ void EasyGraphicsView::keyPressEvent(QKeyEvent* _event)
|
||||
void EasyGraphicsView::keyReleaseEvent(QKeyEvent* _event)
|
||||
{
|
||||
const int key = _event->key();
|
||||
m_idleTime = 0;
|
||||
|
||||
m_keys.erase(key);
|
||||
_event->accept();
|
||||
@ -2054,6 +2181,7 @@ void EasyGraphicsView::initMode()
|
||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
||||
connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &This::onScrollbarValueChange);
|
||||
connect(&m_flickerTimer, &QTimer::timeout, this, &This::onFlickerTimeout);
|
||||
connect(&m_idleTimer, &QTimer::timeout, this, &This::onIdleTimeout);
|
||||
|
||||
auto globalSignals = &EASY_GLOBALS.events;
|
||||
connect(globalSignals, &::profiler_gui::EasyGlobalSignals::selectedThreadChanged, this, &This::onSelectedThreadChange);
|
||||
@ -2088,6 +2216,9 @@ void EasyGraphicsView::onGraphicsScrollbarValueChange(qreal _value)
|
||||
|
||||
void EasyGraphicsView::onFlickerTimeout()
|
||||
{
|
||||
++m_flickerCounterX;
|
||||
++m_flickerCounterY;
|
||||
|
||||
if (m_mouseButtons & Qt::LeftButton)
|
||||
{
|
||||
// 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
|
||||
repaintScene(); // repaint scene
|
||||
|
||||
m_flickerSpeedX -= absmin(sign(m_flickerSpeedX), m_flickerSpeedX);
|
||||
m_flickerSpeedY -= absmin(sign(m_flickerSpeedY), m_flickerSpeedY);
|
||||
const int dx = static_cast<int>(sign(m_flickerSpeedX) * m_flickerCounterX / FLICKER_FACTOR);
|
||||
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)
|
||||
{
|
||||
// Flicker stopped, no timer needed.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,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:
|
||||
|
||||
@ -272,6 +273,8 @@ EASY_QGRAPHICSITEM(EasyTimelineIndicatorItem);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class QGraphicsProxyWidget;
|
||||
|
||||
class EasyGraphicsView : public QGraphicsView
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -287,19 +290,24 @@ private:
|
||||
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
|
||||
@ -361,6 +369,7 @@ private slots:
|
||||
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();
|
||||
|
@ -138,7 +138,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
|
||||
|
@ -515,9 +515,9 @@ 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)
|
||||
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
|
||||
{
|
||||
|
@ -161,20 +161,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.gotName())
|
||||
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);
|
||||
@ -259,19 +253,13 @@ 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->gotName())
|
||||
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);
|
||||
|
@ -3,7 +3,9 @@
|
||||
#ifdef _WIN32
|
||||
#include <memory.h>
|
||||
#include <chrono>
|
||||
#include <unordered_map>
|
||||
#include "event_trace_win.h"
|
||||
#include "Psapi.h"
|
||||
#include "profiler/profiler.h"
|
||||
#include "profile_manager.h"
|
||||
|
||||
@ -15,6 +17,16 @@
|
||||
|
||||
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
|
||||
@ -34,6 +46,17 @@ namespace profiler {
|
||||
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;
|
||||
@ -43,13 +66,79 @@ namespace profiler {
|
||||
if (sizeof(CSwitch) != _traceEvent->UserDataLength)
|
||||
return;
|
||||
|
||||
//EASY_FUNCTION(::profiler::colors::White);
|
||||
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);
|
||||
|
||||
static const auto& desc = MANAGER.addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, ::profiler::BLOCK_TYPE_CONTEXT_SWITCH, ::profiler::colors::White);
|
||||
MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, desc.id());
|
||||
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 (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);
|
||||
}
|
||||
|
||||
@ -109,6 +198,7 @@ namespace profiler {
|
||||
{
|
||||
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;
|
||||
|
||||
@ -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.)
|
||||
//
|
||||
// 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");
|
||||
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;
|
||||
@ -155,17 +248,23 @@ namespace profiler {
|
||||
|
||||
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 ProcessThread to finish
|
||||
if (m_stubThread.joinable())
|
||||
m_stubThread.join();
|
||||
// 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.
|
||||
|
@ -12,7 +12,8 @@
|
||||
#include <evntrace.h>
|
||||
#include <evntcons.h>
|
||||
#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)
|
||||
|
||||
::std::thread m_stubThread;
|
||||
::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;
|
||||
|
@ -64,7 +64,11 @@ namespace profiler {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.
|
||||
@ -110,11 +124,19 @@ namespace profiler {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -206,6 +228,16 @@ namespace profiler {
|
||||
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.
|
||||
|
@ -47,9 +47,9 @@ extern timestamp_t getCurrentTime();
|
||||
|
||||
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()
|
||||
@ -60,15 +60,9 @@ 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 storeBlock(const BaseBlockDescriptor& _desc, const char* _runtimeName)
|
||||
PROFILER_API void storeEvent(const BaseBlockDescriptor& _desc, const char* _runtimeName)
|
||||
{
|
||||
MANAGER.storeBlock(_desc, _runtimeName);
|
||||
}
|
||||
@ -83,11 +77,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);
|
||||
@ -97,6 +92,7 @@ extern "C" {
|
||||
{
|
||||
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)
|
||||
, m_name(_name)
|
||||
, m_filename(_filename)
|
||||
, m_pEnable(nullptr)
|
||||
, m_expired(false)
|
||||
{
|
||||
_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)
|
||||
, m_name(_name)
|
||||
, m_filename(_filename)
|
||||
, m_pEnable(nullptr)
|
||||
, m_expired(false)
|
||||
{
|
||||
_used_mem += sizeof(profiler::SerializedBlockDescriptor) + strlen(_name) + strlen(_filename) + 2;
|
||||
}
|
||||
|
||||
void BlockDescriptor::setId(block_id_t _id)
|
||||
{
|
||||
m_id = _id;
|
||||
}
|
||||
|
||||
BlockDescRef::~BlockDescRef()
|
||||
{
|
||||
MANAGER.markExpired(m_desc.id());
|
||||
@ -151,7 +146,7 @@ BlockDescRef::~BlockDescRef()
|
||||
void ThreadStorage::storeBlock(const profiler::Block& block)
|
||||
{
|
||||
#if EASY_MEASURE_STORAGE_EXPAND != 0
|
||||
static const auto desc = MANAGER.addBlockDescriptor(EASY_STORAGE_EXPAND_ENABLED, "EasyProfiler.ExpandStorage", __FILE__, __LINE__, profiler::BLOCK_TYPE_BLOCK, profiler::colors::White);
|
||||
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()));
|
||||
@ -205,7 +200,8 @@ EASY_THREAD_LOCAL static ::ThreadStorage* THREAD_STORAGE = nullptr;
|
||||
|
||||
ProfileManager::ProfileManager()
|
||||
{
|
||||
m_expiredDescriptors.reserve(1024U);
|
||||
m_isEnabled = ATOMIC_VAR_INIT(false);
|
||||
m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_EVENT_TRACING_ENABLED);
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager()
|
||||
@ -213,6 +209,7 @@ ProfileManager::~ProfileManager()
|
||||
//dumpBlocksToFile("test.prof");
|
||||
for (auto desc : m_descriptors)
|
||||
{
|
||||
if (desc != nullptr)
|
||||
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.
|
||||
|
||||
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)
|
||||
@ -240,9 +237,8 @@ ThreadStorage& ProfileManager::threadStorage(profiler::thread_id_t _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);
|
||||
return it != m_threads.end() ? &it->second : nullptr;
|
||||
}
|
||||
@ -272,19 +268,21 @@ void ProfileManager::beginBlock(Block& _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, _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 b(_time, _id, "");
|
||||
profiler::Block b(_time, _target_thread_id, "");
|
||||
b.finish(_time);
|
||||
ts->storeCSwitch(b);
|
||||
}
|
||||
@ -309,9 +307,9 @@ void ProfileManager::endBlock()
|
||||
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;
|
||||
|
||||
@ -325,6 +323,15 @@ void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, profiler
|
||||
void ProfileManager::setEnabled(bool 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)
|
||||
{
|
||||
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;
|
||||
uint32_t thread_from, thread_to;
|
||||
uint64_t timestamp = 0;
|
||||
uint32_t thread_from = 0, thread_to = 0;
|
||||
|
||||
std::ifstream infile(m_csInfoFilename.c_str());
|
||||
|
||||
if(infile.is_open())
|
||||
{
|
||||
static const auto& desc = addBlockDescriptor(true, "OS.ContextSwitch", __FILE__, __LINE__, profiler::BLOCK_TYPE_CONTEXT_SWITCH, profiler::colors::White);
|
||||
while (infile >> timestamp >> thread_from >> thread_to)
|
||||
{
|
||||
beginContextSwitch(thread_from, timestamp, desc.id());
|
||||
endContextSwitch(thread_to, timestamp);
|
||||
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;
|
||||
@ -381,6 +396,9 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
|
||||
// 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);
|
||||
@ -399,27 +417,30 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream)
|
||||
|
||||
_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();
|
||||
}
|
||||
|
||||
if (!m_expiredDescriptors.empty())
|
||||
{
|
||||
// Remove all expired block descriptors (descriptor may become expired if it's .dll/.so have been unloaded during application execution)
|
||||
|
||||
std::sort(m_expiredDescriptors.begin(), m_expiredDescriptors.end());
|
||||
for (auto it = m_expiredDescriptors.rbegin(), rend = m_expiredDescriptors.rend(); it != rend; ++it)
|
||||
for (auto& desc : m_descriptors)
|
||||
{
|
||||
auto id = *it;
|
||||
delete m_descriptors[id];
|
||||
m_descriptors.erase(m_descriptors.begin() + id);
|
||||
}
|
||||
m_expiredDescriptors.clear();
|
||||
if (desc == nullptr || !desc->m_expired)
|
||||
continue;
|
||||
|
||||
delete desc;
|
||||
desc = nullptr;
|
||||
}
|
||||
|
||||
//if (wasEnabled)
|
||||
@ -439,7 +460,7 @@ uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
|
||||
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)
|
||||
{
|
||||
@ -448,18 +469,31 @@ const char* ProfileManager::setThreadName(const char* name, const char* filename
|
||||
|
||||
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->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
|
||||
{
|
||||
blocks_enable_status_t::key_type key(_key.c_str(), _key.size(), _key.hcode());
|
||||
m_blocksEnableStatus[key] = _enabled;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -22,12 +22,14 @@ along with this program.If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "profiler/profiler.h"
|
||||
#include "spin_lock.h"
|
||||
#include "outstream.h"
|
||||
//#include "hashed_cstr.h"
|
||||
#include "hashed_cstr.h"
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <atomic>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -51,7 +53,17 @@ 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);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -149,7 +161,7 @@ public:
|
||||
*(uint16_t*)data = n;
|
||||
data = data + sizeof(uint16_t);
|
||||
|
||||
if (m_shift < N)
|
||||
if (m_shift + 1 < N)
|
||||
*(uint16_t*)(data + n) = 0;
|
||||
|
||||
return data;
|
||||
@ -176,6 +188,11 @@ public:
|
||||
return m_size;
|
||||
}
|
||||
|
||||
inline bool empty() const
|
||||
{
|
||||
return m_size == 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_size = 0;
|
||||
@ -201,7 +218,7 @@ public:
|
||||
_outputStream.write((const char*)data, size);
|
||||
data = data + size;
|
||||
i += size;
|
||||
} while (i < N && *(uint16_t*)data != 0);
|
||||
} while (i + 1 < N && *(uint16_t*)data != 0);
|
||||
current = current->prev;
|
||||
} while (current != nullptr);
|
||||
|
||||
@ -295,20 +312,24 @@ class ProfileManager final
|
||||
typedef profiler::guard_lock<profiler::spin_lock> guard_lock_t;
|
||||
typedef std::map<profiler::thread_id_t, ThreadStorage> map_of_threads_stacks;
|
||||
typedef std::vector<profiler::BlockDescriptor*> block_descriptors_t;
|
||||
typedef std::vector<profiler::block_id_t> expired_ids_t;
|
||||
typedef std::unordered_map<profiler::hashed_cstr, bool> blocks_enable_status_t;
|
||||
|
||||
map_of_threads_stacks m_threads;
|
||||
block_descriptors_t m_descriptors;
|
||||
expired_ids_t m_expiredDescriptors;
|
||||
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;
|
||||
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";
|
||||
#endif
|
||||
|
||||
uint32_t dumpBlocksToStream(profiler::OStream& _outputStream);
|
||||
void setBlockEnabled(profiler::block_id_t _id, const profiler::hashed_stdstring& _key, bool _enabled);
|
||||
|
||||
public:
|
||||
|
||||
@ -316,14 +337,26 @@ public:
|
||||
~ProfileManager();
|
||||
|
||||
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...);
|
||||
|
||||
guard_lock_t lock(m_storedSpin);
|
||||
desc->setId(m_idCounter++);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -332,8 +365,9 @@ public:
|
||||
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;
|
||||
@ -343,16 +377,23 @@ 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);
|
||||
|
||||
private:
|
||||
|
||||
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);
|
||||
|
||||
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
|
||||
{
|
||||
guard_lock_t lock(m_spin);
|
||||
return _findThreadStorage(_thread_id);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EASY_PROFILER____MANAGER____H______
|
||||
|
@ -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 {
|
||||
|
||||
void SerializedData::set(char* _data)
|
||||
@ -90,7 +82,7 @@ namespace profiler {
|
||||
|
||||
#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,
|
||||
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
|
||||
|
||||
// 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;
|
||||
|
||||
#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));
|
||||
}
|
||||
|
||||
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;
|
||||
IdMap identification_table;
|
||||
|
||||
::std::vector<char> name;
|
||||
|
||||
i = 0;
|
||||
uint32_t read_number = 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];
|
||||
|
||||
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;
|
||||
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(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;
|
||||
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);
|
||||
|
||||
if (progress.load() < 0)
|
||||
@ -338,18 +337,13 @@ extern "C" ::profiler::block_index_t fillTreesFromFile(::std::atomic<int>& progr
|
||||
|
||||
blocks.emplace_back();
|
||||
::profiler::BlocksTree& tree = blocks.back();
|
||||
tree.node = baseData;// new ::profiler::SerializedBlock(sz, data);
|
||||
tree.node = baseData;
|
||||
const auto block_index = blocks_counter++;
|
||||
|
||||
auto& per_parent_statistics = parent_statistics[thread_id];
|
||||
auto& per_thread_statistics = thread_statistics[thread_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 block has runtime name then generate new id for such block.
|
||||
|
Loading…
x
Reference in New Issue
Block a user