mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-25 23:40:51 +08:00
[ui] added workaround for Qt memory leak on Linux when using multiple threads;
[ui] added median duration into tree stats; [ui] added max rows count for "Call-stack" tree mode;
This commit is contained in:
parent
b4494bcef9
commit
c74744fae4
@ -12,6 +12,13 @@ else ()
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
if (NOT (MSVC_VERSION LESS 1914))
|
||||||
|
# turn on valid __cplusplus macro value for visual studio (available since msvc 2017 update 7)
|
||||||
|
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus")
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
option(EASY_PROFILER_NO_GUI "Build easy_profiler without the GUI application (required Qt)" OFF)
|
option(EASY_PROFILER_NO_GUI "Build easy_profiler without the GUI application (required Qt)" OFF)
|
||||||
|
|
||||||
set(EASY_PROGRAM_VERSION_MAJOR 2)
|
set(EASY_PROGRAM_VERSION_MAJOR 2)
|
||||||
|
@ -62,6 +62,17 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
# if __cplusplus >= 201703L
|
||||||
|
# define EASY_STD 17
|
||||||
|
# elif __cplusplus >= 201402L
|
||||||
|
# define EASY_STD 14
|
||||||
|
# else
|
||||||
|
# define EASY_STD 11
|
||||||
|
# endif
|
||||||
|
#else
|
||||||
|
# define EASY_STD 11
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
@ -92,6 +103,10 @@
|
|||||||
# define EASY_NOEXCEPT throw()
|
# define EASY_NOEXCEPT throw()
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# if EASY_STD > 11 && _MSC_VER >= 1900
|
||||||
|
# define EASY_LAMBDA_MOVE_CAPTURE
|
||||||
|
# endif
|
||||||
|
|
||||||
# define EASY_FORCE_INLINE __forceinline
|
# define EASY_FORCE_INLINE __forceinline
|
||||||
|
|
||||||
#elif defined(__clang__)
|
#elif defined(__clang__)
|
||||||
@ -123,6 +138,10 @@
|
|||||||
# define EASY_FINAL
|
# define EASY_FINAL
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# if EASY_STD > 11 && EASY_COMPILER_VERSION >= 34
|
||||||
|
# define EASY_LAMBDA_MOVE_CAPTURE
|
||||||
|
# endif
|
||||||
|
|
||||||
# define EASY_FORCE_INLINE inline __attribute__((always_inline))
|
# define EASY_FORCE_INLINE inline __attribute__((always_inline))
|
||||||
# undef EASY_COMPILER_VERSION
|
# undef EASY_COMPILER_VERSION
|
||||||
|
|
||||||
@ -160,6 +179,10 @@
|
|||||||
# define EASY_FINAL
|
# define EASY_FINAL
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
# if EASY_STD > 11 && EASY_COMPILER_VERSION >= 49
|
||||||
|
# define EASY_LAMBDA_MOVE_CAPTURE
|
||||||
|
# endif
|
||||||
|
|
||||||
# define EASY_FORCE_INLINE inline __attribute__((always_inline))
|
# define EASY_FORCE_INLINE inline __attribute__((always_inline))
|
||||||
# undef EASY_COMPILER_VERSION
|
# undef EASY_COMPILER_VERSION
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ using async_future = std::future<async_result_t>;
|
|||||||
template <class T>
|
template <class T>
|
||||||
struct Counter
|
struct Counter
|
||||||
{
|
{
|
||||||
T value = 0;
|
T count = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Stats
|
struct Stats
|
||||||
@ -147,7 +147,7 @@ struct Stats
|
|||||||
Stats(profiler::BlockStatistics* stats_ptr, profiler::timestamp_t duration) EASY_NOEXCEPT
|
Stats(profiler::BlockStatistics* stats_ptr, profiler::timestamp_t duration) EASY_NOEXCEPT
|
||||||
: stats(stats_ptr)
|
: stats(stats_ptr)
|
||||||
{
|
{
|
||||||
durations[duration].value = 1;
|
durations[duration].count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stats(Stats&& another) EASY_NOEXCEPT
|
Stats(Stats&& another) EASY_NOEXCEPT
|
||||||
@ -393,7 +393,7 @@ static profiler::BlockStatistics* update_statistics(
|
|||||||
// write pointer to statistics into output (this is BlocksTree:: per_thread_stats or per_parent_stats or per_frame_stats)
|
// write pointer to statistics into output (this is BlocksTree:: per_thread_stats or per_parent_stats or per_frame_stats)
|
||||||
auto stats = it->second.stats;
|
auto stats = it->second.stats;
|
||||||
auto& durations = it->second.durations;
|
auto& durations = it->second.durations;
|
||||||
++durations[duration].value;
|
++durations[duration].count;
|
||||||
|
|
||||||
++stats->calls_number; // update calls number of this block
|
++stats->calls_number; // update calls number of this block
|
||||||
stats->total_duration += duration; // update summary duration of all block calls
|
stats->total_duration += duration; // update summary duration of all block calls
|
||||||
@ -457,7 +457,7 @@ static profiler::BlockStatistics* update_statistics(
|
|||||||
auto stats = it->second.stats;
|
auto stats = it->second.stats;
|
||||||
auto& durations = it->second.durations;
|
auto& durations = it->second.durations;
|
||||||
|
|
||||||
++durations[duration].value;
|
++durations[duration].count;
|
||||||
|
|
||||||
++stats->calls_number; // update calls number of this block
|
++stats->calls_number; // update calls number of this block
|
||||||
stats->total_duration += duration; // update summary duration of all block calls
|
stats->total_duration += duration; // update summary duration of all block calls
|
||||||
@ -515,7 +515,7 @@ static void calculate_medians(TStatsMapIterator begin, TStatsMapIterator end)
|
|||||||
size_t total_count = 0;
|
size_t total_count = 0;
|
||||||
for (auto& kv : durations)
|
for (auto& kv : durations)
|
||||||
{
|
{
|
||||||
total_count += kv.second.value;
|
total_count += kv.second.count;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto stats = it->second.stats;
|
auto stats = it->second.stats;
|
||||||
@ -525,7 +525,7 @@ static void calculate_medians(TStatsMapIterator begin, TStatsMapIterator end)
|
|||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
for (auto& kv : durations)
|
for (auto& kv : durations)
|
||||||
{
|
{
|
||||||
const auto count = kv.second.value;
|
const auto count = kv.second.count;
|
||||||
|
|
||||||
i += count;
|
i += count;
|
||||||
if (i < index)
|
if (i < index)
|
||||||
@ -546,7 +546,7 @@ static void calculate_medians(TStatsMapIterator begin, TStatsMapIterator end)
|
|||||||
bool i1 = false;
|
bool i1 = false;
|
||||||
for (auto& kv : durations)
|
for (auto& kv : durations)
|
||||||
{
|
{
|
||||||
const auto count = kv.second.value;
|
const auto count = kv.second.count;
|
||||||
|
|
||||||
i += count;
|
i += count;
|
||||||
if (i < index1)
|
if (i < index1)
|
||||||
|
@ -72,6 +72,7 @@
|
|||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
|
#include <QMessageBox>
|
||||||
#include <QMoveEvent>
|
#include <QMoveEvent>
|
||||||
#include <QResizeEvent>
|
#include <QResizeEvent>
|
||||||
#include <QScrollBar>
|
#include <QScrollBar>
|
||||||
@ -99,99 +100,87 @@
|
|||||||
const int HIERARCHY_BUILDER_TIMER_INTERVAL = 40;
|
const int HIERARCHY_BUILDER_TIMER_INTERVAL = 40;
|
||||||
|
|
||||||
const bool PLAIN_MODE_COLUMNS[COL_COLUMNS_NUMBER] = {
|
const bool PLAIN_MODE_COLUMNS[COL_COLUMNS_NUMBER] = {
|
||||||
true, //COL_NAME,
|
true // COL_NAME = 0,
|
||||||
true, //COL_BEGIN,
|
, true // COL_BEGIN,
|
||||||
true, //COL_TIME,
|
, true // COL_TIME,
|
||||||
true, //COL_SELF_TIME,
|
, true // COL_SELF_TIME,
|
||||||
|
, true // COL_SELF_TIME_PERCENT,
|
||||||
false, //COL_TOTAL_TIME_PER_PARENT,
|
, true // COL_END,
|
||||||
false, //COL_TOTAL_TIME_PER_FRAME,
|
, true // COL_PERCENT_PER_FRAME,
|
||||||
true, //COL_TOTAL_TIME_PER_THREAD,
|
, false // COL_TOTAL_TIME_PER_FRAME,
|
||||||
|
, false // COL_PERCENT_SUM_PER_FRAME,
|
||||||
true, //COL_SELF_TIME_PERCENT,
|
, true // COL_MIN_PER_FRAME,
|
||||||
|
, true // COL_MAX_PER_FRAME,
|
||||||
false, //COL_PERCENT_PER_PARENT,
|
, true // COL_AVG_PER_FRAME,
|
||||||
true, //COL_PERCENT_PER_FRAME,
|
, true // COL_MEDIAN_PER_FRAME,
|
||||||
|
, true // COL_NCALLS_PER_FRAME,
|
||||||
false, //COL_PERCENT_SUM_PER_PARENT,
|
, true // COL_TOTAL_TIME_PER_THREAD,
|
||||||
false, //COL_PERCENT_SUM_PER_FRAME,
|
, true // COL_PERCENT_SUM_PER_THREAD,
|
||||||
true, //COL_PERCENT_SUM_PER_THREAD,
|
, true // COL_MIN_PER_THREAD,
|
||||||
|
, true // COL_MAX_PER_THREAD,
|
||||||
true, //COL_END,
|
, true // COL_AVG_PER_THREAD,
|
||||||
|
, true // COL_MEDIAN_PER_THREAD,
|
||||||
true, //COL_MIN_PER_FRAME,
|
, true // COL_NCALLS_PER_THREAD,
|
||||||
true, //COL_MAX_PER_FRAME,
|
, false // COL_PERCENT_PER_PARENT,
|
||||||
true, //COL_AVG_PER_FRAME,
|
, false // COL_TOTAL_TIME_PER_PARENT,
|
||||||
true, //COL_NCALLS_PER_FRAME,
|
, false // COL_PERCENT_SUM_PER_PARENT,
|
||||||
|
, false // COL_MIN_PER_PARENT,
|
||||||
true, //COL_MIN_PER_THREAD,
|
, false // COL_MAX_PER_PARENT,
|
||||||
true, //COL_MAX_PER_THREAD,
|
, false // COL_AVG_PER_PARENT,
|
||||||
true, //COL_AVG_PER_THREAD,
|
, false // COL_MEDIAN_PER_PARENT,
|
||||||
true, //COL_NCALLS_PER_THREAD,
|
, false // COL_NCALLS_PER_PARENT,
|
||||||
|
, true // COL_ACTIVE_TIME,
|
||||||
false, //COL_MIN_PER_PARENT,
|
, true // COL_ACTIVE_PERCENT,
|
||||||
false, //COL_MAX_PER_PARENT,
|
, true // COL_PERCENT_PER_AREA,
|
||||||
false, //COL_AVG_PER_PARENT,
|
, true // COL_TOTAL_TIME_PER_AREA,
|
||||||
false, //COL_NCALLS_PER_PARENT,
|
, true // COL_PERCENT_SUM_PER_AREA,
|
||||||
|
, true // COL_MIN_PER_AREA,
|
||||||
true, //COL_ACTIVE_TIME,
|
, true // COL_MAX_PER_AREA,
|
||||||
true, //COL_ACTIVE_PERCENT,
|
, true // COL_AVG_PER_AREA,
|
||||||
|
, true // COL_MEDIAN_PER_AREA,
|
||||||
true, //COL_PERCENT_PER_AREA,
|
, true // COL_NCALLS_PER_AREA,
|
||||||
true, //COL_TOTAL_TIME_PER_AREA,
|
|
||||||
true, //COL_PERCENT_SUM_PER_AREA,
|
|
||||||
true, //COL_MIN_PER_AREA,
|
|
||||||
true, //COL_MAX_PER_AREA,
|
|
||||||
true, //COL_AVG_PER_AREA,
|
|
||||||
true //COL_NCALLS_PER_AREA,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const bool SELECTION_MODE_COLUMNS[COL_COLUMNS_NUMBER] = {
|
const bool SELECTION_MODE_COLUMNS[COL_COLUMNS_NUMBER] = {
|
||||||
true, //COL_NAME,
|
true // COL_NAME = 0,
|
||||||
false, //COL_BEGIN,
|
, false // COL_BEGIN,
|
||||||
true, //COL_TIME,
|
, true // COL_TIME,
|
||||||
true, //COL_SELF_TIME,
|
, true // COL_SELF_TIME,
|
||||||
|
, true // COL_SELF_TIME_PERCENT,
|
||||||
false, //COL_TOTAL_TIME_PER_PARENT,
|
, false // COL_END,
|
||||||
false, //COL_TOTAL_TIME_PER_FRAME,
|
, false // COL_PERCENT_PER_FRAME,
|
||||||
true, //COL_TOTAL_TIME_PER_THREAD,
|
, false // COL_TOTAL_TIME_PER_FRAME,
|
||||||
|
, false // COL_PERCENT_SUM_PER_FRAME,
|
||||||
true, //COL_SELF_TIME_PERCENT,
|
, false // COL_MIN_PER_FRAME,
|
||||||
|
, false // COL_MAX_PER_FRAME,
|
||||||
false, //COL_PERCENT_PER_PARENT,
|
, false // COL_AVG_PER_FRAME,
|
||||||
false, //COL_PERCENT_PER_FRAME,
|
, false // COL_MEDIAN_PER_FRAME,
|
||||||
|
, false // COL_NCALLS_PER_FRAME,
|
||||||
false, //COL_PERCENT_SUM_PER_PARENT,
|
, true // COL_TOTAL_TIME_PER_THREAD,
|
||||||
false, //COL_PERCENT_SUM_PER_FRAME,
|
, true // COL_PERCENT_SUM_PER_THREAD,
|
||||||
true, //COL_PERCENT_SUM_PER_THREAD,
|
, true // COL_MIN_PER_THREAD,
|
||||||
|
, true // COL_MAX_PER_THREAD,
|
||||||
false, //COL_END,
|
, true // COL_AVG_PER_THREAD,
|
||||||
|
, true // COL_MEDIAN_PER_THREAD,
|
||||||
false, //COL_MIN_PER_FRAME,
|
, true // COL_NCALLS_PER_THREAD,
|
||||||
false, //COL_MAX_PER_FRAME,
|
, false // COL_PERCENT_PER_PARENT,
|
||||||
false, //COL_AVG_PER_FRAME,
|
, false // COL_TOTAL_TIME_PER_PARENT,
|
||||||
false, //COL_NCALLS_PER_FRAME,
|
, false // COL_PERCENT_SUM_PER_PARENT,
|
||||||
|
, false // COL_MIN_PER_PARENT,
|
||||||
true, //COL_MIN_PER_THREAD,
|
, false // COL_MAX_PER_PARENT,
|
||||||
true, //COL_MAX_PER_THREAD,
|
, false // COL_AVG_PER_PARENT,
|
||||||
true, //COL_AVG_PER_THREAD,
|
, false // COL_MEDIAN_PER_PARENT,
|
||||||
true, //COL_NCALLS_PER_THREAD,
|
, false // COL_NCALLS_PER_PARENT,
|
||||||
|
, true // COL_ACTIVE_TIME,
|
||||||
false, //COL_MIN_PER_PARENT,
|
, true // COL_ACTIVE_PERCENT,
|
||||||
false, //COL_MAX_PER_PARENT,
|
, true // COL_PERCENT_PER_AREA,
|
||||||
false, //COL_AVG_PER_PARENT,
|
, true // COL_TOTAL_TIME_PER_AREA,
|
||||||
false, //COL_NCALLS_PER_PARENT,
|
, true // COL_PERCENT_SUM_PER_AREA,
|
||||||
|
, true // COL_MIN_PER_AREA,
|
||||||
true, //COL_ACTIVE_TIME,
|
, true // COL_MAX_PER_AREA,
|
||||||
true, //COL_ACTIVE_PERCENT,
|
, true // COL_AVG_PER_AREA,
|
||||||
|
, true // COL_MEDIAN_PER_AREA,
|
||||||
false, //COL_PERCENT_PER_AREA,
|
, true // COL_NCALLS_PER_AREA,
|
||||||
true, //COL_TOTAL_TIME_PER_AREA,
|
|
||||||
true, //COL_PERCENT_SUM_PER_AREA,
|
|
||||||
true, //COL_MIN_PER_AREA,
|
|
||||||
true, //COL_MAX_PER_AREA,
|
|
||||||
true, //COL_AVG_PER_AREA,
|
|
||||||
true //COL_NCALLS_PER_AREA,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
@ -249,16 +238,19 @@ BlocksTreeWidget::BlocksTreeWidget(QWidget* _parent)
|
|||||||
header_item->setText(COL_MIN_PER_FRAME, "Min/frame");
|
header_item->setText(COL_MIN_PER_FRAME, "Min/frame");
|
||||||
header_item->setText(COL_MAX_PER_FRAME, "Max/frame");
|
header_item->setText(COL_MAX_PER_FRAME, "Max/frame");
|
||||||
header_item->setText(COL_AVG_PER_FRAME, "Avg/frame");
|
header_item->setText(COL_AVG_PER_FRAME, "Avg/frame");
|
||||||
|
header_item->setText(COL_MEDIAN_PER_FRAME, "Mdn/frame");
|
||||||
header_item->setText(COL_NCALLS_PER_FRAME, "N/frame");
|
header_item->setText(COL_NCALLS_PER_FRAME, "N/frame");
|
||||||
|
|
||||||
header_item->setText(COL_MIN_PER_PARENT, "Min/parent");
|
header_item->setText(COL_MIN_PER_PARENT, "Min/parent");
|
||||||
header_item->setText(COL_MAX_PER_PARENT, "Max/parent");
|
header_item->setText(COL_MAX_PER_PARENT, "Max/parent");
|
||||||
header_item->setText(COL_AVG_PER_PARENT, "Avg/parent");
|
header_item->setText(COL_AVG_PER_PARENT, "Avg/parent");
|
||||||
|
header_item->setText(COL_MEDIAN_PER_PARENT, "Mdn/parent");
|
||||||
header_item->setText(COL_NCALLS_PER_PARENT, "N/parent");
|
header_item->setText(COL_NCALLS_PER_PARENT, "N/parent");
|
||||||
|
|
||||||
header_item->setText(COL_MIN_PER_THREAD, "Min/thread");
|
header_item->setText(COL_MIN_PER_THREAD, "Min/thread");
|
||||||
header_item->setText(COL_MAX_PER_THREAD, "Max/thread");
|
header_item->setText(COL_MAX_PER_THREAD, "Max/thread");
|
||||||
header_item->setText(COL_AVG_PER_THREAD, "Avg/thread");
|
header_item->setText(COL_AVG_PER_THREAD, "Avg/thread");
|
||||||
|
header_item->setText(COL_MEDIAN_PER_THREAD, "Mdn/thread");
|
||||||
header_item->setText(COL_NCALLS_PER_THREAD, "N/thread");
|
header_item->setText(COL_NCALLS_PER_THREAD, "N/thread");
|
||||||
|
|
||||||
header_item->setText(COL_ACTIVE_TIME, "WorkTime");
|
header_item->setText(COL_ACTIVE_TIME, "WorkTime");
|
||||||
@ -270,12 +262,14 @@ BlocksTreeWidget::BlocksTreeWidget(QWidget* _parent)
|
|||||||
header_item->setText(COL_MIN_PER_AREA, "Min/area");
|
header_item->setText(COL_MIN_PER_AREA, "Min/area");
|
||||||
header_item->setText(COL_MAX_PER_AREA, "Max/area");
|
header_item->setText(COL_MAX_PER_AREA, "Max/area");
|
||||||
header_item->setText(COL_AVG_PER_AREA, "Avg/area");
|
header_item->setText(COL_AVG_PER_AREA, "Avg/area");
|
||||||
|
header_item->setText(COL_MEDIAN_PER_AREA, "Mdn/area");
|
||||||
header_item->setText(COL_NCALLS_PER_AREA, "N/area");
|
header_item->setText(COL_NCALLS_PER_AREA, "N/area");
|
||||||
|
|
||||||
auto color = QColor::fromRgb(profiler::colors::DeepOrange900);
|
auto color = QColor::fromRgb(profiler::colors::DeepOrange900);
|
||||||
header_item->setForeground(COL_MIN_PER_THREAD, color);
|
header_item->setForeground(COL_MIN_PER_THREAD, color);
|
||||||
header_item->setForeground(COL_MAX_PER_THREAD, color);
|
header_item->setForeground(COL_MAX_PER_THREAD, color);
|
||||||
header_item->setForeground(COL_AVG_PER_THREAD, color);
|
header_item->setForeground(COL_AVG_PER_THREAD, color);
|
||||||
|
header_item->setForeground(COL_MEDIAN_PER_THREAD, color);
|
||||||
header_item->setForeground(COL_NCALLS_PER_THREAD, color);
|
header_item->setForeground(COL_NCALLS_PER_THREAD, color);
|
||||||
header_item->setForeground(COL_PERCENT_SUM_PER_THREAD, color);
|
header_item->setForeground(COL_PERCENT_SUM_PER_THREAD, color);
|
||||||
header_item->setForeground(COL_TOTAL_TIME_PER_THREAD, color);
|
header_item->setForeground(COL_TOTAL_TIME_PER_THREAD, color);
|
||||||
@ -284,6 +278,7 @@ BlocksTreeWidget::BlocksTreeWidget(QWidget* _parent)
|
|||||||
header_item->setForeground(COL_MIN_PER_FRAME, color);
|
header_item->setForeground(COL_MIN_PER_FRAME, color);
|
||||||
header_item->setForeground(COL_MAX_PER_FRAME, color);
|
header_item->setForeground(COL_MAX_PER_FRAME, color);
|
||||||
header_item->setForeground(COL_AVG_PER_FRAME, color);
|
header_item->setForeground(COL_AVG_PER_FRAME, color);
|
||||||
|
header_item->setForeground(COL_MEDIAN_PER_FRAME, color);
|
||||||
header_item->setForeground(COL_NCALLS_PER_FRAME, color);
|
header_item->setForeground(COL_NCALLS_PER_FRAME, color);
|
||||||
header_item->setForeground(COL_PERCENT_SUM_PER_FRAME, color);
|
header_item->setForeground(COL_PERCENT_SUM_PER_FRAME, color);
|
||||||
header_item->setForeground(COL_TOTAL_TIME_PER_FRAME, color);
|
header_item->setForeground(COL_TOTAL_TIME_PER_FRAME, color);
|
||||||
@ -293,6 +288,7 @@ BlocksTreeWidget::BlocksTreeWidget(QWidget* _parent)
|
|||||||
header_item->setForeground(COL_MIN_PER_PARENT, color);
|
header_item->setForeground(COL_MIN_PER_PARENT, color);
|
||||||
header_item->setForeground(COL_MAX_PER_PARENT, color);
|
header_item->setForeground(COL_MAX_PER_PARENT, color);
|
||||||
header_item->setForeground(COL_AVG_PER_PARENT, color);
|
header_item->setForeground(COL_AVG_PER_PARENT, color);
|
||||||
|
header_item->setForeground(COL_MEDIAN_PER_PARENT, color);
|
||||||
header_item->setForeground(COL_NCALLS_PER_PARENT, color);
|
header_item->setForeground(COL_NCALLS_PER_PARENT, color);
|
||||||
header_item->setForeground(COL_PERCENT_SUM_PER_PARENT, color);
|
header_item->setForeground(COL_PERCENT_SUM_PER_PARENT, color);
|
||||||
header_item->setForeground(COL_TOTAL_TIME_PER_PARENT, color);
|
header_item->setForeground(COL_TOTAL_TIME_PER_PARENT, color);
|
||||||
@ -305,6 +301,7 @@ BlocksTreeWidget::BlocksTreeWidget(QWidget* _parent)
|
|||||||
header_item->setForeground(COL_MIN_PER_AREA, color);
|
header_item->setForeground(COL_MIN_PER_AREA, color);
|
||||||
header_item->setForeground(COL_MAX_PER_AREA, color);
|
header_item->setForeground(COL_MAX_PER_AREA, color);
|
||||||
header_item->setForeground(COL_AVG_PER_AREA, color);
|
header_item->setForeground(COL_AVG_PER_AREA, color);
|
||||||
|
header_item->setForeground(COL_MEDIAN_PER_AREA, color);
|
||||||
header_item->setForeground(COL_NCALLS_PER_AREA, color);
|
header_item->setForeground(COL_NCALLS_PER_AREA, color);
|
||||||
|
|
||||||
setHeaderItem(header_item);
|
setHeaderItem(header_item);
|
||||||
@ -502,6 +499,7 @@ void BlocksTreeWidget::onFillTimerTimeout()
|
|||||||
ThreadedItems toplevelitems;
|
ThreadedItems toplevelitems;
|
||||||
m_hierarchyBuilder.takeItems(m_items);
|
m_hierarchyBuilder.takeItems(m_items);
|
||||||
m_hierarchyBuilder.takeTopLevelItems(toplevelitems);
|
m_hierarchyBuilder.takeTopLevelItems(toplevelitems);
|
||||||
|
auto error = m_hierarchyBuilder.error();
|
||||||
m_hierarchyBuilder.interrupt();
|
m_hierarchyBuilder.interrupt();
|
||||||
{
|
{
|
||||||
const QSignalBlocker b(this);
|
const QSignalBlocker b(this);
|
||||||
@ -550,6 +548,12 @@ void BlocksTreeWidget::onFillTimerTimeout()
|
|||||||
connect(this, &Parent::itemDoubleClicked, this, &This::onItemDoubleClicked);
|
connect(this, &Parent::itemDoubleClicked, this, &This::onItemDoubleClicked);
|
||||||
onSelectedThreadChange(EASY_GLOBALS.selected_thread);
|
onSelectedThreadChange(EASY_GLOBALS.selected_thread);
|
||||||
onSelectedBlockChange(EASY_GLOBALS.selected_block);
|
onSelectedBlockChange(EASY_GLOBALS.selected_block);
|
||||||
|
|
||||||
|
if (!error.isEmpty())
|
||||||
|
{
|
||||||
|
QMessageBox::warning(this, "Warning", error, QMessageBox::Close);
|
||||||
|
clearSilent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (m_progress != nullptr)
|
else if (m_progress != nullptr)
|
||||||
{
|
{
|
||||||
@ -578,7 +582,7 @@ void BlocksTreeWidget::onIdleTimeout()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
const int column = columnAt(pos.x());
|
const int column = columnAt(pos.x());
|
||||||
if (item->hasToolTip(column))
|
if (!item->data(column, Qt::ToolTipRole).isNull())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto focusWidget = qApp->focusWidget();
|
auto focusWidget = qApp->focusWidget();
|
||||||
@ -675,9 +679,14 @@ void BlocksTreeWidget::clearSilent(bool _global)
|
|||||||
for (int i = topLevelItemCount() - 1; i >= 0; --i)
|
for (int i = topLevelItemCount() - 1; i >= 0; --i)
|
||||||
topLevelItems.push_back(takeTopLevelItem(i));
|
topLevelItems.push_back(takeTopLevelItem(i));
|
||||||
|
|
||||||
ThreadPool::instance().backgroundJob([=] {
|
#ifdef EASY_LAMBDA_MOVE_CAPTURE
|
||||||
|
ThreadPool::instance().backgroundJob([items = std::move(topLevelItems)] {
|
||||||
|
for (auto item : items)
|
||||||
|
#else
|
||||||
|
ThreadPool::instance().backgroundJob([topLevelItems] {
|
||||||
for (auto item : topLevelItems)
|
for (auto item : topLevelItems)
|
||||||
delete item;
|
#endif
|
||||||
|
profiler_gui::deleteTreeItem(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -947,9 +956,11 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
|||||||
case COL_MIN_PER_THREAD:
|
case COL_MIN_PER_THREAD:
|
||||||
case COL_MIN_PER_PARENT:
|
case COL_MIN_PER_PARENT:
|
||||||
case COL_MIN_PER_FRAME:
|
case COL_MIN_PER_FRAME:
|
||||||
|
case COL_MIN_PER_AREA:
|
||||||
case COL_MAX_PER_THREAD:
|
case COL_MAX_PER_THREAD:
|
||||||
case COL_MAX_PER_PARENT:
|
case COL_MAX_PER_PARENT:
|
||||||
case COL_MAX_PER_FRAME:
|
case COL_MAX_PER_FRAME:
|
||||||
|
case COL_MAX_PER_AREA:
|
||||||
{
|
{
|
||||||
auto& block = item->block();
|
auto& block = item->block();
|
||||||
auto i = profiler_gui::numeric_max<uint32_t>();
|
auto i = profiler_gui::numeric_max<uint32_t>();
|
||||||
@ -961,6 +972,22 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
|||||||
case COL_MAX_PER_THREAD: i = block.per_thread_stats->max_duration_block; break;
|
case COL_MAX_PER_THREAD: i = block.per_thread_stats->max_duration_block; break;
|
||||||
case COL_MAX_PER_PARENT: i = block.per_parent_stats->max_duration_block; break;
|
case COL_MAX_PER_PARENT: i = block.per_parent_stats->max_duration_block; break;
|
||||||
case COL_MAX_PER_FRAME: i = block.per_frame_stats->max_duration_block; break;
|
case COL_MAX_PER_FRAME: i = block.per_frame_stats->max_duration_block; break;
|
||||||
|
|
||||||
|
case COL_MIN_PER_AREA:
|
||||||
|
{
|
||||||
|
auto data = item->data(COL_MIN_PER_AREA, MinMaxBlockIndexRole);
|
||||||
|
if (!data.isNull())
|
||||||
|
i = data.toUInt();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case COL_MAX_PER_AREA:
|
||||||
|
{
|
||||||
|
auto data = item->data(COL_MAX_PER_AREA, MinMaxBlockIndexRole);
|
||||||
|
if (!data.isNull())
|
||||||
|
i = data.toUInt();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i != profiler_gui::numeric_max(i))
|
if (i != profiler_gui::numeric_max(i))
|
||||||
@ -1036,6 +1063,7 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
|||||||
ADD_COLUMN_ACTION(COL_MIN_PER_FRAME);
|
ADD_COLUMN_ACTION(COL_MIN_PER_FRAME);
|
||||||
ADD_COLUMN_ACTION(COL_MAX_PER_FRAME);
|
ADD_COLUMN_ACTION(COL_MAX_PER_FRAME);
|
||||||
ADD_COLUMN_ACTION(COL_AVG_PER_FRAME);
|
ADD_COLUMN_ACTION(COL_AVG_PER_FRAME);
|
||||||
|
ADD_COLUMN_ACTION(COL_MEDIAN_PER_FRAME);
|
||||||
ADD_COLUMN_ACTION(COL_NCALLS_PER_FRAME);
|
ADD_COLUMN_ACTION(COL_NCALLS_PER_FRAME);
|
||||||
|
|
||||||
hidemenu->addSeparator();
|
hidemenu->addSeparator();
|
||||||
@ -1045,6 +1073,7 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
|||||||
ADD_COLUMN_ACTION(COL_MIN_PER_THREAD);
|
ADD_COLUMN_ACTION(COL_MIN_PER_THREAD);
|
||||||
ADD_COLUMN_ACTION(COL_MAX_PER_THREAD);
|
ADD_COLUMN_ACTION(COL_MAX_PER_THREAD);
|
||||||
ADD_COLUMN_ACTION(COL_AVG_PER_THREAD);
|
ADD_COLUMN_ACTION(COL_AVG_PER_THREAD);
|
||||||
|
ADD_COLUMN_ACTION(COL_MEDIAN_PER_THREAD);
|
||||||
ADD_COLUMN_ACTION(COL_NCALLS_PER_THREAD);
|
ADD_COLUMN_ACTION(COL_NCALLS_PER_THREAD);
|
||||||
|
|
||||||
hidemenu->addSeparator();
|
hidemenu->addSeparator();
|
||||||
@ -1055,6 +1084,7 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
|||||||
ADD_COLUMN_ACTION(COL_MIN_PER_PARENT);
|
ADD_COLUMN_ACTION(COL_MIN_PER_PARENT);
|
||||||
ADD_COLUMN_ACTION(COL_MAX_PER_PARENT);
|
ADD_COLUMN_ACTION(COL_MAX_PER_PARENT);
|
||||||
ADD_COLUMN_ACTION(COL_AVG_PER_PARENT);
|
ADD_COLUMN_ACTION(COL_AVG_PER_PARENT);
|
||||||
|
ADD_COLUMN_ACTION(COL_MEDIAN_PER_PARENT);
|
||||||
ADD_COLUMN_ACTION(COL_NCALLS_PER_PARENT);
|
ADD_COLUMN_ACTION(COL_NCALLS_PER_PARENT);
|
||||||
|
|
||||||
hidemenu->addSeparator();
|
hidemenu->addSeparator();
|
||||||
@ -1070,6 +1100,7 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
|||||||
ADD_COLUMN_ACTION(COL_MIN_PER_AREA);
|
ADD_COLUMN_ACTION(COL_MIN_PER_AREA);
|
||||||
ADD_COLUMN_ACTION(COL_MAX_PER_AREA);
|
ADD_COLUMN_ACTION(COL_MAX_PER_AREA);
|
||||||
ADD_COLUMN_ACTION(COL_AVG_PER_AREA);
|
ADD_COLUMN_ACTION(COL_AVG_PER_AREA);
|
||||||
|
ADD_COLUMN_ACTION(COL_MEDIAN_PER_AREA);
|
||||||
ADD_COLUMN_ACTION(COL_NCALLS_PER_AREA);
|
ADD_COLUMN_ACTION(COL_NCALLS_PER_AREA);
|
||||||
|
|
||||||
#undef ADD_STATUS_ACTION
|
#undef ADD_STATUS_ACTION
|
||||||
@ -1473,19 +1504,29 @@ void BlocksTreeWidget::loadSettings()
|
|||||||
settings.beginGroup("tree_widget");
|
settings.beginGroup("tree_widget");
|
||||||
|
|
||||||
auto val = settings.value("regime");
|
auto val = settings.value("regime");
|
||||||
if (!val.isNull())
|
|
||||||
m_mode = static_cast<TreeMode>(val.toUInt());
|
|
||||||
|
|
||||||
val = settings.value("columns");
|
|
||||||
if (!val.isNull())
|
if (!val.isNull())
|
||||||
{
|
{
|
||||||
auto byteArray = val.toByteArray();
|
m_mode = static_cast<TreeMode>(val.toUInt());
|
||||||
memcpy(m_columnsHiddenStatus, byteArray.constData(), ::std::min(sizeof(m_columnsHiddenStatus), (size_t)byteArray.size()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto state = settings.value("headerState").toByteArray();
|
val = settings.value("columns_version");
|
||||||
if (!state.isEmpty())
|
if (!val.isNull() && val.toInt() == COLUMNS_VERSION)
|
||||||
header()->restoreState(state);
|
{
|
||||||
|
val = settings.value("columns");
|
||||||
|
if (!val.isNull())
|
||||||
|
{
|
||||||
|
auto byteArray = val.toByteArray();
|
||||||
|
memcpy(
|
||||||
|
m_columnsHiddenStatus, byteArray.constData(), std::min(sizeof(m_columnsHiddenStatus), (size_t)byteArray.size())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto state = settings.value("headerState").toByteArray();
|
||||||
|
if (!state.isEmpty())
|
||||||
|
{
|
||||||
|
header()->restoreState(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
}
|
}
|
||||||
@ -1495,6 +1536,7 @@ void BlocksTreeWidget::saveSettings()
|
|||||||
QSettings settings(profiler_gui::ORGANAZATION_NAME, profiler_gui::APPLICATION_NAME);
|
QSettings settings(profiler_gui::ORGANAZATION_NAME, profiler_gui::APPLICATION_NAME);
|
||||||
settings.beginGroup("tree_widget");
|
settings.beginGroup("tree_widget");
|
||||||
settings.setValue("regime", static_cast<uint8_t>(m_mode));
|
settings.setValue("regime", static_cast<uint8_t>(m_mode));
|
||||||
|
settings.setValue("columns_version", COLUMNS_VERSION);
|
||||||
settings.setValue("columns", QByteArray(m_columnsHiddenStatus, COL_COLUMNS_NUMBER));
|
settings.setValue("columns", QByteArray(m_columnsHiddenStatus, COL_COLUMNS_NUMBER));
|
||||||
settings.setValue("headerState", header()->saveState());
|
settings.setValue("headerState", header()->saveState());
|
||||||
settings.endGroup();
|
settings.endGroup();
|
||||||
|
@ -52,6 +52,8 @@
|
|||||||
* : limitations under the License.
|
* : limitations under the License.
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
|
|
||||||
|
#include <QList>
|
||||||
|
#include <QTreeWidgetItem>
|
||||||
#include "common_functions.h"
|
#include "common_functions.h"
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -459,4 +461,22 @@ namespace profiler_gui {
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void deleteTreeItem(QTreeWidgetItem* item)
|
||||||
|
{
|
||||||
|
if (item == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QTreeWidgetItem*> stack;
|
||||||
|
stack.append(item);
|
||||||
|
|
||||||
|
while (!stack.isEmpty())
|
||||||
|
{
|
||||||
|
auto i = stack.takeFirst();
|
||||||
|
stack.append(i->takeChildren());
|
||||||
|
delete i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // end of namespace profiler_gui.
|
} // end of namespace profiler_gui.
|
||||||
|
@ -66,6 +66,8 @@
|
|||||||
|
|
||||||
#include "common_types.h"
|
#include "common_types.h"
|
||||||
|
|
||||||
|
class QTreeWidgetItem;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -237,6 +239,10 @@ void updateProperty(QWidget* widget, const char* name, T&& property)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void deleteTreeItem(QTreeWidgetItem* item);
|
||||||
|
|
||||||
} // END of namespace profiler_gui.
|
} // END of namespace profiler_gui.
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -459,9 +459,14 @@ void DescriptorsTreeWidget::clearSilent(bool _global)
|
|||||||
topLevelItems.push_back(item);
|
topLevelItems.push_back(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPool::instance().backgroundJob([=] {
|
#ifdef EASY_LAMBDA_MOVE_CAPTURE
|
||||||
|
ThreadPool::instance().backgroundJob([items = std::move(topLevelItems)] {
|
||||||
|
for (auto item : items)
|
||||||
|
#else
|
||||||
|
ThreadPool::instance().backgroundJob([topLevelItems] {
|
||||||
for (auto item : topLevelItems)
|
for (auto item : topLevelItems)
|
||||||
delete item;
|
#endif
|
||||||
|
profiler_gui::deleteTreeItem(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +85,7 @@ Globals::Globals()
|
|||||||
, selected_block(::profiler_gui::numeric_max<decltype(selected_block)>())
|
, selected_block(::profiler_gui::numeric_max<decltype(selected_block)>())
|
||||||
, selected_block_id(::profiler_gui::numeric_max<decltype(selected_block_id)>())
|
, selected_block_id(::profiler_gui::numeric_max<decltype(selected_block_id)>())
|
||||||
, version(0)
|
, version(0)
|
||||||
|
, max_rows_count(500 * 1000)
|
||||||
, frame_time(16700)
|
, frame_time(16700)
|
||||||
, blocks_spacing(0)
|
, blocks_spacing(0)
|
||||||
, blocks_size_min(2)
|
, blocks_size_min(2)
|
||||||
|
@ -213,6 +213,8 @@ namespace profiler_gui {
|
|||||||
::profiler::block_id_t selected_block_id; ///< Current selected profiler block id
|
::profiler::block_id_t selected_block_id; ///< Current selected profiler block id
|
||||||
uint32_t version; ///< Opened file version (files may have different format)
|
uint32_t version; ///< Opened file version (files may have different format)
|
||||||
|
|
||||||
|
uint32_t max_rows_count; ///< Maximum blocks count for the Hierarchy widget for the full call-stack mode
|
||||||
|
|
||||||
float frame_time; ///< Expected frame time value in microseconds to be displayed at minimap on graphics scrollbar
|
float frame_time; ///< Expected frame time value in microseconds to be displayed at minimap on graphics scrollbar
|
||||||
int blocks_spacing; ///< Minimum blocks spacing on diagram
|
int blocks_spacing; ///< Minimum blocks spacing on diagram
|
||||||
int blocks_size_min; ///< Minimum blocks size on diagram
|
int blocks_size_min; ///< Minimum blocks size on diagram
|
||||||
|
@ -722,6 +722,22 @@ MainWindow::MainWindow() : Parent(), m_theme("default"), m_lastAddress("localhos
|
|||||||
submenu->addAction(waction);
|
submenu->addAction(waction);
|
||||||
|
|
||||||
|
|
||||||
|
submenu->addSeparator();
|
||||||
|
w = new QWidget(submenu);
|
||||||
|
l = new QHBoxLayout(w);
|
||||||
|
l->setContentsMargins(26, 1, 16, 1);
|
||||||
|
l->addWidget(new QLabel("Max rows in tree", w), 0, Qt::AlignLeft);
|
||||||
|
spinbox = new QSpinBox(w);
|
||||||
|
spinbox->setRange(0, std::numeric_limits<int>::max());
|
||||||
|
spinbox->setValue(static_cast<int>(EASY_GLOBALS.max_rows_count));
|
||||||
|
spinbox->setFixedWidth(px(100));
|
||||||
|
connect(spinbox, Overload<int>::of(&QSpinBox::valueChanged), this, &This::onMaxBlocksCountChange);
|
||||||
|
l->addWidget(spinbox);
|
||||||
|
w->setLayout(l);
|
||||||
|
waction = new QWidgetAction(submenu);
|
||||||
|
waction->setDefaultWidget(w);
|
||||||
|
submenu->addAction(waction);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
submenu = menu->addMenu("FPS Monitor");
|
submenu = menu->addMenu("FPS Monitor");
|
||||||
@ -1470,6 +1486,16 @@ void MainWindow::onViewportInfoClicked(bool)
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void MainWindow::onMaxBlocksCountChange(int _value)
|
||||||
|
{
|
||||||
|
if (_value >= 0)
|
||||||
|
{
|
||||||
|
EASY_GLOBALS.max_rows_count = static_cast<uint32_t>(_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void MainWindow::onSpacingChange(int _value)
|
void MainWindow::onSpacingChange(int _value)
|
||||||
{
|
{
|
||||||
EASY_GLOBALS.blocks_spacing = _value;
|
EASY_GLOBALS.blocks_spacing = _value;
|
||||||
@ -1676,6 +1702,10 @@ void MainWindow::loadSettings()
|
|||||||
if (!val.isNull())
|
if (!val.isNull())
|
||||||
EASY_GLOBALS.frame_time = val.toFloat();
|
EASY_GLOBALS.frame_time = val.toFloat();
|
||||||
|
|
||||||
|
val = settings.value("max_rows_count");
|
||||||
|
if (!val.isNull())
|
||||||
|
EASY_GLOBALS.max_rows_count = val.toUInt();
|
||||||
|
|
||||||
val = settings.value("blocks_spacing");
|
val = settings.value("blocks_spacing");
|
||||||
if (!val.isNull())
|
if (!val.isNull())
|
||||||
EASY_GLOBALS.blocks_spacing = val.toInt();
|
EASY_GLOBALS.blocks_spacing = val.toInt();
|
||||||
@ -1853,6 +1883,7 @@ void MainWindow::saveSettingsAndGeometry()
|
|||||||
settings.setValue("chrono_text_position", static_cast<int>(EASY_GLOBALS.chrono_text_position));
|
settings.setValue("chrono_text_position", static_cast<int>(EASY_GLOBALS.chrono_text_position));
|
||||||
settings.setValue("time_units", static_cast<int>(EASY_GLOBALS.time_units));
|
settings.setValue("time_units", static_cast<int>(EASY_GLOBALS.time_units));
|
||||||
settings.setValue("frame_time", EASY_GLOBALS.frame_time);
|
settings.setValue("frame_time", EASY_GLOBALS.frame_time);
|
||||||
|
settings.setValue("max_rows_count", EASY_GLOBALS.max_rows_count);
|
||||||
settings.setValue("blocks_spacing", EASY_GLOBALS.blocks_spacing);
|
settings.setValue("blocks_spacing", EASY_GLOBALS.blocks_spacing);
|
||||||
settings.setValue("blocks_size_min", EASY_GLOBALS.blocks_size_min);
|
settings.setValue("blocks_size_min", EASY_GLOBALS.blocks_size_min);
|
||||||
settings.setValue("blocks_narrow_size", EASY_GLOBALS.blocks_narrow_size);
|
settings.setValue("blocks_narrow_size", EASY_GLOBALS.blocks_narrow_size);
|
||||||
|
@ -344,6 +344,7 @@ protected slots:
|
|||||||
void onExpandAllClicked(bool);
|
void onExpandAllClicked(bool);
|
||||||
void onCollapseAllClicked(bool);
|
void onCollapseAllClicked(bool);
|
||||||
void onViewportInfoClicked(bool);
|
void onViewportInfoClicked(bool);
|
||||||
|
void onMaxBlocksCountChange(int _value);
|
||||||
void onSpacingChange(int _value);
|
void onSpacingChange(int _value);
|
||||||
void onMinSizeChange(int _value);
|
void onMinSizeChange(int _value);
|
||||||
void onNarrowSizeChange(int _value);
|
void onNarrowSizeChange(int _value);
|
||||||
|
@ -69,7 +69,7 @@
|
|||||||
# pragma message "TODO: include proper headers to be able to use pthread_setschedprio() on OSX and iOS (pthread.h is not enough...)"
|
# pragma message "TODO: include proper headers to be able to use pthread_setschedprio() on OSX and iOS (pthread.h is not enough...)"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void setLowestThreadPriority()
|
static void setLowestThreadPriority()
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
|
||||||
@ -95,13 +95,32 @@ ThreadPool& ThreadPool::instance()
|
|||||||
|
|
||||||
ThreadPool::ThreadPool()
|
ThreadPool::ThreadPool()
|
||||||
{
|
{
|
||||||
m_threads.reserve(std::thread::hardware_concurrency() + 1);
|
auto count = std::thread::hardware_concurrency();
|
||||||
|
#ifdef EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||||
|
if (count > 1)
|
||||||
|
count -= 1;
|
||||||
|
m_threads.reserve(count + 2);
|
||||||
|
#else
|
||||||
|
m_threads.reserve(count + 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
// N threads for main tasks
|
// N threads for main tasks
|
||||||
std::generate_n(std::back_inserter(m_threads), std::thread::hardware_concurrency(), [this] {
|
std::generate_n(std::back_inserter(m_threads), count, [this] {
|
||||||
return std::thread(&ThreadPool::tasksWorker, this);
|
return std::thread(&ThreadPool::tasksWorker, this, std::ref(m_tasks));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
#ifdef EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||||
|
// FIXME: Workaround for Qt on Linux
|
||||||
|
//
|
||||||
|
// There is a memory leak in Qt when creating Qt objects in several separate threads
|
||||||
|
// and removing them in one another (different) thread.
|
||||||
|
//
|
||||||
|
// But there is no memory leak when there is only one separate thread (apart from main thread)
|
||||||
|
// for creating Qt objects and one separate (different!) thread for removing them.
|
||||||
|
//
|
||||||
|
m_threads.emplace_back(&ThreadPool::tasksWorker, this, std::ref(m_qtasks));
|
||||||
|
#endif
|
||||||
|
|
||||||
// One thread for background jobs
|
// One thread for background jobs
|
||||||
m_threads.emplace_back(&ThreadPool::jobsWorker, this);
|
m_threads.emplace_back(&ThreadPool::jobsWorker, this);
|
||||||
}
|
}
|
||||||
@ -110,6 +129,9 @@ ThreadPool::~ThreadPool()
|
|||||||
{
|
{
|
||||||
m_interrupt.store(true, std::memory_order_release);
|
m_interrupt.store(true, std::memory_order_release);
|
||||||
m_tasks.cv.notify_all();
|
m_tasks.cv.notify_all();
|
||||||
|
#ifdef EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||||
|
m_qtasks.cv.notify_all();
|
||||||
|
#endif
|
||||||
m_backgroundJobs.cv.notify_all();
|
m_backgroundJobs.cv.notify_all();
|
||||||
for (auto& thread : m_threads)
|
for (auto& thread : m_threads)
|
||||||
thread.join();
|
thread.join();
|
||||||
@ -125,53 +147,69 @@ void ThreadPool::backgroundJob(std::function<void()>&& func)
|
|||||||
|
|
||||||
void ThreadPool::enqueue(ThreadPoolTask& task)
|
void ThreadPool::enqueue(ThreadPoolTask& task)
|
||||||
{
|
{
|
||||||
m_tasks.mutex.lock();
|
#ifdef EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||||
m_tasks.queue.emplace_back(task);
|
if (task.creatingQtObjects())
|
||||||
m_tasks.mutex.unlock();
|
enqueue(task, m_qtasks);
|
||||||
m_tasks.cv.notify_one();
|
else
|
||||||
|
#endif
|
||||||
|
enqueue(task, m_tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::dequeue(ThreadPoolTask& task)
|
void ThreadPool::dequeue(ThreadPoolTask& task)
|
||||||
{
|
{
|
||||||
const std::lock_guard<std::mutex> lock(m_tasks.mutex);
|
#ifdef EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||||
|
if (task.creatingQtObjects())
|
||||||
|
dequeue(task, m_qtasks);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
dequeue(task, m_tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::enqueue(ThreadPoolTask& task, TaskJobs& tasks)
|
||||||
|
{
|
||||||
|
tasks.mutex.lock();
|
||||||
|
tasks.queue.emplace_back(task);
|
||||||
|
tasks.mutex.unlock();
|
||||||
|
tasks.cv.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::dequeue(ThreadPoolTask& task, TaskJobs& tasks)
|
||||||
|
{
|
||||||
|
const std::lock_guard<std::mutex> lock(tasks.mutex);
|
||||||
|
|
||||||
if (task.status() != TaskStatus::Enqueued)
|
if (task.status() != TaskStatus::Enqueued)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (auto it = m_tasks.queue.begin(); it != m_tasks.queue.end(); ++it)
|
for (auto it = tasks.queue.begin(); it != tasks.queue.end(); ++it)
|
||||||
{
|
{
|
||||||
if (&it->get() == &task)
|
if (&it->get() == &task)
|
||||||
{
|
{
|
||||||
m_tasks.queue.erase(it);
|
tasks.queue.erase(it);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPool::tasksWorker()
|
void ThreadPool::tasksWorker(Jobs<std::reference_wrapper<ThreadPoolTask> >& tasks)
|
||||||
{
|
{
|
||||||
while (true)
|
while (!m_interrupt.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(m_tasks.mutex);
|
std::unique_lock<std::mutex> lock(tasks.mutex);
|
||||||
m_tasks.cv.wait(lock, [this] { return !m_tasks.queue.empty() || m_interrupt.load(std::memory_order_acquire); });
|
tasks.cv.wait(lock, [this, &tasks] {
|
||||||
|
return !tasks.queue.empty() || m_interrupt.load(std::memory_order_acquire);
|
||||||
|
});
|
||||||
|
|
||||||
while (true) // execute all available tasks
|
while (!tasks.queue.empty() && !m_interrupt.load(std::memory_order_acquire)) // execute all available tasks
|
||||||
{
|
{
|
||||||
if (m_interrupt.load(std::memory_order_acquire))
|
auto& task = tasks.queue.front().get();
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_tasks.queue.empty())
|
|
||||||
break; // the lock will be released on the outer loop new iteration
|
|
||||||
|
|
||||||
auto& task = m_tasks.queue.front().get();
|
|
||||||
task.setStatus(TaskStatus::Processing);
|
task.setStatus(TaskStatus::Processing);
|
||||||
m_tasks.queue.pop_front();
|
tasks.queue.pop_front();
|
||||||
|
|
||||||
// unlock to permit tasks execution for other worker threads and for adding new tasks
|
// unlock to permit tasks execution for other worker threads and for adding new tasks
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
|
||||||
// execute task
|
// execute task
|
||||||
task.execute();
|
task();
|
||||||
|
|
||||||
// lock again to check if there are new tasks in the queue
|
// lock again to check if there are new tasks in the queue
|
||||||
lock.lock();
|
lock.lock();
|
||||||
@ -183,21 +221,15 @@ void ThreadPool::jobsWorker()
|
|||||||
{
|
{
|
||||||
setLowestThreadPriority(); // Background thread has lowest priority
|
setLowestThreadPriority(); // Background thread has lowest priority
|
||||||
|
|
||||||
while (true)
|
while (!m_interrupt.load(std::memory_order_acquire))
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(m_backgroundJobs.mutex);
|
std::unique_lock<std::mutex> lock(m_backgroundJobs.mutex);
|
||||||
m_backgroundJobs.cv.wait(lock, [this] {
|
m_backgroundJobs.cv.wait(lock, [this] {
|
||||||
return !m_backgroundJobs.queue.empty() || m_interrupt.load(std::memory_order_acquire);
|
return !m_backgroundJobs.queue.empty() || m_interrupt.load(std::memory_order_acquire);
|
||||||
});
|
});
|
||||||
|
|
||||||
while (true) // execute all available jobs
|
while (!m_backgroundJobs.queue.empty()) // execute all available jobs
|
||||||
{
|
{
|
||||||
if (m_interrupt.load(std::memory_order_acquire))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_backgroundJobs.queue.empty())
|
|
||||||
break; // the lock will be released on the outer loop new iteration
|
|
||||||
|
|
||||||
auto job = std::move(m_backgroundJobs.queue.front());
|
auto job = std::move(m_backgroundJobs.queue.front());
|
||||||
m_backgroundJobs.queue.pop_front();
|
m_backgroundJobs.queue.pop_front();
|
||||||
|
|
||||||
|
@ -58,6 +58,18 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && !defined(__APPLE__)
|
||||||
|
// FIXME: Workaround for Qt on Linux
|
||||||
|
//
|
||||||
|
// There is a memory leak in Qt when creating Qt objects in several separate threads
|
||||||
|
// and removing them in one another (different) thread.
|
||||||
|
//
|
||||||
|
// But there is no memory leak when there is only one separate thread (apart from main thread)
|
||||||
|
// for creating Qt objects and one separate (different!) thread for removing them.
|
||||||
|
//
|
||||||
|
#define EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||||
|
#endif
|
||||||
|
|
||||||
class ThreadPool EASY_FINAL
|
class ThreadPool EASY_FINAL
|
||||||
{
|
{
|
||||||
friend ThreadPoolTask;
|
friend ThreadPoolTask;
|
||||||
@ -70,8 +82,14 @@ class ThreadPool EASY_FINAL
|
|||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
};
|
};
|
||||||
|
|
||||||
Jobs<std::reference_wrapper<ThreadPoolTask> > m_tasks;
|
using TaskJobs = Jobs<std::reference_wrapper<ThreadPoolTask> >;
|
||||||
Jobs<std::function<void()> > m_backgroundJobs;
|
using BackgroundJobs = Jobs<std::function<void()> >;
|
||||||
|
|
||||||
|
TaskJobs m_tasks;
|
||||||
|
#ifdef EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||||
|
TaskJobs m_qtasks;
|
||||||
|
#endif
|
||||||
|
BackgroundJobs m_backgroundJobs;
|
||||||
std::vector<std::thread> m_threads;
|
std::vector<std::thread> m_threads;
|
||||||
std::atomic_bool m_interrupt;
|
std::atomic_bool m_interrupt;
|
||||||
|
|
||||||
@ -89,7 +107,11 @@ private:
|
|||||||
|
|
||||||
void enqueue(ThreadPoolTask& task);
|
void enqueue(ThreadPoolTask& task);
|
||||||
void dequeue(ThreadPoolTask& task);
|
void dequeue(ThreadPoolTask& task);
|
||||||
void tasksWorker();
|
|
||||||
|
void enqueue(ThreadPoolTask& task, TaskJobs& tasks);
|
||||||
|
void dequeue(ThreadPoolTask& task, TaskJobs& tasks);
|
||||||
|
|
||||||
|
void tasksWorker(TaskJobs& tasks);
|
||||||
void jobsWorker();
|
void jobsWorker();
|
||||||
|
|
||||||
}; // end of class ThreadPool.
|
}; // end of class ThreadPool.
|
||||||
|
@ -53,7 +53,10 @@
|
|||||||
|
|
||||||
static std::atomic_bool s_dummy_flag {false};
|
static std::atomic_bool s_dummy_flag {false};
|
||||||
|
|
||||||
ThreadPoolTask::ThreadPoolTask() : m_func([] {}), m_interrupt(&s_dummy_flag)
|
ThreadPoolTask::ThreadPoolTask(bool creatingQtObjects)
|
||||||
|
: m_func([] {})
|
||||||
|
, m_interrupt(&s_dummy_flag)
|
||||||
|
, m_creatingQtObjects(creatingQtObjects)
|
||||||
{
|
{
|
||||||
m_status = static_cast<int8_t>(TaskStatus::Finished);
|
m_status = static_cast<int8_t>(TaskStatus::Finished);
|
||||||
}
|
}
|
||||||
@ -64,6 +67,11 @@ ThreadPoolTask::~ThreadPoolTask()
|
|||||||
dequeue();
|
dequeue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ThreadPoolTask::creatingQtObjects() const
|
||||||
|
{
|
||||||
|
return m_creatingQtObjects;
|
||||||
|
}
|
||||||
|
|
||||||
void ThreadPoolTask::enqueue(Func&& func, std::atomic_bool& interruptFlag)
|
void ThreadPoolTask::enqueue(Func&& func, std::atomic_bool& interruptFlag)
|
||||||
{
|
{
|
||||||
dequeue();
|
dequeue();
|
||||||
@ -101,7 +109,7 @@ TaskStatus ThreadPoolTask::status() const
|
|||||||
return static_cast<TaskStatus>(m_status.load(std::memory_order_acquire));
|
return static_cast<TaskStatus>(m_status.load(std::memory_order_acquire));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadPoolTask::execute()
|
void ThreadPoolTask::operator()()
|
||||||
{
|
{
|
||||||
// execute if not cancelled
|
// execute if not cancelled
|
||||||
{
|
{
|
||||||
|
@ -93,13 +93,14 @@ private:
|
|||||||
std::atomic_bool* m_interrupt;
|
std::atomic_bool* m_interrupt;
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::atomic<int8_t> m_status;
|
std::atomic<int8_t> m_status;
|
||||||
|
const bool m_creatingQtObjects;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ThreadPoolTask(const ThreadPoolTask&) = delete;
|
ThreadPoolTask(const ThreadPoolTask&) = delete;
|
||||||
ThreadPoolTask(ThreadPoolTask&&) = delete;
|
ThreadPoolTask(ThreadPoolTask&&) = delete;
|
||||||
|
|
||||||
ThreadPoolTask();
|
explicit ThreadPoolTask(bool creatingQtObjects = false);
|
||||||
~ThreadPoolTask();
|
~ThreadPoolTask();
|
||||||
|
|
||||||
void enqueue(Func&& func, std::atomic_bool& interruptFlag);
|
void enqueue(Func&& func, std::atomic_bool& interruptFlag);
|
||||||
@ -109,7 +110,9 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void execute();
|
bool creatingQtObjects() const;
|
||||||
|
|
||||||
|
void operator() ();
|
||||||
|
|
||||||
TaskStatus status() const;
|
TaskStatus status() const;
|
||||||
void setStatus(TaskStatus status);
|
void setStatus(TaskStatus status);
|
||||||
|
@ -71,55 +71,48 @@
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
EASY_CONSTEXPR int BlockColorRole = Qt::UserRole + 1;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
EASY_CONSTEXPR int ColumnBit[COL_COLUMNS_NUMBER] = {
|
EASY_CONSTEXPR bool HasToolTip[COL_COLUMNS_NUMBER] = {
|
||||||
-1 // COL_NAME = 0,
|
false // COL_NAME = 0,
|
||||||
|
, true // COL_BEGIN,
|
||||||
, 0 // COL_BEGIN,
|
, true // COL_TIME,
|
||||||
|
, true // COL_SELF_TIME,
|
||||||
, 1 // COL_TIME,
|
, false // COL_SELF_TIME_PERCENT,
|
||||||
, 2 // COL_SELF_TIME,
|
, true // COL_END,
|
||||||
, 3 // COL_TOTAL_TIME_PER_PARENT,
|
, false // COL_PERCENT_PER_FRAME,
|
||||||
, 4 // COL_TOTAL_TIME_PER_FRAME,
|
, true // COL_TOTAL_TIME_PER_FRAME,
|
||||||
, 5 // COL_TOTAL_TIME_PER_THREAD,
|
, false // COL_PERCENT_SUM_PER_FRAME,
|
||||||
|
, true // COL_MIN_PER_FRAME,
|
||||||
, -1 // COL_SELF_TIME_PERCENT,
|
, true // COL_MAX_PER_FRAME,
|
||||||
, -1 // COL_PERCENT_PER_PARENT,
|
, true // COL_AVG_PER_FRAME,
|
||||||
, -1 // COL_PERCENT_PER_FRAME,
|
, true // COL_MEDIAN_PER_FRAME,
|
||||||
, -1 // COL_PERCENT_SUM_PER_PARENT,
|
, false // COL_NCALLS_PER_FRAME,
|
||||||
, -1 // COL_PERCENT_SUM_PER_FRAME,
|
, true // COL_TOTAL_TIME_PER_THREAD,
|
||||||
, -1 // COL_PERCENT_SUM_PER_THREAD,
|
, false // COL_PERCENT_SUM_PER_THREAD,
|
||||||
|
, true // COL_MIN_PER_THREAD,
|
||||||
, 6 // COL_END,
|
, true // COL_MAX_PER_THREAD,
|
||||||
|
, true // COL_AVG_PER_THREAD,
|
||||||
, 7 // COL_MIN_PER_FRAME,
|
, true // COL_MEDIAN_PER_THREAD,
|
||||||
, 8 // COL_MAX_PER_FRAME,
|
, false // COL_NCALLS_PER_THREAD,
|
||||||
, 9 // COL_AVG_PER_FRAME,
|
, false // COL_PERCENT_PER_PARENT,
|
||||||
, -1 // COL_NCALLS_PER_FRAME,
|
, true // COL_TOTAL_TIME_PER_PARENT,
|
||||||
|
, false // COL_PERCENT_SUM_PER_PARENT,
|
||||||
, 10 // COL_MIN_PER_THREAD,
|
, true // COL_MIN_PER_PARENT,
|
||||||
, 11 // COL_MAX_PER_THREAD,
|
, true // COL_MAX_PER_PARENT,
|
||||||
, 12 // COL_AVG_PER_THREAD,
|
, true // COL_AVG_PER_PARENT,
|
||||||
, -1 // COL_NCALLS_PER_THREAD,
|
, true // COL_MEDIAN_PER_PARENT,
|
||||||
|
, false // COL_NCALLS_PER_PARENT,
|
||||||
, 13 // COL_MIN_PER_PARENT,
|
, true // COL_ACTIVE_TIME,
|
||||||
, 14 // COL_MAX_PER_PARENT,
|
, false // COL_ACTIVE_PERCENT,
|
||||||
, 15 // COL_AVG_PER_PARENT,
|
, false // COL_PERCENT_PER_AREA,
|
||||||
, -1 // COL_NCALLS_PER_PARENT,
|
, true // COL_TOTAL_TIME_PER_AREA,
|
||||||
|
, false // COL_PERCENT_SUM_PER_AREA,
|
||||||
, 16 // COL_ACTIVE_TIME,
|
, true // COL_MIN_PER_AREA,
|
||||||
, -1 // COL_ACTIVE_PERCENT,
|
, true // COL_MAX_PER_AREA,
|
||||||
|
, true // COL_AVG_PER_AREA,
|
||||||
, -1 // COL_PERCENT_PER_AREA,
|
, true // COL_MEDIAN_PER_AREA,
|
||||||
, 17 // COL_TOTAL_TIME_PER_AREA,
|
, false // COL_NCALLS_PER_AREA,
|
||||||
, -1 // COL_PERCENT_SUM_PER_AREA,
|
|
||||||
, 18 // COL_MIN_PER_AREA,
|
|
||||||
, 19 // COL_MAX_PER_AREA,
|
|
||||||
, 20 // COL_AVG_PER_AREA,
|
|
||||||
, -1 // COL_NCALLS_PER_AREA,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end of namespace <noname>.
|
} // end of namespace <noname>.
|
||||||
@ -192,19 +185,6 @@ bool TreeWidgetItem::isPartial() const
|
|||||||
return m_partial;
|
return m_partial;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TreeWidgetItem::hasToolTip(int _column) const
|
|
||||||
{
|
|
||||||
const int bit = ColumnBit[_column];
|
|
||||||
return bit < 0 ? false : m_bHasToolTip.test(static_cast<size_t>(bit));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TreeWidgetItem::setHasToolTip(int _column)
|
|
||||||
{
|
|
||||||
const int bit = ColumnBit[_column];
|
|
||||||
if (bit >= 0)
|
|
||||||
m_bHasToolTip.set(static_cast<size_t>(bit), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant TreeWidgetItem::data(int _column, int _role) const
|
QVariant TreeWidgetItem::data(int _column, int _role) const
|
||||||
{
|
{
|
||||||
if (_column == COL_NAME)
|
if (_column == COL_NAME)
|
||||||
@ -233,15 +213,29 @@ QVariant TreeWidgetItem::data(int _column, int _role) const
|
|||||||
|
|
||||||
case Qt::ToolTipRole:
|
case Qt::ToolTipRole:
|
||||||
{
|
{
|
||||||
if (hasToolTip(_column))
|
const bool hasToolTip = HasToolTip[_column];
|
||||||
return QVariant::fromValue(QString("%1 ns").arg(data(_column, Qt::UserRole).toULongLong()));
|
if (hasToolTip)
|
||||||
|
{
|
||||||
|
auto v = data(_column, Qt::UserRole);
|
||||||
|
if (!v.isNull())
|
||||||
|
return QString("%1 ns").arg(v.toULongLong());
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MinMaxBlockIndexRole:
|
||||||
|
{
|
||||||
|
auto v = Parent::data(_column, _role);
|
||||||
|
if (!v.isNull() || parent() == nullptr)
|
||||||
|
return v;
|
||||||
|
return QVariant::fromValue(m_block);
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
if (_role != Qt::UserRole && _role != Qt::DisplayRole)
|
auto v = Parent::data(_column, _role);
|
||||||
return QTreeWidgetItem::data(_column, _role);
|
if (!v.isNull() || parent() == nullptr || (_role != Qt::UserRole && _role != Qt::DisplayRole))
|
||||||
|
return v;
|
||||||
return relevantData(_column, _role);
|
return relevantData(_column, _role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,7 +263,7 @@ QVariant TreeWidgetItem::relevantData(int _column, int _role) const
|
|||||||
case COL_PERCENT_PER_AREA:
|
case COL_PERCENT_PER_AREA:
|
||||||
case COL_PERCENT_SUM_PER_THREAD:
|
case COL_PERCENT_SUM_PER_THREAD:
|
||||||
{
|
{
|
||||||
return Parent::data(_column, _role);
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -278,10 +272,9 @@ QVariant TreeWidgetItem::relevantData(int _column, int _role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto var = Parent::data(_column, _role);
|
if (EASY_GLOBALS.display_only_relevant_stats && _role == Qt::DisplayRole)
|
||||||
if (!var.isNull() || (EASY_GLOBALS.display_only_relevant_stats && _role == Qt::DisplayRole))
|
|
||||||
{
|
{
|
||||||
return var;
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_column)
|
switch (_column)
|
||||||
@ -302,23 +295,27 @@ QVariant TreeWidgetItem::relevantData(int _column, int _role) const
|
|||||||
case COL_AVG_PER_FRAME:
|
case COL_AVG_PER_FRAME:
|
||||||
case COL_AVG_PER_THREAD:
|
case COL_AVG_PER_THREAD:
|
||||||
case COL_AVG_PER_AREA:
|
case COL_AVG_PER_AREA:
|
||||||
|
case COL_MEDIAN_PER_PARENT:
|
||||||
|
case COL_MEDIAN_PER_FRAME:
|
||||||
|
case COL_MEDIAN_PER_THREAD:
|
||||||
|
case COL_MEDIAN_PER_AREA:
|
||||||
{
|
{
|
||||||
return Parent::data(COL_TIME, _role);
|
return data(COL_TIME, _role);
|
||||||
}
|
}
|
||||||
|
|
||||||
case COL_PERCENT_SUM_PER_PARENT:
|
case COL_PERCENT_SUM_PER_PARENT:
|
||||||
{
|
{
|
||||||
return Parent::data(COL_PERCENT_PER_PARENT, _role);
|
return data(COL_PERCENT_PER_PARENT, _role);
|
||||||
}
|
}
|
||||||
|
|
||||||
case COL_PERCENT_SUM_PER_FRAME:
|
case COL_PERCENT_SUM_PER_FRAME:
|
||||||
{
|
{
|
||||||
return Parent::data(COL_PERCENT_PER_FRAME, _role);
|
return data(COL_PERCENT_PER_FRAME, _role);
|
||||||
}
|
}
|
||||||
|
|
||||||
case COL_PERCENT_SUM_PER_AREA:
|
case COL_PERCENT_SUM_PER_AREA:
|
||||||
{
|
{
|
||||||
return Parent::data(COL_PERCENT_PER_AREA, _role);
|
return data(COL_PERCENT_PER_AREA, _role);
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -327,7 +324,7 @@ QVariant TreeWidgetItem::relevantData(int _column, int _role) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return var;
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant TreeWidgetItem::partialForeground() const
|
QVariant TreeWidgetItem::partialForeground() const
|
||||||
@ -384,24 +381,12 @@ profiler::thread_id_t TreeWidgetItem::threadId() const
|
|||||||
return static_cast<profiler::thread_id_t>(parentItem->data(COL_NAME, Qt::UserRole).toULongLong());
|
return static_cast<profiler::thread_id_t>(parentItem->data(COL_NAME, Qt::UserRole).toULongLong());
|
||||||
}
|
}
|
||||||
|
|
||||||
profiler::timestamp_t TreeWidgetItem::duration() const
|
|
||||||
{
|
|
||||||
if (parent() != nullptr)
|
|
||||||
return block().node->duration();
|
|
||||||
return data(COL_TIME, Qt::UserRole).toULongLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
profiler::timestamp_t TreeWidgetItem::selfDuration() const
|
|
||||||
{
|
|
||||||
return data(COL_SELF_TIME, Qt::UserRole).toULongLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TreeWidgetItem::setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time, const QString& _prefix)
|
void TreeWidgetItem::setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time, const QString& _prefix)
|
||||||
{
|
{
|
||||||
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
||||||
|
|
||||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||||
setHasToolTip(_column);
|
//setHasToolTip(_column);
|
||||||
setText(_column, QString("%1%2").arg(_prefix).arg(profiler_gui::timeStringRealNs(_units, nanosecondsTime, 3)));
|
setText(_column, QString("%1%2").arg(_prefix).arg(profiler_gui::timeStringRealNs(_units, nanosecondsTime, 3)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,7 +395,7 @@ void TreeWidgetItem::setTimeSmart(int _column, profiler_gui::TimeUnits _units, c
|
|||||||
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
||||||
|
|
||||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||||
setHasToolTip(_column);
|
//setHasToolTip(_column);
|
||||||
setText(_column, profiler_gui::timeStringRealNs(_units, nanosecondsTime, 3));
|
setText(_column, profiler_gui::timeStringRealNs(_units, nanosecondsTime, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +403,7 @@ void TreeWidgetItem::setTimeMs(int _column, const profiler::timestamp_t& _time)
|
|||||||
{
|
{
|
||||||
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
||||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||||
setHasToolTip(_column);
|
//setHasToolTip(_column);
|
||||||
setText(_column, QString::number(double(nanosecondsTime) * 1e-6, 'g', 9));
|
setText(_column, QString::number(double(nanosecondsTime) * 1e-6, 'g', 9));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -426,7 +411,7 @@ void TreeWidgetItem::setTimeMs(int _column, const profiler::timestamp_t& _time,
|
|||||||
{
|
{
|
||||||
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
||||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||||
setHasToolTip(_column);
|
//setHasToolTip(_column);
|
||||||
setText(_column, QString("%1%2").arg(_prefix).arg(double(nanosecondsTime) * 1e-6, 0, 'g', 9));
|
setText(_column, QString("%1%2").arg(_prefix).arg(double(nanosecondsTime) * 1e-6, 0, 'g', 9));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,6 @@
|
|||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
#include <QStyledItemDelegate>
|
#include <QStyledItemDelegate>
|
||||||
#include <easy/reader.h>
|
#include <easy/reader.h>
|
||||||
#include <bitset>
|
|
||||||
|
|
||||||
#include "common_functions.h"
|
#include "common_functions.h"
|
||||||
|
|
||||||
@ -69,6 +68,10 @@ class BlocksTreeWidget;
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
EASY_CONSTEXPR int COLUMNS_VERSION = 3;
|
||||||
|
EASY_CONSTEXPR int BlockColorRole = Qt::UserRole + 1;
|
||||||
|
EASY_CONSTEXPR int MinMaxBlockIndexRole = Qt::UserRole + 2;
|
||||||
|
|
||||||
enum EasyColumnsIndexes
|
enum EasyColumnsIndexes
|
||||||
{
|
{
|
||||||
COL_UNKNOWN = -1,
|
COL_UNKNOWN = -1,
|
||||||
@ -79,32 +82,34 @@ enum EasyColumnsIndexes
|
|||||||
|
|
||||||
COL_TIME,
|
COL_TIME,
|
||||||
COL_SELF_TIME,
|
COL_SELF_TIME,
|
||||||
COL_TOTAL_TIME_PER_PARENT,
|
|
||||||
COL_TOTAL_TIME_PER_FRAME,
|
|
||||||
COL_TOTAL_TIME_PER_THREAD,
|
|
||||||
|
|
||||||
COL_SELF_TIME_PERCENT,
|
COL_SELF_TIME_PERCENT,
|
||||||
COL_PERCENT_PER_PARENT,
|
|
||||||
COL_PERCENT_PER_FRAME,
|
|
||||||
COL_PERCENT_SUM_PER_PARENT,
|
|
||||||
COL_PERCENT_SUM_PER_FRAME,
|
|
||||||
COL_PERCENT_SUM_PER_THREAD,
|
|
||||||
|
|
||||||
COL_END,
|
COL_END,
|
||||||
|
|
||||||
|
COL_PERCENT_PER_FRAME,
|
||||||
|
COL_TOTAL_TIME_PER_FRAME,
|
||||||
|
COL_PERCENT_SUM_PER_FRAME,
|
||||||
COL_MIN_PER_FRAME,
|
COL_MIN_PER_FRAME,
|
||||||
COL_MAX_PER_FRAME,
|
COL_MAX_PER_FRAME,
|
||||||
COL_AVG_PER_FRAME,
|
COL_AVG_PER_FRAME,
|
||||||
|
COL_MEDIAN_PER_FRAME,
|
||||||
COL_NCALLS_PER_FRAME,
|
COL_NCALLS_PER_FRAME,
|
||||||
|
|
||||||
|
COL_TOTAL_TIME_PER_THREAD,
|
||||||
|
COL_PERCENT_SUM_PER_THREAD,
|
||||||
COL_MIN_PER_THREAD,
|
COL_MIN_PER_THREAD,
|
||||||
COL_MAX_PER_THREAD,
|
COL_MAX_PER_THREAD,
|
||||||
COL_AVG_PER_THREAD,
|
COL_AVG_PER_THREAD,
|
||||||
|
COL_MEDIAN_PER_THREAD,
|
||||||
COL_NCALLS_PER_THREAD,
|
COL_NCALLS_PER_THREAD,
|
||||||
|
|
||||||
|
COL_PERCENT_PER_PARENT,
|
||||||
|
COL_TOTAL_TIME_PER_PARENT,
|
||||||
|
COL_PERCENT_SUM_PER_PARENT,
|
||||||
COL_MIN_PER_PARENT,
|
COL_MIN_PER_PARENT,
|
||||||
COL_MAX_PER_PARENT,
|
COL_MAX_PER_PARENT,
|
||||||
COL_AVG_PER_PARENT,
|
COL_AVG_PER_PARENT,
|
||||||
|
COL_MEDIAN_PER_PARENT,
|
||||||
COL_NCALLS_PER_PARENT,
|
COL_NCALLS_PER_PARENT,
|
||||||
|
|
||||||
COL_ACTIVE_TIME,
|
COL_ACTIVE_TIME,
|
||||||
@ -116,6 +121,7 @@ enum EasyColumnsIndexes
|
|||||||
COL_MIN_PER_AREA,
|
COL_MIN_PER_AREA,
|
||||||
COL_MAX_PER_AREA,
|
COL_MAX_PER_AREA,
|
||||||
COL_AVG_PER_AREA,
|
COL_AVG_PER_AREA,
|
||||||
|
COL_MEDIAN_PER_AREA,
|
||||||
COL_NCALLS_PER_AREA,
|
COL_NCALLS_PER_AREA,
|
||||||
|
|
||||||
COL_COLUMNS_NUMBER
|
COL_COLUMNS_NUMBER
|
||||||
@ -130,7 +136,6 @@ class TreeWidgetItem : public QTreeWidgetItem
|
|||||||
|
|
||||||
const profiler::block_index_t m_block;
|
const profiler::block_index_t m_block;
|
||||||
QRgb m_customBGColor;
|
QRgb m_customBGColor;
|
||||||
std::bitset<21> m_bHasToolTip;
|
|
||||||
bool m_bMain;
|
bool m_bMain;
|
||||||
bool m_partial;
|
bool m_partial;
|
||||||
|
|
||||||
@ -147,14 +152,10 @@ public:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
bool isPartial() const;
|
bool isPartial() const;
|
||||||
bool hasToolTip(int _column) const;
|
|
||||||
profiler::block_index_t block_index() const;
|
profiler::block_index_t block_index() const;
|
||||||
profiler_gui::EasyBlock& guiBlock();
|
profiler_gui::EasyBlock& guiBlock();
|
||||||
const profiler::BlocksTree& block() const;
|
const profiler::BlocksTree& block() const;
|
||||||
|
|
||||||
profiler::timestamp_t duration() const;
|
|
||||||
profiler::timestamp_t selfDuration() const;
|
|
||||||
|
|
||||||
profiler::thread_id_t threadId() const;
|
profiler::thread_id_t threadId() const;
|
||||||
|
|
||||||
void setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time, const QString& _prefix);
|
void setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time, const QString& _prefix);
|
||||||
@ -174,7 +175,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void setHasToolTip(int _column);
|
//void setHasToolTip(int _column);
|
||||||
QVariant relevantData(int _column, int _role) const;
|
QVariant relevantData(int _column, int _role) const;
|
||||||
QVariant partialForeground() const;
|
QVariant partialForeground() const;
|
||||||
|
|
||||||
|
@ -54,10 +54,13 @@
|
|||||||
* : limitations under the License.
|
* : limitations under the License.
|
||||||
************************************************************************/
|
************************************************************************/
|
||||||
|
|
||||||
#include "tree_widget_loader.h"
|
#include <map>
|
||||||
#include "tree_widget_item.h"
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "thread_pool.h"
|
#include "thread_pool.h"
|
||||||
|
#include "tree_widget_item.h"
|
||||||
|
|
||||||
|
#include "tree_widget_loader.h"
|
||||||
|
|
||||||
#ifdef max
|
#ifdef max
|
||||||
#undef max
|
#undef max
|
||||||
@ -75,15 +78,94 @@
|
|||||||
#define EASY_INIT_ATOMIC(v) {v}
|
#define EASY_INIT_ATOMIC(v) {v}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
struct ThreadData
|
struct ThreadData
|
||||||
{
|
{
|
||||||
StatsMap stats;
|
StatsMap stats;
|
||||||
IdItems iditems;
|
IdItems iditems;
|
||||||
TreeWidgetItem* item = nullptr;
|
TreeWidgetItem* item = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
using ThreadDataMap = std::unordered_map<profiler::thread_id_t, ThreadData, estd::hash<profiler::thread_id_t> >;
|
using ThreadDataMap = std::unordered_map<profiler::thread_id_t, ThreadData, estd::hash<profiler::thread_id_t> >;
|
||||||
|
|
||||||
|
void calculate_medians(StatsMap::iterator begin, StatsMap::iterator end)
|
||||||
|
{
|
||||||
|
for (auto it = begin; it != end; ++it)
|
||||||
|
{
|
||||||
|
auto& durations = it->second.durations;
|
||||||
|
if (durations.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total_count = 0;
|
||||||
|
for (auto& kv : durations)
|
||||||
|
{
|
||||||
|
total_count += kv.second.count;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& stats = it->second.stats;
|
||||||
|
if (total_count & 1)
|
||||||
|
{
|
||||||
|
const auto index = total_count >> 1;
|
||||||
|
size_t i = 0;
|
||||||
|
for (auto& kv : durations)
|
||||||
|
{
|
||||||
|
const auto count = kv.second.count;
|
||||||
|
|
||||||
|
i += count;
|
||||||
|
if (i < index)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.median_duration = kv.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto index2 = total_count >> 1;
|
||||||
|
const auto index1 = index2 - 1;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
bool i1 = false;
|
||||||
|
for (auto& kv : durations)
|
||||||
|
{
|
||||||
|
const auto count = kv.second.count;
|
||||||
|
|
||||||
|
i += count;
|
||||||
|
if (i < index1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i1)
|
||||||
|
{
|
||||||
|
i1 = true;
|
||||||
|
stats.median_duration = kv.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i < index2)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.median_duration += kv.first;
|
||||||
|
stats.median_duration >>= 1;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(it->second.durations) dummy;
|
||||||
|
dummy.swap(durations);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of namespace <noname>.
|
||||||
|
|
||||||
static void fillStatsColumns(
|
static void fillStatsColumns(
|
||||||
TreeWidgetItem* item,
|
TreeWidgetItem* item,
|
||||||
const profiler::BlockStatistics* stats,
|
const profiler::BlockStatistics* stats,
|
||||||
@ -91,12 +173,19 @@ static void fillStatsColumns(
|
|||||||
int min_column,
|
int min_column,
|
||||||
int max_column,
|
int max_column,
|
||||||
int avg_column,
|
int avg_column,
|
||||||
|
int median_column,
|
||||||
int total_column,
|
int total_column,
|
||||||
int n_calls_column
|
int n_calls_column
|
||||||
) {
|
) {
|
||||||
item->setData(n_calls_column, Qt::UserRole, stats->calls_number);
|
item->setData(n_calls_column, Qt::UserRole, stats->calls_number);
|
||||||
item->setText(n_calls_column, QString::number(stats->calls_number));
|
item->setText(n_calls_column, QString::number(stats->calls_number));
|
||||||
|
|
||||||
|
if (min_column == COL_MIN_PER_AREA)
|
||||||
|
{
|
||||||
|
item->setData(min_column, MinMaxBlockIndexRole, stats->min_duration_block);
|
||||||
|
item->setData(max_column, MinMaxBlockIndexRole, stats->max_duration_block);
|
||||||
|
}
|
||||||
|
|
||||||
if (stats->calls_number < 2)// && EASY_GLOBALS.display_only_relevant_stats)
|
if (stats->calls_number < 2)// && EASY_GLOBALS.display_only_relevant_stats)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -106,21 +195,23 @@ static void fillStatsColumns(
|
|||||||
const auto max_duration = easyBlock(stats->max_duration_block).tree.node->duration();
|
const auto max_duration = easyBlock(stats->max_duration_block).tree.node->duration();
|
||||||
const auto avg_duration = stats->average_duration();
|
const auto avg_duration = stats->average_duration();
|
||||||
const auto tot_duration = stats->total_duration;
|
const auto tot_duration = stats->total_duration;
|
||||||
|
const auto median_duration = stats->median_duration;
|
||||||
|
|
||||||
item->setTimeSmart(min_column, units, min_duration);
|
item->setTimeSmart(min_column, units, min_duration);
|
||||||
item->setTimeSmart(max_column, units, max_duration);
|
item->setTimeSmart(max_column, units, max_duration);
|
||||||
item->setTimeSmart(avg_column, units, avg_duration);
|
item->setTimeSmart(avg_column, units, avg_duration);
|
||||||
|
item->setTimeSmart(median_column, units, median_duration);
|
||||||
item->setTimeSmart(total_column, units, tot_duration);
|
item->setTimeSmart(total_column, units, tot_duration);
|
||||||
|
|
||||||
if (stats->calls_number > 1 && tot_duration != 0)
|
if (stats->calls_number > 1 && tot_duration != 0)
|
||||||
{
|
{
|
||||||
if (max_duration >= (tot_duration >> 1))
|
if (max_duration >= (tot_duration >> 1))
|
||||||
{
|
{
|
||||||
item->setForeground(max_column, QColor::fromRgb(profiler::colors::Red500));
|
item->setForeground(max_column, QColor::fromRgb(profiler::colors::RedA700));
|
||||||
}
|
}
|
||||||
else if (max_duration >= (tot_duration >> 2))
|
else if (max_duration >= (tot_duration >> 2))
|
||||||
{
|
{
|
||||||
item->setForeground(max_column, QColor::fromRgb(profiler::colors::Red700));
|
item->setForeground(max_column, QColor::fromRgb(profiler::colors::OrangeA400));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,6 +225,7 @@ inline void fillStatsColumnsThread(TreeWidgetItem* item, const profiler::BlockSt
|
|||||||
COL_MIN_PER_THREAD,
|
COL_MIN_PER_THREAD,
|
||||||
COL_MAX_PER_THREAD,
|
COL_MAX_PER_THREAD,
|
||||||
COL_AVG_PER_THREAD,
|
COL_AVG_PER_THREAD,
|
||||||
|
COL_MEDIAN_PER_THREAD,
|
||||||
COL_TOTAL_TIME_PER_THREAD,
|
COL_TOTAL_TIME_PER_THREAD,
|
||||||
COL_NCALLS_PER_THREAD
|
COL_NCALLS_PER_THREAD
|
||||||
);
|
);
|
||||||
@ -148,6 +240,7 @@ inline void fillStatsColumnsFrame(TreeWidgetItem* item, const profiler::BlockSta
|
|||||||
COL_MIN_PER_FRAME,
|
COL_MIN_PER_FRAME,
|
||||||
COL_MAX_PER_FRAME,
|
COL_MAX_PER_FRAME,
|
||||||
COL_AVG_PER_FRAME,
|
COL_AVG_PER_FRAME,
|
||||||
|
COL_MEDIAN_PER_FRAME,
|
||||||
COL_TOTAL_TIME_PER_FRAME,
|
COL_TOTAL_TIME_PER_FRAME,
|
||||||
COL_NCALLS_PER_FRAME
|
COL_NCALLS_PER_FRAME
|
||||||
);
|
);
|
||||||
@ -162,6 +255,7 @@ inline void fillStatsColumnsParent(TreeWidgetItem* item, const profiler::BlockSt
|
|||||||
COL_MIN_PER_PARENT,
|
COL_MIN_PER_PARENT,
|
||||||
COL_MAX_PER_PARENT,
|
COL_MAX_PER_PARENT,
|
||||||
COL_AVG_PER_PARENT,
|
COL_AVG_PER_PARENT,
|
||||||
|
COL_MEDIAN_PER_PARENT,
|
||||||
COL_TOTAL_TIME_PER_PARENT,
|
COL_TOTAL_TIME_PER_PARENT,
|
||||||
COL_NCALLS_PER_PARENT
|
COL_NCALLS_PER_PARENT
|
||||||
);
|
);
|
||||||
@ -176,13 +270,15 @@ inline void fillStatsColumnsSelection(TreeWidgetItem* item, const profiler::Bloc
|
|||||||
COL_MIN_PER_AREA,
|
COL_MIN_PER_AREA,
|
||||||
COL_MAX_PER_AREA,
|
COL_MAX_PER_AREA,
|
||||||
COL_AVG_PER_AREA,
|
COL_AVG_PER_AREA,
|
||||||
|
COL_MEDIAN_PER_AREA,
|
||||||
COL_TOTAL_TIME_PER_AREA,
|
COL_TOTAL_TIME_PER_AREA,
|
||||||
COL_NCALLS_PER_AREA
|
COL_NCALLS_PER_AREA
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
TreeWidgetLoader::TreeWidgetLoader()
|
TreeWidgetLoader::TreeWidgetLoader()
|
||||||
: m_bDone(EASY_INIT_ATOMIC(false))
|
: m_worker(true)
|
||||||
|
, m_bDone(EASY_INIT_ATOMIC(false))
|
||||||
, m_bInterrupt(EASY_INIT_ATOMIC(false))
|
, m_bInterrupt(EASY_INIT_ATOMIC(false))
|
||||||
, m_progress(EASY_INIT_ATOMIC(0))
|
, m_progress(EASY_INIT_ATOMIC(0))
|
||||||
, m_mode(TreeMode::Full)
|
, m_mode(TreeMode::Full)
|
||||||
@ -238,6 +334,11 @@ void TreeWidgetLoader::takeItems(Items& _output)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString TreeWidgetLoader::error() const
|
||||||
|
{
|
||||||
|
return done() ? m_error : QString();
|
||||||
|
}
|
||||||
|
|
||||||
void TreeWidgetLoader::interrupt(bool _wait)
|
void TreeWidgetLoader::interrupt(bool _wait)
|
||||||
{
|
{
|
||||||
m_worker.dequeue();
|
m_worker.dequeue();
|
||||||
@ -248,21 +349,28 @@ void TreeWidgetLoader::interrupt(bool _wait)
|
|||||||
{
|
{
|
||||||
if (!_wait)
|
if (!_wait)
|
||||||
{
|
{
|
||||||
auto items = std::move(m_topLevelItems);
|
auto topLevelItems = std::move(m_topLevelItems);
|
||||||
ThreadPool::instance().backgroundJob([=] {
|
|
||||||
|
#ifdef EASY_LAMBDA_MOVE_CAPTURE
|
||||||
|
ThreadPool::instance().backgroundJob([items = std::move(topLevelItems)] {
|
||||||
for (auto item : items)
|
for (auto item : items)
|
||||||
delete item.second;
|
#else
|
||||||
|
ThreadPool::instance().backgroundJob([=] {
|
||||||
|
for (auto item : topLevelItems)
|
||||||
|
#endif
|
||||||
|
profiler_gui::deleteTreeItem(item.second);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (auto item : m_topLevelItems)
|
for (auto item : m_topLevelItems)
|
||||||
delete item.second;
|
profiler_gui::deleteTreeItem(item.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_items.clear();
|
m_items.clear();
|
||||||
m_topLevelItems.clear();
|
m_topLevelItems.clear();
|
||||||
|
m_error.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TreeWidgetLoader::fillTreeBlocks(
|
void TreeWidgetLoader::fillTreeBlocks(
|
||||||
@ -280,6 +388,7 @@ void TreeWidgetLoader::fillTreeBlocks(
|
|||||||
const auto decoratedNames = EASY_GLOBALS.use_decorated_thread_name;
|
const auto decoratedNames = EASY_GLOBALS.use_decorated_thread_name;
|
||||||
const auto hexThreadIds = EASY_GLOBALS.hex_thread_id;
|
const auto hexThreadIds = EASY_GLOBALS.hex_thread_id;
|
||||||
const auto timeUnits = EASY_GLOBALS.time_units;
|
const auto timeUnits = EASY_GLOBALS.time_units;
|
||||||
|
const auto maxCount = EASY_GLOBALS.max_rows_count;
|
||||||
const auto blocks = std::ref(_blocks);
|
const auto blocks = std::ref(_blocks);
|
||||||
const auto mode = m_mode;
|
const auto mode = m_mode;
|
||||||
m_worker.enqueue([=] {
|
m_worker.enqueue([=] {
|
||||||
@ -287,17 +396,48 @@ void TreeWidgetLoader::fillTreeBlocks(
|
|||||||
{
|
{
|
||||||
case TreeMode::Full:
|
case TreeMode::Full:
|
||||||
{
|
{
|
||||||
setTreeInternalTop(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits);
|
setTreeInternalTop(
|
||||||
|
_beginTime,
|
||||||
|
blocks,
|
||||||
|
_left,
|
||||||
|
_right,
|
||||||
|
_strict,
|
||||||
|
zeroBlocks,
|
||||||
|
decoratedNames,
|
||||||
|
hexThreadIds,
|
||||||
|
timeUnits,
|
||||||
|
maxCount
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TreeMode::Plain:
|
case TreeMode::Plain:
|
||||||
{
|
{
|
||||||
setTreeInternalPlainTop(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits);
|
setTreeInternalPlainTop(
|
||||||
|
_beginTime,
|
||||||
|
blocks,
|
||||||
|
_left,
|
||||||
|
_right,
|
||||||
|
_strict,
|
||||||
|
zeroBlocks,
|
||||||
|
decoratedNames,
|
||||||
|
hexThreadIds,
|
||||||
|
timeUnits
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TreeMode::SelectedArea:
|
case TreeMode::SelectedArea:
|
||||||
{
|
{
|
||||||
setTreeInternalAggregateTop(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits);
|
setTreeInternalAggregateTop(
|
||||||
|
_beginTime,
|
||||||
|
blocks,
|
||||||
|
_left,
|
||||||
|
_right,
|
||||||
|
_strict,
|
||||||
|
zeroBlocks,
|
||||||
|
decoratedNames,
|
||||||
|
hexThreadIds,
|
||||||
|
timeUnits
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -306,14 +446,6 @@ void TreeWidgetLoader::fillTreeBlocks(
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// auto calculateTotalChildrenNumber(const profiler::BlocksTree& _tree) -> decltype(_tree.children.size())
|
|
||||||
// {
|
|
||||||
// auto children_number = _tree.children.size();
|
|
||||||
// for (auto i : _tree.children)
|
|
||||||
// children_number += calculateTotalChildrenNumber(easyBlocksTree(i));
|
|
||||||
// return children_number;
|
|
||||||
// }
|
|
||||||
|
|
||||||
using BeginEndIndicesMap = std::unordered_map<profiler::thread_id_t, profiler::block_index_t,
|
using BeginEndIndicesMap = std::unordered_map<profiler::thread_id_t, profiler::block_index_t,
|
||||||
::estd::hash<profiler::thread_id_t> >;
|
::estd::hash<profiler::thread_id_t> >;
|
||||||
|
|
||||||
@ -326,13 +458,79 @@ void TreeWidgetLoader::setTreeInternalTop(
|
|||||||
bool _addZeroBlocks,
|
bool _addZeroBlocks,
|
||||||
bool _decoratedThreadNames,
|
bool _decoratedThreadNames,
|
||||||
bool _hexThreadId,
|
bool _hexThreadId,
|
||||||
profiler_gui::TimeUnits _units
|
profiler_gui::TimeUnits _units,
|
||||||
|
size_t _maxCount
|
||||||
) {
|
) {
|
||||||
BeginEndIndicesMap beginEndMap;
|
BeginEndIndicesMap beginEndMap;
|
||||||
ThreadDataMap threadsMap;
|
ThreadDataMap threadsMap;
|
||||||
|
|
||||||
|
auto total = static_cast<int>(_blocks.size());
|
||||||
|
|
||||||
|
uint32_t total_count = 0;
|
||||||
|
int i = 0;
|
||||||
|
for (const auto& block : _blocks)
|
||||||
|
{
|
||||||
|
if (interrupted())
|
||||||
|
{
|
||||||
|
setDone();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& gui_block = easyBlock(block.tree);
|
||||||
|
const auto& tree = gui_block.tree;
|
||||||
|
const auto startTime = tree.node->begin();
|
||||||
|
const auto endTime = tree.node->end();
|
||||||
|
|
||||||
|
if (startTime > _right || endTime < _left)
|
||||||
|
{
|
||||||
|
setProgress((3 * ++i) / total);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profiler::timestamp_t duration = endTime - startTime;
|
||||||
|
const bool partial = _strict && (startTime < _left || endTime > _right);
|
||||||
|
if (partial && duration != 0 && (startTime == _right || endTime == _left))
|
||||||
|
{
|
||||||
|
setProgress((3 * ++i) / total);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
total_count += calculateChildrenCountRecursive(tree.children, startTime, endTime, _strict, partial, _addZeroBlocks) + 1;
|
||||||
|
|
||||||
|
setProgress((3 * ++i) / total);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_count > _maxCount)
|
||||||
|
{
|
||||||
|
if (_maxCount > 10000)
|
||||||
|
{
|
||||||
|
m_error = QString(
|
||||||
|
"Exceeded maximum rows count = %1k.\n"
|
||||||
|
"Actual rows count: %2k (%3%).\n"
|
||||||
|
"Please, reduce selected area width\n"
|
||||||
|
"or increase maximum count in settings\n"
|
||||||
|
"or change the tree mode."
|
||||||
|
).arg(_maxCount / 1000).arg(total_count / 1000).arg(profiler_gui::percent(total_count, _maxCount));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_error = QString(
|
||||||
|
"Exceeded maximum rows count = %1.\n"
|
||||||
|
"Actual rows count: %2 (%3%).\n"
|
||||||
|
"Please, reduce selected area width\n"
|
||||||
|
"or increase maximum count in settings\n"
|
||||||
|
"or change the tree mode."
|
||||||
|
).arg(_maxCount).arg(total_count).arg(profiler_gui::percent(total_count, _maxCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
setDone();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const auto u_thread = profiler_gui::toUnicode("thread");
|
const auto u_thread = profiler_gui::toUnicode("thread");
|
||||||
int i = 0, total = static_cast<int>(_blocks.size());
|
|
||||||
|
i = 0;
|
||||||
for (const auto& block : _blocks)
|
for (const auto& block : _blocks)
|
||||||
{
|
{
|
||||||
if (interrupted())
|
if (interrupted())
|
||||||
@ -345,7 +543,7 @@ void TreeWidgetLoader::setTreeInternalTop(
|
|||||||
|
|
||||||
if (startTime > _right || endTime < _left)
|
if (startTime > _right || endTime < _left)
|
||||||
{
|
{
|
||||||
setProgress((95 * ++i) / total);
|
setProgress(3 + (92 * ++i) / total);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,7 +552,7 @@ void TreeWidgetLoader::setTreeInternalTop(
|
|||||||
const bool partial = _strict && (startTime < _left || endTime > _right);
|
const bool partial = _strict && (startTime < _left || endTime > _right);
|
||||||
if (partial && duration != 0 && (startTime == _right || endTime == _left))
|
if (partial && duration != 0 && (startTime == _right || endTime == _left))
|
||||||
{
|
{
|
||||||
setProgress((95 * ++i) / total);
|
setProgress(3 + (92 * ++i) / total);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -465,7 +663,7 @@ void TreeWidgetLoader::setTreeInternalTop(
|
|||||||
if (partial && children_items_number == 0)
|
if (partial && children_items_number == 0)
|
||||||
{
|
{
|
||||||
delete item;
|
delete item;
|
||||||
setProgress((95 * ++i) / total);
|
setProgress(3 + (92 * ++i) / total);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -481,14 +679,14 @@ void TreeWidgetLoader::setTreeInternalTop(
|
|||||||
item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage);
|
item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, percentage);
|
||||||
item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage));
|
item->setText(COL_SELF_TIME_PERCENT, QString::number(percentage));
|
||||||
|
|
||||||
//total_items += children_items_number + 1;
|
updateStats(stats, tree.node->id(), block.tree, duration, children_duration);
|
||||||
|
|
||||||
if (gui_block.expanded)
|
if (gui_block.expanded)
|
||||||
item->setExpanded(true);
|
item->setExpanded(true);
|
||||||
|
|
||||||
m_items.insert(std::make_pair(block.tree, item));
|
m_items.insert(std::make_pair(block.tree, item));
|
||||||
|
|
||||||
setProgress((95 * ++i) / total);
|
setProgress(3 + (92 * ++i) / total);
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
@ -1088,7 +1286,7 @@ size_t TreeWidgetLoader::setTreeInternal(
|
|||||||
const auto per_parent_stats = child.per_parent_stats;
|
const auto per_parent_stats = child.per_parent_stats;
|
||||||
const auto per_frame_stats = child.per_frame_stats;
|
const auto per_frame_stats = child.per_frame_stats;
|
||||||
|
|
||||||
auto parent_duration = _parent->duration();
|
auto parent_duration = _parent->data(COL_TIME, Qt::UserRole).toULongLong();
|
||||||
auto percentage = duration == 0 ? 0 : profiler_gui::percent(duration, parent_duration);
|
auto percentage = duration == 0 ? 0 : profiler_gui::percent(duration, parent_duration);
|
||||||
auto percentage_sum = profiler_gui::percent(per_parent_stats->total_duration, parent_duration);
|
auto percentage_sum = profiler_gui::percent(per_parent_stats->total_duration, parent_duration);
|
||||||
item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage);
|
item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage);
|
||||||
@ -1100,7 +1298,7 @@ size_t TreeWidgetLoader::setTreeInternal(
|
|||||||
{
|
{
|
||||||
if (_parent != _frame)
|
if (_parent != _frame)
|
||||||
{
|
{
|
||||||
parent_duration = _frame->duration();
|
parent_duration = _frame->data(COL_TIME, Qt::UserRole).toULongLong();
|
||||||
percentage = duration == 0 ? 0 : profiler_gui::percent(duration, parent_duration);
|
percentage = duration == 0 ? 0 : profiler_gui::percent(duration, parent_duration);
|
||||||
percentage_sum = profiler_gui::percent(per_frame_stats->total_duration, parent_duration);
|
percentage_sum = profiler_gui::percent(per_frame_stats->total_duration, parent_duration);
|
||||||
}
|
}
|
||||||
@ -1194,8 +1392,10 @@ size_t TreeWidgetLoader::setTreeInternal(
|
|||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
profiler::timestamp_t TreeWidgetLoader::calculateChildrenDurationRecursive(const profiler::BlocksTree::children_t& _children, profiler::block_id_t _id)
|
profiler::timestamp_t TreeWidgetLoader::calculateChildrenDurationRecursive(
|
||||||
{
|
const profiler::BlocksTree::children_t& _children,
|
||||||
|
profiler::block_id_t _id
|
||||||
|
) const {
|
||||||
profiler::timestamp_t total_duration = 0;
|
profiler::timestamp_t total_duration = 0;
|
||||||
|
|
||||||
for (auto child_index : _children)
|
for (auto child_index : _children)
|
||||||
@ -1212,6 +1412,45 @@ profiler::timestamp_t TreeWidgetLoader::calculateChildrenDurationRecursive(const
|
|||||||
return total_duration;
|
return total_duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t TreeWidgetLoader::calculateChildrenCountRecursive(
|
||||||
|
const profiler::BlocksTree::children_t& children,
|
||||||
|
profiler::timestamp_t left,
|
||||||
|
profiler::timestamp_t right,
|
||||||
|
bool strict,
|
||||||
|
bool partial_parent,
|
||||||
|
bool addZeroBlocks
|
||||||
|
) const {
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
for (auto child_index : children)
|
||||||
|
{
|
||||||
|
if (interrupted())
|
||||||
|
break;
|
||||||
|
|
||||||
|
const auto& gui_block = easyBlock(child_index);
|
||||||
|
const auto& child = gui_block.tree;
|
||||||
|
const auto startTime = child.node->begin();
|
||||||
|
const auto endTime = child.node->end();
|
||||||
|
const auto duration = endTime - startTime;
|
||||||
|
|
||||||
|
if (startTime > right || endTime < left)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const bool partial = strict && (startTime < left || endTime > right);
|
||||||
|
if (partial && partial_parent && duration != 0 && (startTime == right || endTime == left))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto& desc = easyDescriptor(child.node->id());
|
||||||
|
|
||||||
|
if (duration == 0 && !addZeroBlocks && desc.type() == profiler::BlockType::Block)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
count += calculateChildrenCountRecursive(gui_block.tree.children, left, right, strict, partial, addZeroBlocks) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
size_t TreeWidgetLoader::setTreeInternalPlain(
|
size_t TreeWidgetLoader::setTreeInternalPlain(
|
||||||
const profiler::BlocksTreeRoot& threadRoot,
|
const profiler::BlocksTreeRoot& threadRoot,
|
||||||
IdItems& iditems,
|
IdItems& iditems,
|
||||||
@ -1322,7 +1561,8 @@ size_t TreeWidgetLoader::setTreeInternalPlain(
|
|||||||
item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
|
item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
|
||||||
item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
|
item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
|
||||||
|
|
||||||
const auto percentage_sum = profiler_gui::percent(per_frame_stats->total_duration, frame->duration());
|
const auto frame_duration = frame->data(COL_TIME, Qt::UserRole).toULongLong();
|
||||||
|
const auto percentage_sum = profiler_gui::percent(per_frame_stats->total_duration, frame_duration);
|
||||||
item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage_sum);
|
item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage_sum);
|
||||||
item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage_sum));
|
item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage_sum));
|
||||||
item->setTimeSmart(COL_TIME, units, per_frame_stats->total_duration);
|
item->setTimeSmart(COL_TIME, units, per_frame_stats->total_duration);
|
||||||
@ -1603,12 +1843,15 @@ void TreeWidgetLoader::updateStats(
|
|||||||
auto stats_it = statsMap.find(id);
|
auto stats_it = statsMap.find(id);
|
||||||
if (stats_it == statsMap.end())
|
if (stats_it == statsMap.end())
|
||||||
{
|
{
|
||||||
stats_it = statsMap.emplace(id, profiler::BlockStatistics(duration, index, 0)).first;
|
stats_it = statsMap.emplace(id, loader::Stats(duration, index, 0)).first;
|
||||||
stats_it->second.total_children_duration = children_duration;
|
auto& stat = stats_it->second.stats;
|
||||||
|
stat.total_children_duration = children_duration;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto& stat = stats_it->second;
|
auto& stat = stats_it->second.stats;
|
||||||
|
auto& durations = stats_it->second.durations;
|
||||||
|
|
||||||
++stat.calls_number;
|
++stat.calls_number;
|
||||||
stat.total_duration += duration;
|
stat.total_duration += duration;
|
||||||
stat.total_children_duration += children_duration;
|
stat.total_children_duration += children_duration;
|
||||||
@ -1622,18 +1865,22 @@ void TreeWidgetLoader::updateStats(
|
|||||||
{
|
{
|
||||||
stat.min_duration_block = index;
|
stat.min_duration_block = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
++durations[duration].count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
void TreeWidgetLoader::fillStatsForTree(TreeWidgetItem* root, const StatsMap& stats, profiler_gui::TimeUnits _units, profiler::timestamp_t selectionDuration) const
|
void TreeWidgetLoader::fillStatsForTree(TreeWidgetItem* root, StatsMap& stats, profiler_gui::TimeUnits _units, profiler::timestamp_t selectionDuration) const
|
||||||
{
|
{
|
||||||
if (stats.empty())
|
if (stats.empty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
calculate_medians(stats.begin(), stats.end());
|
||||||
|
|
||||||
std::deque<TreeWidgetItem*> queue;
|
std::deque<TreeWidgetItem*> queue;
|
||||||
|
|
||||||
if (root->parent() != nullptr)
|
if (root->parent() != nullptr)
|
||||||
@ -1660,11 +1907,11 @@ void TreeWidgetLoader::fillStatsForTree(TreeWidgetItem* root, const StatsMap& st
|
|||||||
auto stat_it = stats.find(item->block().node->id());
|
auto stat_it = stats.find(item->block().node->id());
|
||||||
if (stat_it != stats.end())
|
if (stat_it != stats.end())
|
||||||
{
|
{
|
||||||
const auto& stat = stat_it->second;
|
auto& stat = stat_it->second;
|
||||||
|
|
||||||
fillStatsColumnsSelection(item, &stat, _units);
|
fillStatsColumnsSelection(item, &stat.stats, _units);
|
||||||
|
|
||||||
auto percent_per_selection = std::min(100, profiler_gui::percent(stat.total_duration, selectionDuration));
|
auto percent_per_selection = std::min(100, profiler_gui::percent(stat.stats.total_duration, selectionDuration));
|
||||||
item->setData(COL_PERCENT_SUM_PER_AREA, Qt::UserRole, percent_per_selection);
|
item->setData(COL_PERCENT_SUM_PER_AREA, Qt::UserRole, percent_per_selection);
|
||||||
item->setText(COL_PERCENT_SUM_PER_AREA, QString::number(percent_per_selection));
|
item->setText(COL_PERCENT_SUM_PER_AREA, QString::number(percent_per_selection));
|
||||||
|
|
||||||
|
@ -68,11 +68,29 @@
|
|||||||
|
|
||||||
class TreeWidgetItem;
|
class TreeWidgetItem;
|
||||||
|
|
||||||
|
namespace loader {
|
||||||
|
|
||||||
|
struct Stats
|
||||||
|
{
|
||||||
|
struct Counter { uint32_t count = 0; };
|
||||||
|
|
||||||
|
profiler::BlockStatistics stats;
|
||||||
|
std::map<profiler::timestamp_t, Counter> durations;
|
||||||
|
|
||||||
|
Stats(profiler::timestamp_t duration, profiler::block_index_t block_index, profiler::block_index_t parent_index)
|
||||||
|
: stats(duration, block_index, parent_index)
|
||||||
|
{
|
||||||
|
durations[duration].count = 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // end of namespace loader.
|
||||||
|
|
||||||
using Items = ::std::unordered_map<profiler::block_index_t, TreeWidgetItem*, ::estd::hash<profiler::block_index_t> >;
|
using Items = ::std::unordered_map<profiler::block_index_t, TreeWidgetItem*, ::estd::hash<profiler::block_index_t> >;
|
||||||
using ThreadedItems = std::vector<std::pair<profiler::thread_id_t, TreeWidgetItem*> >;
|
using ThreadedItems = std::vector<std::pair<profiler::thread_id_t, TreeWidgetItem*> >;
|
||||||
using RootsMap = std::unordered_map<profiler::thread_id_t, TreeWidgetItem*, estd::hash<profiler::thread_id_t> >;
|
using RootsMap = std::unordered_map<profiler::thread_id_t, TreeWidgetItem*, estd::hash<profiler::thread_id_t> >;
|
||||||
using IdItems = std::unordered_map<profiler::block_id_t, std::pair<TreeWidgetItem*, int>, estd::hash<profiler::block_index_t> >;
|
using IdItems = std::unordered_map<profiler::block_id_t, std::pair<TreeWidgetItem*, int>, estd::hash<profiler::block_index_t> >;
|
||||||
using StatsMap = std::unordered_map<profiler::block_id_t, profiler::BlockStatistics, estd::hash<profiler::block_id_t> >;
|
using StatsMap = std::unordered_map<profiler::block_id_t, loader::Stats, estd::hash<profiler::block_id_t> >;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -90,6 +108,7 @@ class TreeWidgetLoader Q_DECL_FINAL
|
|||||||
ThreadedItems m_topLevelItems; ///<
|
ThreadedItems m_topLevelItems; ///<
|
||||||
Items m_items; ///<
|
Items m_items; ///<
|
||||||
ThreadPoolTask m_worker; ///<
|
ThreadPoolTask m_worker; ///<
|
||||||
|
QString m_error; ///<
|
||||||
std::atomic_bool m_bDone; ///<
|
std::atomic_bool m_bDone; ///<
|
||||||
std::atomic_bool m_bInterrupt; ///<
|
std::atomic_bool m_bInterrupt; ///<
|
||||||
std::atomic<int> m_progress; ///<
|
std::atomic<int> m_progress; ///<
|
||||||
@ -106,6 +125,8 @@ public:
|
|||||||
void takeTopLevelItems(ThreadedItems& _output);
|
void takeTopLevelItems(ThreadedItems& _output);
|
||||||
void takeItems(Items& _output);
|
void takeItems(Items& _output);
|
||||||
|
|
||||||
|
QString error() const;
|
||||||
|
|
||||||
void interrupt(bool _wait = false);
|
void interrupt(bool _wait = false);
|
||||||
void fillTreeBlocks(
|
void fillTreeBlocks(
|
||||||
const::profiler_gui::TreeBlocks& _blocks,
|
const::profiler_gui::TreeBlocks& _blocks,
|
||||||
@ -130,7 +151,8 @@ private:
|
|||||||
bool _addZeroBlocks,
|
bool _addZeroBlocks,
|
||||||
bool _decoratedThreadNames,
|
bool _decoratedThreadNames,
|
||||||
bool _hexThreadId,
|
bool _hexThreadId,
|
||||||
::profiler_gui::TimeUnits _units
|
::profiler_gui::TimeUnits _units,
|
||||||
|
size_t _maxCount
|
||||||
);
|
);
|
||||||
|
|
||||||
size_t setTreeInternal(
|
size_t setTreeInternal(
|
||||||
@ -213,11 +235,23 @@ private:
|
|||||||
int depth
|
int depth
|
||||||
);
|
);
|
||||||
|
|
||||||
profiler::timestamp_t calculateChildrenDurationRecursive(const profiler::BlocksTree::children_t& _children, profiler::block_id_t _id);
|
profiler::timestamp_t calculateChildrenDurationRecursive(
|
||||||
|
const profiler::BlocksTree::children_t& _children,
|
||||||
|
profiler::block_id_t _id
|
||||||
|
) const;
|
||||||
|
|
||||||
|
uint32_t calculateChildrenCountRecursive(
|
||||||
|
const profiler::BlocksTree::children_t& children,
|
||||||
|
profiler::timestamp_t left,
|
||||||
|
profiler::timestamp_t right,
|
||||||
|
bool strict,
|
||||||
|
bool partial_parent,
|
||||||
|
bool addZeroBlocks
|
||||||
|
) const;
|
||||||
|
|
||||||
profiler::timestamp_t calculateIdleTime(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t& _firstCSwitch, profiler::timestamp_t _begin, profiler::timestamp_t _end) const;
|
profiler::timestamp_t calculateIdleTime(const profiler::BlocksTreeRoot& _threadRoot, profiler::block_index_t& _firstCSwitch, profiler::timestamp_t _begin, profiler::timestamp_t _end) const;
|
||||||
void updateStats(StatsMap& stats, profiler::block_id_t id, profiler::block_index_t index, profiler::timestamp_t duration, profiler::timestamp_t children_duration) const;
|
void updateStats(StatsMap& stats, profiler::block_id_t id, profiler::block_index_t index, profiler::timestamp_t duration, profiler::timestamp_t children_duration) const;
|
||||||
void fillStatsForTree(TreeWidgetItem* root, const StatsMap& stats, profiler_gui::TimeUnits _units, profiler::timestamp_t selectionDuration) const;
|
void fillStatsForTree(TreeWidgetItem* root, StatsMap& stats, profiler_gui::TimeUnits _units, profiler::timestamp_t selectionDuration) const;
|
||||||
|
|
||||||
}; // END of class TreeWidgetLoader.
|
}; // END of class TreeWidgetLoader.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user