mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-25 07:20: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)
|
||||
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)
|
||||
|
||||
set(EASY_PROGRAM_VERSION_MAJOR 2)
|
||||
|
@ -62,6 +62,17 @@
|
||||
# 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)
|
||||
@ -92,6 +103,10 @@
|
||||
# define EASY_NOEXCEPT throw()
|
||||
# endif
|
||||
|
||||
# if EASY_STD > 11 && _MSC_VER >= 1900
|
||||
# define EASY_LAMBDA_MOVE_CAPTURE
|
||||
# endif
|
||||
|
||||
# define EASY_FORCE_INLINE __forceinline
|
||||
|
||||
#elif defined(__clang__)
|
||||
@ -123,6 +138,10 @@
|
||||
# define EASY_FINAL
|
||||
# endif
|
||||
|
||||
# if EASY_STD > 11 && EASY_COMPILER_VERSION >= 34
|
||||
# define EASY_LAMBDA_MOVE_CAPTURE
|
||||
# endif
|
||||
|
||||
# define EASY_FORCE_INLINE inline __attribute__((always_inline))
|
||||
# undef EASY_COMPILER_VERSION
|
||||
|
||||
@ -160,6 +179,10 @@
|
||||
# define EASY_FINAL
|
||||
# endif
|
||||
|
||||
# if EASY_STD > 11 && EASY_COMPILER_VERSION >= 49
|
||||
# define EASY_LAMBDA_MOVE_CAPTURE
|
||||
# endif
|
||||
|
||||
# define EASY_FORCE_INLINE inline __attribute__((always_inline))
|
||||
# undef EASY_COMPILER_VERSION
|
||||
|
||||
|
@ -136,7 +136,7 @@ using async_future = std::future<async_result_t>;
|
||||
template <class T>
|
||||
struct Counter
|
||||
{
|
||||
T value = 0;
|
||||
T count = 0;
|
||||
};
|
||||
|
||||
struct Stats
|
||||
@ -147,7 +147,7 @@ struct Stats
|
||||
Stats(profiler::BlockStatistics* stats_ptr, profiler::timestamp_t duration) EASY_NOEXCEPT
|
||||
: stats(stats_ptr)
|
||||
{
|
||||
durations[duration].value = 1;
|
||||
durations[duration].count = 1;
|
||||
}
|
||||
|
||||
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)
|
||||
auto stats = it->second.stats;
|
||||
auto& durations = it->second.durations;
|
||||
++durations[duration].value;
|
||||
++durations[duration].count;
|
||||
|
||||
++stats->calls_number; // update calls number of this block
|
||||
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& durations = it->second.durations;
|
||||
|
||||
++durations[duration].value;
|
||||
++durations[duration].count;
|
||||
|
||||
++stats->calls_number; // update calls number of this block
|
||||
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;
|
||||
for (auto& kv : durations)
|
||||
{
|
||||
total_count += kv.second.value;
|
||||
total_count += kv.second.count;
|
||||
}
|
||||
|
||||
auto stats = it->second.stats;
|
||||
@ -525,7 +525,7 @@ static void calculate_medians(TStatsMapIterator begin, TStatsMapIterator end)
|
||||
size_t i = 0;
|
||||
for (auto& kv : durations)
|
||||
{
|
||||
const auto count = kv.second.value;
|
||||
const auto count = kv.second.count;
|
||||
|
||||
i += count;
|
||||
if (i < index)
|
||||
@ -546,7 +546,7 @@ static void calculate_medians(TStatsMapIterator begin, TStatsMapIterator end)
|
||||
bool i1 = false;
|
||||
for (auto& kv : durations)
|
||||
{
|
||||
const auto count = kv.second.value;
|
||||
const auto count = kv.second.count;
|
||||
|
||||
i += count;
|
||||
if (i < index1)
|
||||
|
@ -72,6 +72,7 @@
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QMoveEvent>
|
||||
#include <QResizeEvent>
|
||||
#include <QScrollBar>
|
||||
@ -99,99 +100,87 @@
|
||||
const int HIERARCHY_BUILDER_TIMER_INTERVAL = 40;
|
||||
|
||||
const bool PLAIN_MODE_COLUMNS[COL_COLUMNS_NUMBER] = {
|
||||
true, //COL_NAME,
|
||||
true, //COL_BEGIN,
|
||||
true, //COL_TIME,
|
||||
true, //COL_SELF_TIME,
|
||||
|
||||
false, //COL_TOTAL_TIME_PER_PARENT,
|
||||
false, //COL_TOTAL_TIME_PER_FRAME,
|
||||
true, //COL_TOTAL_TIME_PER_THREAD,
|
||||
|
||||
true, //COL_SELF_TIME_PERCENT,
|
||||
|
||||
false, //COL_PERCENT_PER_PARENT,
|
||||
true, //COL_PERCENT_PER_FRAME,
|
||||
|
||||
false, //COL_PERCENT_SUM_PER_PARENT,
|
||||
false, //COL_PERCENT_SUM_PER_FRAME,
|
||||
true, //COL_PERCENT_SUM_PER_THREAD,
|
||||
|
||||
true, //COL_END,
|
||||
|
||||
true, //COL_MIN_PER_FRAME,
|
||||
true, //COL_MAX_PER_FRAME,
|
||||
true, //COL_AVG_PER_FRAME,
|
||||
true, //COL_NCALLS_PER_FRAME,
|
||||
|
||||
true, //COL_MIN_PER_THREAD,
|
||||
true, //COL_MAX_PER_THREAD,
|
||||
true, //COL_AVG_PER_THREAD,
|
||||
true, //COL_NCALLS_PER_THREAD,
|
||||
|
||||
false, //COL_MIN_PER_PARENT,
|
||||
false, //COL_MAX_PER_PARENT,
|
||||
false, //COL_AVG_PER_PARENT,
|
||||
false, //COL_NCALLS_PER_PARENT,
|
||||
|
||||
true, //COL_ACTIVE_TIME,
|
||||
true, //COL_ACTIVE_PERCENT,
|
||||
|
||||
true, //COL_PERCENT_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,
|
||||
true // COL_NAME = 0,
|
||||
, true // COL_BEGIN,
|
||||
, true // COL_TIME,
|
||||
, true // COL_SELF_TIME,
|
||||
, true // COL_SELF_TIME_PERCENT,
|
||||
, true // COL_END,
|
||||
, true // COL_PERCENT_PER_FRAME,
|
||||
, false // COL_TOTAL_TIME_PER_FRAME,
|
||||
, false // COL_PERCENT_SUM_PER_FRAME,
|
||||
, true // COL_MIN_PER_FRAME,
|
||||
, true // COL_MAX_PER_FRAME,
|
||||
, true // COL_AVG_PER_FRAME,
|
||||
, true // COL_MEDIAN_PER_FRAME,
|
||||
, true // COL_NCALLS_PER_FRAME,
|
||||
, true // COL_TOTAL_TIME_PER_THREAD,
|
||||
, true // COL_PERCENT_SUM_PER_THREAD,
|
||||
, true // COL_MIN_PER_THREAD,
|
||||
, true // COL_MAX_PER_THREAD,
|
||||
, true // COL_AVG_PER_THREAD,
|
||||
, true // COL_MEDIAN_PER_THREAD,
|
||||
, true // COL_NCALLS_PER_THREAD,
|
||||
, false // COL_PERCENT_PER_PARENT,
|
||||
, false // COL_TOTAL_TIME_PER_PARENT,
|
||||
, false // COL_PERCENT_SUM_PER_PARENT,
|
||||
, false // COL_MIN_PER_PARENT,
|
||||
, false // COL_MAX_PER_PARENT,
|
||||
, false // COL_AVG_PER_PARENT,
|
||||
, false // COL_MEDIAN_PER_PARENT,
|
||||
, false // COL_NCALLS_PER_PARENT,
|
||||
, true // COL_ACTIVE_TIME,
|
||||
, true // COL_ACTIVE_PERCENT,
|
||||
, true // COL_PERCENT_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_MEDIAN_PER_AREA,
|
||||
, true // COL_NCALLS_PER_AREA,
|
||||
};
|
||||
|
||||
const bool SELECTION_MODE_COLUMNS[COL_COLUMNS_NUMBER] = {
|
||||
true, //COL_NAME,
|
||||
false, //COL_BEGIN,
|
||||
true, //COL_TIME,
|
||||
true, //COL_SELF_TIME,
|
||||
|
||||
false, //COL_TOTAL_TIME_PER_PARENT,
|
||||
false, //COL_TOTAL_TIME_PER_FRAME,
|
||||
true, //COL_TOTAL_TIME_PER_THREAD,
|
||||
|
||||
true, //COL_SELF_TIME_PERCENT,
|
||||
|
||||
false, //COL_PERCENT_PER_PARENT,
|
||||
false, //COL_PERCENT_PER_FRAME,
|
||||
|
||||
false, //COL_PERCENT_SUM_PER_PARENT,
|
||||
false, //COL_PERCENT_SUM_PER_FRAME,
|
||||
true, //COL_PERCENT_SUM_PER_THREAD,
|
||||
|
||||
false, //COL_END,
|
||||
|
||||
false, //COL_MIN_PER_FRAME,
|
||||
false, //COL_MAX_PER_FRAME,
|
||||
false, //COL_AVG_PER_FRAME,
|
||||
false, //COL_NCALLS_PER_FRAME,
|
||||
|
||||
true, //COL_MIN_PER_THREAD,
|
||||
true, //COL_MAX_PER_THREAD,
|
||||
true, //COL_AVG_PER_THREAD,
|
||||
true, //COL_NCALLS_PER_THREAD,
|
||||
|
||||
false, //COL_MIN_PER_PARENT,
|
||||
false, //COL_MAX_PER_PARENT,
|
||||
false, //COL_AVG_PER_PARENT,
|
||||
false, //COL_NCALLS_PER_PARENT,
|
||||
|
||||
true, //COL_ACTIVE_TIME,
|
||||
true, //COL_ACTIVE_PERCENT,
|
||||
|
||||
false, //COL_PERCENT_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,
|
||||
true // COL_NAME = 0,
|
||||
, false // COL_BEGIN,
|
||||
, true // COL_TIME,
|
||||
, true // COL_SELF_TIME,
|
||||
, true // COL_SELF_TIME_PERCENT,
|
||||
, false // COL_END,
|
||||
, false // COL_PERCENT_PER_FRAME,
|
||||
, false // COL_TOTAL_TIME_PER_FRAME,
|
||||
, false // COL_PERCENT_SUM_PER_FRAME,
|
||||
, false // COL_MIN_PER_FRAME,
|
||||
, false // COL_MAX_PER_FRAME,
|
||||
, false // COL_AVG_PER_FRAME,
|
||||
, false // COL_MEDIAN_PER_FRAME,
|
||||
, false // COL_NCALLS_PER_FRAME,
|
||||
, true // COL_TOTAL_TIME_PER_THREAD,
|
||||
, true // COL_PERCENT_SUM_PER_THREAD,
|
||||
, true // COL_MIN_PER_THREAD,
|
||||
, true // COL_MAX_PER_THREAD,
|
||||
, true // COL_AVG_PER_THREAD,
|
||||
, true // COL_MEDIAN_PER_THREAD,
|
||||
, true // COL_NCALLS_PER_THREAD,
|
||||
, false // COL_PERCENT_PER_PARENT,
|
||||
, false // COL_TOTAL_TIME_PER_PARENT,
|
||||
, false // COL_PERCENT_SUM_PER_PARENT,
|
||||
, false // COL_MIN_PER_PARENT,
|
||||
, false // COL_MAX_PER_PARENT,
|
||||
, false // COL_AVG_PER_PARENT,
|
||||
, false // COL_MEDIAN_PER_PARENT,
|
||||
, false // COL_NCALLS_PER_PARENT,
|
||||
, true // COL_ACTIVE_TIME,
|
||||
, true // COL_ACTIVE_PERCENT,
|
||||
, true // COL_PERCENT_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_MEDIAN_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_MAX_PER_FRAME, "Max/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_MIN_PER_PARENT, "Min/parent");
|
||||
header_item->setText(COL_MAX_PER_PARENT, "Max/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_MIN_PER_THREAD, "Min/thread");
|
||||
header_item->setText(COL_MAX_PER_THREAD, "Max/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_ACTIVE_TIME, "WorkTime");
|
||||
@ -270,12 +262,14 @@ BlocksTreeWidget::BlocksTreeWidget(QWidget* _parent)
|
||||
header_item->setText(COL_MIN_PER_AREA, "Min/area");
|
||||
header_item->setText(COL_MAX_PER_AREA, "Max/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");
|
||||
|
||||
auto color = QColor::fromRgb(profiler::colors::DeepOrange900);
|
||||
header_item->setForeground(COL_MIN_PER_THREAD, color);
|
||||
header_item->setForeground(COL_MAX_PER_THREAD, color);
|
||||
header_item->setForeground(COL_AVG_PER_THREAD, color);
|
||||
header_item->setForeground(COL_MEDIAN_PER_THREAD, color);
|
||||
header_item->setForeground(COL_NCALLS_PER_THREAD, color);
|
||||
header_item->setForeground(COL_PERCENT_SUM_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_MAX_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_PERCENT_SUM_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_MAX_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_PERCENT_SUM_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_MAX_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);
|
||||
|
||||
setHeaderItem(header_item);
|
||||
@ -502,6 +499,7 @@ void BlocksTreeWidget::onFillTimerTimeout()
|
||||
ThreadedItems toplevelitems;
|
||||
m_hierarchyBuilder.takeItems(m_items);
|
||||
m_hierarchyBuilder.takeTopLevelItems(toplevelitems);
|
||||
auto error = m_hierarchyBuilder.error();
|
||||
m_hierarchyBuilder.interrupt();
|
||||
{
|
||||
const QSignalBlocker b(this);
|
||||
@ -550,6 +548,12 @@ void BlocksTreeWidget::onFillTimerTimeout()
|
||||
connect(this, &Parent::itemDoubleClicked, this, &This::onItemDoubleClicked);
|
||||
onSelectedThreadChange(EASY_GLOBALS.selected_thread);
|
||||
onSelectedBlockChange(EASY_GLOBALS.selected_block);
|
||||
|
||||
if (!error.isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, "Warning", error, QMessageBox::Close);
|
||||
clearSilent();
|
||||
}
|
||||
}
|
||||
else if (m_progress != nullptr)
|
||||
{
|
||||
@ -578,7 +582,7 @@ void BlocksTreeWidget::onIdleTimeout()
|
||||
return;
|
||||
|
||||
const int column = columnAt(pos.x());
|
||||
if (item->hasToolTip(column))
|
||||
if (!item->data(column, Qt::ToolTipRole).isNull())
|
||||
return;
|
||||
|
||||
auto focusWidget = qApp->focusWidget();
|
||||
@ -675,9 +679,14 @@ void BlocksTreeWidget::clearSilent(bool _global)
|
||||
for (int i = topLevelItemCount() - 1; i >= 0; --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)
|
||||
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_PARENT:
|
||||
case COL_MIN_PER_FRAME:
|
||||
case COL_MIN_PER_AREA:
|
||||
case COL_MAX_PER_THREAD:
|
||||
case COL_MAX_PER_PARENT:
|
||||
case COL_MAX_PER_FRAME:
|
||||
case COL_MAX_PER_AREA:
|
||||
{
|
||||
auto& block = item->block();
|
||||
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_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_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))
|
||||
@ -1036,6 +1063,7 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
ADD_COLUMN_ACTION(COL_MIN_PER_FRAME);
|
||||
ADD_COLUMN_ACTION(COL_MAX_PER_FRAME);
|
||||
ADD_COLUMN_ACTION(COL_AVG_PER_FRAME);
|
||||
ADD_COLUMN_ACTION(COL_MEDIAN_PER_FRAME);
|
||||
ADD_COLUMN_ACTION(COL_NCALLS_PER_FRAME);
|
||||
|
||||
hidemenu->addSeparator();
|
||||
@ -1045,6 +1073,7 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
ADD_COLUMN_ACTION(COL_MIN_PER_THREAD);
|
||||
ADD_COLUMN_ACTION(COL_MAX_PER_THREAD);
|
||||
ADD_COLUMN_ACTION(COL_AVG_PER_THREAD);
|
||||
ADD_COLUMN_ACTION(COL_MEDIAN_PER_THREAD);
|
||||
ADD_COLUMN_ACTION(COL_NCALLS_PER_THREAD);
|
||||
|
||||
hidemenu->addSeparator();
|
||||
@ -1055,6 +1084,7 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
ADD_COLUMN_ACTION(COL_MIN_PER_PARENT);
|
||||
ADD_COLUMN_ACTION(COL_MAX_PER_PARENT);
|
||||
ADD_COLUMN_ACTION(COL_AVG_PER_PARENT);
|
||||
ADD_COLUMN_ACTION(COL_MEDIAN_PER_PARENT);
|
||||
ADD_COLUMN_ACTION(COL_NCALLS_PER_PARENT);
|
||||
|
||||
hidemenu->addSeparator();
|
||||
@ -1070,6 +1100,7 @@ void BlocksTreeWidget::contextMenuEvent(QContextMenuEvent* _event)
|
||||
ADD_COLUMN_ACTION(COL_MIN_PER_AREA);
|
||||
ADD_COLUMN_ACTION(COL_MAX_PER_AREA);
|
||||
ADD_COLUMN_ACTION(COL_AVG_PER_AREA);
|
||||
ADD_COLUMN_ACTION(COL_MEDIAN_PER_AREA);
|
||||
ADD_COLUMN_ACTION(COL_NCALLS_PER_AREA);
|
||||
|
||||
#undef ADD_STATUS_ACTION
|
||||
@ -1473,19 +1504,29 @@ void BlocksTreeWidget::loadSettings()
|
||||
settings.beginGroup("tree_widget");
|
||||
|
||||
auto val = settings.value("regime");
|
||||
if (!val.isNull())
|
||||
m_mode = static_cast<TreeMode>(val.toUInt());
|
||||
|
||||
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()));
|
||||
m_mode = static_cast<TreeMode>(val.toUInt());
|
||||
}
|
||||
|
||||
auto state = settings.value("headerState").toByteArray();
|
||||
if (!state.isEmpty())
|
||||
header()->restoreState(state);
|
||||
val = settings.value("columns_version");
|
||||
if (!val.isNull() && val.toInt() == COLUMNS_VERSION)
|
||||
{
|
||||
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();
|
||||
}
|
||||
@ -1495,6 +1536,7 @@ void BlocksTreeWidget::saveSettings()
|
||||
QSettings settings(profiler_gui::ORGANAZATION_NAME, profiler_gui::APPLICATION_NAME);
|
||||
settings.beginGroup("tree_widget");
|
||||
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("headerState", header()->saveState());
|
||||
settings.endGroup();
|
||||
|
@ -52,6 +52,8 @@
|
||||
* : limitations under the License.
|
||||
************************************************************************/
|
||||
|
||||
#include <QList>
|
||||
#include <QTreeWidgetItem>
|
||||
#include "common_functions.h"
|
||||
|
||||
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.
|
||||
|
@ -66,6 +66,8 @@
|
||||
|
||||
#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.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -459,9 +459,14 @@ void DescriptorsTreeWidget::clearSilent(bool _global)
|
||||
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)
|
||||
delete item;
|
||||
#endif
|
||||
profiler_gui::deleteTreeItem(item);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,7 @@ Globals::Globals()
|
||||
, selected_block(::profiler_gui::numeric_max<decltype(selected_block)>())
|
||||
, selected_block_id(::profiler_gui::numeric_max<decltype(selected_block_id)>())
|
||||
, version(0)
|
||||
, max_rows_count(500 * 1000)
|
||||
, frame_time(16700)
|
||||
, blocks_spacing(0)
|
||||
, blocks_size_min(2)
|
||||
|
@ -213,6 +213,8 @@ namespace profiler_gui {
|
||||
::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 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
|
||||
int blocks_spacing; ///< Minimum blocks spacing 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->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");
|
||||
@ -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)
|
||||
{
|
||||
EASY_GLOBALS.blocks_spacing = _value;
|
||||
@ -1676,6 +1702,10 @@ void MainWindow::loadSettings()
|
||||
if (!val.isNull())
|
||||
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");
|
||||
if (!val.isNull())
|
||||
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("time_units", static_cast<int>(EASY_GLOBALS.time_units));
|
||||
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_size_min", EASY_GLOBALS.blocks_size_min);
|
||||
settings.setValue("blocks_narrow_size", EASY_GLOBALS.blocks_narrow_size);
|
||||
|
@ -344,6 +344,7 @@ protected slots:
|
||||
void onExpandAllClicked(bool);
|
||||
void onCollapseAllClicked(bool);
|
||||
void onViewportInfoClicked(bool);
|
||||
void onMaxBlocksCountChange(int _value);
|
||||
void onSpacingChange(int _value);
|
||||
void onMinSizeChange(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...)"
|
||||
#endif
|
||||
|
||||
void setLowestThreadPriority()
|
||||
static void setLowestThreadPriority()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
|
||||
@ -95,13 +95,32 @@ ThreadPool& ThreadPool::instance()
|
||||
|
||||
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
|
||||
std::generate_n(std::back_inserter(m_threads), std::thread::hardware_concurrency(), [this] {
|
||||
return std::thread(&ThreadPool::tasksWorker, this);
|
||||
std::generate_n(std::back_inserter(m_threads), count, [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
|
||||
m_threads.emplace_back(&ThreadPool::jobsWorker, this);
|
||||
}
|
||||
@ -110,6 +129,9 @@ ThreadPool::~ThreadPool()
|
||||
{
|
||||
m_interrupt.store(true, std::memory_order_release);
|
||||
m_tasks.cv.notify_all();
|
||||
#ifdef EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||
m_qtasks.cv.notify_all();
|
||||
#endif
|
||||
m_backgroundJobs.cv.notify_all();
|
||||
for (auto& thread : m_threads)
|
||||
thread.join();
|
||||
@ -125,53 +147,69 @@ void ThreadPool::backgroundJob(std::function<void()>&& func)
|
||||
|
||||
void ThreadPool::enqueue(ThreadPoolTask& task)
|
||||
{
|
||||
m_tasks.mutex.lock();
|
||||
m_tasks.queue.emplace_back(task);
|
||||
m_tasks.mutex.unlock();
|
||||
m_tasks.cv.notify_one();
|
||||
#ifdef EASY_THREADPOOL_SEPARATE_QT_THREAD
|
||||
if (task.creatingQtObjects())
|
||||
enqueue(task, m_qtasks);
|
||||
else
|
||||
#endif
|
||||
enqueue(task, m_tasks);
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
{
|
||||
m_tasks.queue.erase(it);
|
||||
tasks.queue.erase(it);
|
||||
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);
|
||||
m_tasks.cv.wait(lock, [this] { return !m_tasks.queue.empty() || m_interrupt.load(std::memory_order_acquire); });
|
||||
std::unique_lock<std::mutex> lock(tasks.mutex);
|
||||
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))
|
||||
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();
|
||||
auto& task = tasks.queue.front().get();
|
||||
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
|
||||
lock.unlock();
|
||||
|
||||
// execute task
|
||||
task.execute();
|
||||
task();
|
||||
|
||||
// lock again to check if there are new tasks in the queue
|
||||
lock.lock();
|
||||
@ -183,21 +221,15 @@ void ThreadPool::jobsWorker()
|
||||
{
|
||||
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);
|
||||
m_backgroundJobs.cv.wait(lock, [this] {
|
||||
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());
|
||||
m_backgroundJobs.queue.pop_front();
|
||||
|
||||
|
@ -58,6 +58,18 @@
|
||||
#include <thread>
|
||||
#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
|
||||
{
|
||||
friend ThreadPoolTask;
|
||||
@ -70,8 +82,14 @@ class ThreadPool EASY_FINAL
|
||||
std::condition_variable cv;
|
||||
};
|
||||
|
||||
Jobs<std::reference_wrapper<ThreadPoolTask> > m_tasks;
|
||||
Jobs<std::function<void()> > m_backgroundJobs;
|
||||
using TaskJobs = Jobs<std::reference_wrapper<ThreadPoolTask> >;
|
||||
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::atomic_bool m_interrupt;
|
||||
|
||||
@ -89,7 +107,11 @@ private:
|
||||
|
||||
void enqueue(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();
|
||||
|
||||
}; // end of class ThreadPool.
|
||||
|
@ -53,7 +53,10 @@
|
||||
|
||||
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);
|
||||
}
|
||||
@ -64,6 +67,11 @@ ThreadPoolTask::~ThreadPoolTask()
|
||||
dequeue();
|
||||
}
|
||||
|
||||
bool ThreadPoolTask::creatingQtObjects() const
|
||||
{
|
||||
return m_creatingQtObjects;
|
||||
}
|
||||
|
||||
void ThreadPoolTask::enqueue(Func&& func, std::atomic_bool& interruptFlag)
|
||||
{
|
||||
dequeue();
|
||||
@ -101,7 +109,7 @@ TaskStatus ThreadPoolTask::status() const
|
||||
return static_cast<TaskStatus>(m_status.load(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
void ThreadPoolTask::execute()
|
||||
void ThreadPoolTask::operator()()
|
||||
{
|
||||
// execute if not cancelled
|
||||
{
|
||||
|
@ -93,13 +93,14 @@ private:
|
||||
std::atomic_bool* m_interrupt;
|
||||
std::mutex m_mutex;
|
||||
std::atomic<int8_t> m_status;
|
||||
const bool m_creatingQtObjects;
|
||||
|
||||
public:
|
||||
|
||||
ThreadPoolTask(const ThreadPoolTask&) = delete;
|
||||
ThreadPoolTask(ThreadPoolTask&&) = delete;
|
||||
|
||||
ThreadPoolTask();
|
||||
explicit ThreadPoolTask(bool creatingQtObjects = false);
|
||||
~ThreadPoolTask();
|
||||
|
||||
void enqueue(Func&& func, std::atomic_bool& interruptFlag);
|
||||
@ -109,7 +110,9 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void execute();
|
||||
bool creatingQtObjects() const;
|
||||
|
||||
void operator() ();
|
||||
|
||||
TaskStatus status() const;
|
||||
void setStatus(TaskStatus status);
|
||||
|
@ -71,55 +71,48 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EASY_CONSTEXPR int BlockColorRole = Qt::UserRole + 1;
|
||||
|
||||
namespace {
|
||||
|
||||
EASY_CONSTEXPR int ColumnBit[COL_COLUMNS_NUMBER] = {
|
||||
-1 // COL_NAME = 0,
|
||||
|
||||
, 0 // COL_BEGIN,
|
||||
|
||||
, 1 // COL_TIME,
|
||||
, 2 // COL_SELF_TIME,
|
||||
, 3 // COL_TOTAL_TIME_PER_PARENT,
|
||||
, 4 // COL_TOTAL_TIME_PER_FRAME,
|
||||
, 5 // COL_TOTAL_TIME_PER_THREAD,
|
||||
|
||||
, -1 // COL_SELF_TIME_PERCENT,
|
||||
, -1 // COL_PERCENT_PER_PARENT,
|
||||
, -1 // COL_PERCENT_PER_FRAME,
|
||||
, -1 // COL_PERCENT_SUM_PER_PARENT,
|
||||
, -1 // COL_PERCENT_SUM_PER_FRAME,
|
||||
, -1 // COL_PERCENT_SUM_PER_THREAD,
|
||||
|
||||
, 6 // COL_END,
|
||||
|
||||
, 7 // COL_MIN_PER_FRAME,
|
||||
, 8 // COL_MAX_PER_FRAME,
|
||||
, 9 // COL_AVG_PER_FRAME,
|
||||
, -1 // COL_NCALLS_PER_FRAME,
|
||||
|
||||
, 10 // COL_MIN_PER_THREAD,
|
||||
, 11 // COL_MAX_PER_THREAD,
|
||||
, 12 // COL_AVG_PER_THREAD,
|
||||
, -1 // COL_NCALLS_PER_THREAD,
|
||||
|
||||
, 13 // COL_MIN_PER_PARENT,
|
||||
, 14 // COL_MAX_PER_PARENT,
|
||||
, 15 // COL_AVG_PER_PARENT,
|
||||
, -1 // COL_NCALLS_PER_PARENT,
|
||||
|
||||
, 16 // COL_ACTIVE_TIME,
|
||||
, -1 // COL_ACTIVE_PERCENT,
|
||||
|
||||
, -1 // COL_PERCENT_PER_AREA,
|
||||
, 17 // COL_TOTAL_TIME_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,
|
||||
EASY_CONSTEXPR bool HasToolTip[COL_COLUMNS_NUMBER] = {
|
||||
false // COL_NAME = 0,
|
||||
, true // COL_BEGIN,
|
||||
, true // COL_TIME,
|
||||
, true // COL_SELF_TIME,
|
||||
, false // COL_SELF_TIME_PERCENT,
|
||||
, true // COL_END,
|
||||
, false // COL_PERCENT_PER_FRAME,
|
||||
, true // COL_TOTAL_TIME_PER_FRAME,
|
||||
, false // COL_PERCENT_SUM_PER_FRAME,
|
||||
, true // COL_MIN_PER_FRAME,
|
||||
, true // COL_MAX_PER_FRAME,
|
||||
, true // COL_AVG_PER_FRAME,
|
||||
, true // COL_MEDIAN_PER_FRAME,
|
||||
, false // COL_NCALLS_PER_FRAME,
|
||||
, true // COL_TOTAL_TIME_PER_THREAD,
|
||||
, false // COL_PERCENT_SUM_PER_THREAD,
|
||||
, true // COL_MIN_PER_THREAD,
|
||||
, true // COL_MAX_PER_THREAD,
|
||||
, true // COL_AVG_PER_THREAD,
|
||||
, true // COL_MEDIAN_PER_THREAD,
|
||||
, false // COL_NCALLS_PER_THREAD,
|
||||
, false // COL_PERCENT_PER_PARENT,
|
||||
, true // COL_TOTAL_TIME_PER_PARENT,
|
||||
, false // COL_PERCENT_SUM_PER_PARENT,
|
||||
, true // COL_MIN_PER_PARENT,
|
||||
, true // COL_MAX_PER_PARENT,
|
||||
, true // COL_AVG_PER_PARENT,
|
||||
, true // COL_MEDIAN_PER_PARENT,
|
||||
, false // COL_NCALLS_PER_PARENT,
|
||||
, true // COL_ACTIVE_TIME,
|
||||
, false // COL_ACTIVE_PERCENT,
|
||||
, false // COL_PERCENT_PER_AREA,
|
||||
, true // COL_TOTAL_TIME_PER_AREA,
|
||||
, false // COL_PERCENT_SUM_PER_AREA,
|
||||
, true // COL_MIN_PER_AREA,
|
||||
, true // COL_MAX_PER_AREA,
|
||||
, true // COL_AVG_PER_AREA,
|
||||
, true // COL_MEDIAN_PER_AREA,
|
||||
, false // COL_NCALLS_PER_AREA,
|
||||
};
|
||||
|
||||
} // end of namespace <noname>.
|
||||
@ -192,19 +185,6 @@ bool TreeWidgetItem::isPartial() const
|
||||
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
|
||||
{
|
||||
if (_column == COL_NAME)
|
||||
@ -233,15 +213,29 @@ QVariant TreeWidgetItem::data(int _column, int _role) const
|
||||
|
||||
case Qt::ToolTipRole:
|
||||
{
|
||||
if (hasToolTip(_column))
|
||||
return QVariant::fromValue(QString("%1 ns").arg(data(_column, Qt::UserRole).toULongLong()));
|
||||
const bool hasToolTip = HasToolTip[_column];
|
||||
if (hasToolTip)
|
||||
{
|
||||
auto v = data(_column, Qt::UserRole);
|
||||
if (!v.isNull())
|
||||
return QString("%1 ns").arg(v.toULongLong());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MinMaxBlockIndexRole:
|
||||
{
|
||||
auto v = Parent::data(_column, _role);
|
||||
if (!v.isNull() || parent() == nullptr)
|
||||
return v;
|
||||
return QVariant::fromValue(m_block);
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
if (_role != Qt::UserRole && _role != Qt::DisplayRole)
|
||||
return QTreeWidgetItem::data(_column, _role);
|
||||
auto v = Parent::data(_column, _role);
|
||||
if (!v.isNull() || parent() == nullptr || (_role != Qt::UserRole && _role != Qt::DisplayRole))
|
||||
return v;
|
||||
return relevantData(_column, _role);
|
||||
}
|
||||
}
|
||||
@ -269,7 +263,7 @@ QVariant TreeWidgetItem::relevantData(int _column, int _role) const
|
||||
case COL_PERCENT_PER_AREA:
|
||||
case COL_PERCENT_SUM_PER_THREAD:
|
||||
{
|
||||
return Parent::data(_column, _role);
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
default:
|
||||
@ -278,10 +272,9 @@ QVariant TreeWidgetItem::relevantData(int _column, int _role) const
|
||||
}
|
||||
}
|
||||
|
||||
auto var = Parent::data(_column, _role);
|
||||
if (!var.isNull() || (EASY_GLOBALS.display_only_relevant_stats && _role == Qt::DisplayRole))
|
||||
if (EASY_GLOBALS.display_only_relevant_stats && _role == Qt::DisplayRole)
|
||||
{
|
||||
return var;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
switch (_column)
|
||||
@ -302,23 +295,27 @@ QVariant TreeWidgetItem::relevantData(int _column, int _role) const
|
||||
case COL_AVG_PER_FRAME:
|
||||
case COL_AVG_PER_THREAD:
|
||||
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:
|
||||
{
|
||||
return Parent::data(COL_PERCENT_PER_PARENT, _role);
|
||||
return data(COL_PERCENT_PER_PARENT, _role);
|
||||
}
|
||||
|
||||
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:
|
||||
{
|
||||
return Parent::data(COL_PERCENT_PER_AREA, _role);
|
||||
return data(COL_PERCENT_PER_AREA, _role);
|
||||
}
|
||||
|
||||
default:
|
||||
@ -327,7 +324,7 @@ QVariant TreeWidgetItem::relevantData(int _column, int _role) const
|
||||
}
|
||||
}
|
||||
|
||||
return var;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
const profiler::timestamp_t nanosecondsTime = PROF_NANOSECONDS(_time);
|
||||
|
||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||
setHasToolTip(_column);
|
||||
//setHasToolTip(_column);
|
||||
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);
|
||||
|
||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||
setHasToolTip(_column);
|
||||
//setHasToolTip(_column);
|
||||
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);
|
||||
setData(_column, Qt::UserRole, (quint64)nanosecondsTime);
|
||||
setHasToolTip(_column);
|
||||
//setHasToolTip(_column);
|
||||
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);
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -61,7 +61,6 @@
|
||||
#include <QTreeWidget>
|
||||
#include <QStyledItemDelegate>
|
||||
#include <easy/reader.h>
|
||||
#include <bitset>
|
||||
|
||||
#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
|
||||
{
|
||||
COL_UNKNOWN = -1,
|
||||
@ -79,32 +82,34 @@ enum EasyColumnsIndexes
|
||||
|
||||
COL_TIME,
|
||||
COL_SELF_TIME,
|
||||
COL_TOTAL_TIME_PER_PARENT,
|
||||
COL_TOTAL_TIME_PER_FRAME,
|
||||
COL_TOTAL_TIME_PER_THREAD,
|
||||
|
||||
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_PERCENT_PER_FRAME,
|
||||
COL_TOTAL_TIME_PER_FRAME,
|
||||
COL_PERCENT_SUM_PER_FRAME,
|
||||
COL_MIN_PER_FRAME,
|
||||
COL_MAX_PER_FRAME,
|
||||
COL_AVG_PER_FRAME,
|
||||
COL_MEDIAN_PER_FRAME,
|
||||
COL_NCALLS_PER_FRAME,
|
||||
|
||||
COL_TOTAL_TIME_PER_THREAD,
|
||||
COL_PERCENT_SUM_PER_THREAD,
|
||||
COL_MIN_PER_THREAD,
|
||||
COL_MAX_PER_THREAD,
|
||||
COL_AVG_PER_THREAD,
|
||||
COL_MEDIAN_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_MAX_PER_PARENT,
|
||||
COL_AVG_PER_PARENT,
|
||||
COL_MEDIAN_PER_PARENT,
|
||||
COL_NCALLS_PER_PARENT,
|
||||
|
||||
COL_ACTIVE_TIME,
|
||||
@ -116,6 +121,7 @@ enum EasyColumnsIndexes
|
||||
COL_MIN_PER_AREA,
|
||||
COL_MAX_PER_AREA,
|
||||
COL_AVG_PER_AREA,
|
||||
COL_MEDIAN_PER_AREA,
|
||||
COL_NCALLS_PER_AREA,
|
||||
|
||||
COL_COLUMNS_NUMBER
|
||||
@ -130,7 +136,6 @@ class TreeWidgetItem : public QTreeWidgetItem
|
||||
|
||||
const profiler::block_index_t m_block;
|
||||
QRgb m_customBGColor;
|
||||
std::bitset<21> m_bHasToolTip;
|
||||
bool m_bMain;
|
||||
bool m_partial;
|
||||
|
||||
@ -147,14 +152,10 @@ public:
|
||||
public:
|
||||
|
||||
bool isPartial() const;
|
||||
bool hasToolTip(int _column) const;
|
||||
profiler::block_index_t block_index() const;
|
||||
profiler_gui::EasyBlock& guiBlock();
|
||||
const profiler::BlocksTree& block() const;
|
||||
|
||||
profiler::timestamp_t duration() const;
|
||||
profiler::timestamp_t selfDuration() const;
|
||||
|
||||
profiler::thread_id_t threadId() const;
|
||||
|
||||
void setTimeSmart(int _column, profiler_gui::TimeUnits _units, const profiler::timestamp_t& _time, const QString& _prefix);
|
||||
@ -174,7 +175,7 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
void setHasToolTip(int _column);
|
||||
//void setHasToolTip(int _column);
|
||||
QVariant relevantData(int _column, int _role) const;
|
||||
QVariant partialForeground() const;
|
||||
|
||||
|
@ -54,10 +54,13 @@
|
||||
* : limitations under the License.
|
||||
************************************************************************/
|
||||
|
||||
#include "tree_widget_loader.h"
|
||||
#include "tree_widget_item.h"
|
||||
#include <map>
|
||||
|
||||
#include "globals.h"
|
||||
#include "thread_pool.h"
|
||||
#include "tree_widget_item.h"
|
||||
|
||||
#include "tree_widget_loader.h"
|
||||
|
||||
#ifdef max
|
||||
#undef max
|
||||
@ -75,15 +78,94 @@
|
||||
#define EASY_INIT_ATOMIC(v) {v}
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct ThreadData
|
||||
{
|
||||
StatsMap stats;
|
||||
StatsMap stats;
|
||||
IdItems iditems;
|
||||
TreeWidgetItem* item = nullptr;
|
||||
};
|
||||
|
||||
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(
|
||||
TreeWidgetItem* item,
|
||||
const profiler::BlockStatistics* stats,
|
||||
@ -91,12 +173,19 @@ static void fillStatsColumns(
|
||||
int min_column,
|
||||
int max_column,
|
||||
int avg_column,
|
||||
int median_column,
|
||||
int total_column,
|
||||
int n_calls_column
|
||||
) {
|
||||
item->setData(n_calls_column, Qt::UserRole, 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)
|
||||
{
|
||||
return;
|
||||
@ -106,21 +195,23 @@ static void fillStatsColumns(
|
||||
const auto max_duration = easyBlock(stats->max_duration_block).tree.node->duration();
|
||||
const auto avg_duration = stats->average_duration();
|
||||
const auto tot_duration = stats->total_duration;
|
||||
const auto median_duration = stats->median_duration;
|
||||
|
||||
item->setTimeSmart(min_column, units, min_duration);
|
||||
item->setTimeSmart(max_column, units, max_duration);
|
||||
item->setTimeSmart(avg_column, units, avg_duration);
|
||||
item->setTimeSmart(median_column, units, median_duration);
|
||||
item->setTimeSmart(total_column, units, tot_duration);
|
||||
|
||||
if (stats->calls_number > 1 && tot_duration != 0)
|
||||
{
|
||||
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))
|
||||
{
|
||||
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_MAX_PER_THREAD,
|
||||
COL_AVG_PER_THREAD,
|
||||
COL_MEDIAN_PER_THREAD,
|
||||
COL_TOTAL_TIME_PER_THREAD,
|
||||
COL_NCALLS_PER_THREAD
|
||||
);
|
||||
@ -148,6 +240,7 @@ inline void fillStatsColumnsFrame(TreeWidgetItem* item, const profiler::BlockSta
|
||||
COL_MIN_PER_FRAME,
|
||||
COL_MAX_PER_FRAME,
|
||||
COL_AVG_PER_FRAME,
|
||||
COL_MEDIAN_PER_FRAME,
|
||||
COL_TOTAL_TIME_PER_FRAME,
|
||||
COL_NCALLS_PER_FRAME
|
||||
);
|
||||
@ -162,6 +255,7 @@ inline void fillStatsColumnsParent(TreeWidgetItem* item, const profiler::BlockSt
|
||||
COL_MIN_PER_PARENT,
|
||||
COL_MAX_PER_PARENT,
|
||||
COL_AVG_PER_PARENT,
|
||||
COL_MEDIAN_PER_PARENT,
|
||||
COL_TOTAL_TIME_PER_PARENT,
|
||||
COL_NCALLS_PER_PARENT
|
||||
);
|
||||
@ -176,13 +270,15 @@ inline void fillStatsColumnsSelection(TreeWidgetItem* item, const profiler::Bloc
|
||||
COL_MIN_PER_AREA,
|
||||
COL_MAX_PER_AREA,
|
||||
COL_AVG_PER_AREA,
|
||||
COL_MEDIAN_PER_AREA,
|
||||
COL_TOTAL_TIME_PER_AREA,
|
||||
COL_NCALLS_PER_AREA
|
||||
);
|
||||
}
|
||||
|
||||
TreeWidgetLoader::TreeWidgetLoader()
|
||||
: m_bDone(EASY_INIT_ATOMIC(false))
|
||||
: m_worker(true)
|
||||
, m_bDone(EASY_INIT_ATOMIC(false))
|
||||
, m_bInterrupt(EASY_INIT_ATOMIC(false))
|
||||
, m_progress(EASY_INIT_ATOMIC(0))
|
||||
, 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)
|
||||
{
|
||||
m_worker.dequeue();
|
||||
@ -248,21 +349,28 @@ void TreeWidgetLoader::interrupt(bool _wait)
|
||||
{
|
||||
if (!_wait)
|
||||
{
|
||||
auto items = std::move(m_topLevelItems);
|
||||
ThreadPool::instance().backgroundJob([=] {
|
||||
auto topLevelItems = std::move(m_topLevelItems);
|
||||
|
||||
#ifdef EASY_LAMBDA_MOVE_CAPTURE
|
||||
ThreadPool::instance().backgroundJob([items = std::move(topLevelItems)] {
|
||||
for (auto item : items)
|
||||
delete item.second;
|
||||
#else
|
||||
ThreadPool::instance().backgroundJob([=] {
|
||||
for (auto item : topLevelItems)
|
||||
#endif
|
||||
profiler_gui::deleteTreeItem(item.second);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto item : m_topLevelItems)
|
||||
delete item.second;
|
||||
profiler_gui::deleteTreeItem(item.second);
|
||||
}
|
||||
}
|
||||
|
||||
m_items.clear();
|
||||
m_topLevelItems.clear();
|
||||
m_error.clear();
|
||||
}
|
||||
|
||||
void TreeWidgetLoader::fillTreeBlocks(
|
||||
@ -280,6 +388,7 @@ void TreeWidgetLoader::fillTreeBlocks(
|
||||
const auto decoratedNames = EASY_GLOBALS.use_decorated_thread_name;
|
||||
const auto hexThreadIds = EASY_GLOBALS.hex_thread_id;
|
||||
const auto timeUnits = EASY_GLOBALS.time_units;
|
||||
const auto maxCount = EASY_GLOBALS.max_rows_count;
|
||||
const auto blocks = std::ref(_blocks);
|
||||
const auto mode = m_mode;
|
||||
m_worker.enqueue([=] {
|
||||
@ -287,17 +396,48 @@ void TreeWidgetLoader::fillTreeBlocks(
|
||||
{
|
||||
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;
|
||||
}
|
||||
case TreeMode::Plain:
|
||||
{
|
||||
setTreeInternalPlainTop(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits);
|
||||
setTreeInternalPlainTop(
|
||||
_beginTime,
|
||||
blocks,
|
||||
_left,
|
||||
_right,
|
||||
_strict,
|
||||
zeroBlocks,
|
||||
decoratedNames,
|
||||
hexThreadIds,
|
||||
timeUnits
|
||||
);
|
||||
break;
|
||||
}
|
||||
case TreeMode::SelectedArea:
|
||||
{
|
||||
setTreeInternalAggregateTop(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits);
|
||||
setTreeInternalAggregateTop(
|
||||
_beginTime,
|
||||
blocks,
|
||||
_left,
|
||||
_right,
|
||||
_strict,
|
||||
zeroBlocks,
|
||||
decoratedNames,
|
||||
hexThreadIds,
|
||||
timeUnits
|
||||
);
|
||||
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,
|
||||
::estd::hash<profiler::thread_id_t> >;
|
||||
|
||||
@ -326,13 +458,79 @@ void TreeWidgetLoader::setTreeInternalTop(
|
||||
bool _addZeroBlocks,
|
||||
bool _decoratedThreadNames,
|
||||
bool _hexThreadId,
|
||||
profiler_gui::TimeUnits _units
|
||||
profiler_gui::TimeUnits _units,
|
||||
size_t _maxCount
|
||||
) {
|
||||
BeginEndIndicesMap beginEndMap;
|
||||
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");
|
||||
int i = 0, total = static_cast<int>(_blocks.size());
|
||||
|
||||
i = 0;
|
||||
for (const auto& block : _blocks)
|
||||
{
|
||||
if (interrupted())
|
||||
@ -345,7 +543,7 @@ void TreeWidgetLoader::setTreeInternalTop(
|
||||
|
||||
if (startTime > _right || endTime < _left)
|
||||
{
|
||||
setProgress((95 * ++i) / total);
|
||||
setProgress(3 + (92 * ++i) / total);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -354,7 +552,7 @@ void TreeWidgetLoader::setTreeInternalTop(
|
||||
const bool partial = _strict && (startTime < _left || endTime > _right);
|
||||
if (partial && duration != 0 && (startTime == _right || endTime == _left))
|
||||
{
|
||||
setProgress((95 * ++i) / total);
|
||||
setProgress(3 + (92 * ++i) / total);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -465,7 +663,7 @@ void TreeWidgetLoader::setTreeInternalTop(
|
||||
if (partial && children_items_number == 0)
|
||||
{
|
||||
delete item;
|
||||
setProgress((95 * ++i) / total);
|
||||
setProgress(3 + (92 * ++i) / total);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -481,14 +679,14 @@ void TreeWidgetLoader::setTreeInternalTop(
|
||||
item->setData(COL_SELF_TIME_PERCENT, Qt::UserRole, 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)
|
||||
item->setExpanded(true);
|
||||
|
||||
m_items.insert(std::make_pair(block.tree, item));
|
||||
|
||||
setProgress((95 * ++i) / total);
|
||||
setProgress(3 + (92 * ++i) / total);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
@ -1088,7 +1286,7 @@ size_t TreeWidgetLoader::setTreeInternal(
|
||||
const auto per_parent_stats = child.per_parent_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_sum = profiler_gui::percent(per_parent_stats->total_duration, parent_duration);
|
||||
item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage);
|
||||
@ -1100,7 +1298,7 @@ size_t TreeWidgetLoader::setTreeInternal(
|
||||
{
|
||||
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_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;
|
||||
|
||||
for (auto child_index : _children)
|
||||
@ -1212,6 +1412,45 @@ profiler::timestamp_t TreeWidgetLoader::calculateChildrenDurationRecursive(const
|
||||
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(
|
||||
const profiler::BlocksTreeRoot& threadRoot,
|
||||
IdItems& iditems,
|
||||
@ -1322,7 +1561,8 @@ size_t TreeWidgetLoader::setTreeInternalPlain(
|
||||
item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 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->setText(COL_PERCENT_PER_FRAME, QString::number(percentage_sum));
|
||||
item->setTimeSmart(COL_TIME, units, per_frame_stats->total_duration);
|
||||
@ -1603,12 +1843,15 @@ void TreeWidgetLoader::updateStats(
|
||||
auto stats_it = statsMap.find(id);
|
||||
if (stats_it == statsMap.end())
|
||||
{
|
||||
stats_it = statsMap.emplace(id, profiler::BlockStatistics(duration, index, 0)).first;
|
||||
stats_it->second.total_children_duration = children_duration;
|
||||
stats_it = statsMap.emplace(id, loader::Stats(duration, index, 0)).first;
|
||||
auto& stat = stats_it->second.stats;
|
||||
stat.total_children_duration = children_duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto& stat = stats_it->second;
|
||||
auto& stat = stats_it->second.stats;
|
||||
auto& durations = stats_it->second.durations;
|
||||
|
||||
++stat.calls_number;
|
||||
stat.total_duration += duration;
|
||||
stat.total_children_duration += children_duration;
|
||||
@ -1622,18 +1865,22 @@ void TreeWidgetLoader::updateStats(
|
||||
{
|
||||
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())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
calculate_medians(stats.begin(), stats.end());
|
||||
|
||||
std::deque<TreeWidgetItem*> queue;
|
||||
|
||||
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());
|
||||
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->setText(COL_PERCENT_SUM_PER_AREA, QString::number(percent_per_selection));
|
||||
|
||||
|
@ -68,11 +68,29 @@
|
||||
|
||||
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 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 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; ///<
|
||||
Items m_items; ///<
|
||||
ThreadPoolTask m_worker; ///<
|
||||
QString m_error; ///<
|
||||
std::atomic_bool m_bDone; ///<
|
||||
std::atomic_bool m_bInterrupt; ///<
|
||||
std::atomic<int> m_progress; ///<
|
||||
@ -106,6 +125,8 @@ public:
|
||||
void takeTopLevelItems(ThreadedItems& _output);
|
||||
void takeItems(Items& _output);
|
||||
|
||||
QString error() const;
|
||||
|
||||
void interrupt(bool _wait = false);
|
||||
void fillTreeBlocks(
|
||||
const::profiler_gui::TreeBlocks& _blocks,
|
||||
@ -130,7 +151,8 @@ private:
|
||||
bool _addZeroBlocks,
|
||||
bool _decoratedThreadNames,
|
||||
bool _hexThreadId,
|
||||
::profiler_gui::TimeUnits _units
|
||||
::profiler_gui::TimeUnits _units,
|
||||
size_t _maxCount
|
||||
);
|
||||
|
||||
size_t setTreeInternal(
|
||||
@ -213,11 +235,23 @@ private:
|
||||
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;
|
||||
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.
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user