/************************************************************************ * file name : tree_widget_loader.h * ----------------- : * creation time : 2016/08/18 * author : Victor Zarubkin * email : v.s.zarubkin@gmail.com * ----------------- : * description : The file contains implementation of TreeWidgetLoader which aim is * : to load EasyProfiler blocks hierarchy in separate thread. * ----------------- : * change log : * 2016/08/18 Victor Zarubkin: moved sources from blocks_tree_widget.h/.cpp * : and renamed Prof* to Easy*. * : * : * * ----------------- : * license : Lightweight profiler library for c++ * : Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin * : * : Licensed under either of * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT) * : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0) * : at your option. * : * : The MIT License * : * : Permission is hereby granted, free of charge, to any person obtaining a copy * : of this software and associated documentation files (the "Software"), to deal * : in the Software without restriction, including without limitation the rights * : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies * : of the Software, and to permit persons to whom the Software is furnished * : to do so, subject to the following conditions: * : * : The above copyright notice and this permission notice shall be included in all * : copies or substantial portions of the Software. * : * : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * : USE OR OTHER DEALINGS IN THE SOFTWARE. * : * : The Apache License, Version 2.0 (the "License") * : * : You may not use this file except in compliance with the License. * : You may obtain a copy of the License at * : * : http://www.apache.org/licenses/LICENSE-2.0 * : * : Unless required by applicable law or agreed to in writing, software * : distributed under the License is distributed on an "AS IS" BASIS, * : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * : See the License for the specific language governing permissions and * : limitations under the License. ************************************************************************/ #include "tree_widget_loader.h" #include "tree_widget_item.h" #include "globals.h" #include "thread_pool.h" #ifdef max #undef max #endif #ifdef min #undef min #endif ////////////////////////////////////////////////////////////////////////// TreeWidgetLoader::TreeWidgetLoader() : m_bDone(ATOMIC_VAR_INIT(false)) , m_bInterrupt(ATOMIC_VAR_INIT(false)) , m_progress(ATOMIC_VAR_INIT(0)) , m_mode(TreeMode::Full) { } TreeWidgetLoader::~TreeWidgetLoader() { interrupt(true); } bool TreeWidgetLoader::done() const { return m_bDone.load(std::memory_order_acquire); } void TreeWidgetLoader::setDone() { m_bDone.store(true, std::memory_order_release); //m_progress.store(100); } void TreeWidgetLoader::setProgress(int _progress) { m_progress.store(_progress, std::memory_order_release); } bool TreeWidgetLoader::interrupted() const { return m_bInterrupt.load(std::memory_order_acquire); } int TreeWidgetLoader::progress() const { return m_progress.load(std::memory_order_acquire); } void TreeWidgetLoader::takeTopLevelItems(ThreadedItems& _output) { if (done()) { _output = ::std::move(m_topLevelItems); m_topLevelItems.clear(); } } void TreeWidgetLoader::takeItems(Items& _output) { if (done()) { _output = ::std::move(m_items); m_items.clear(); } } void TreeWidgetLoader::interrupt(bool _wait) { m_worker.dequeue(); m_bDone.store(false, std::memory_order_release); m_progress.store(0, std::memory_order_release); if (!m_topLevelItems.empty()) { if (!_wait) { auto items = std::move(m_topLevelItems); ThreadPool::instance().backgroundJob([=] { for (auto item : items) delete item.second; }); } else { for (auto item : m_topLevelItems) delete item.second; } } m_items.clear(); m_topLevelItems.clear(); m_iditems.clear(); } void TreeWidgetLoader::fillTree(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, TreeMode _mode) { interrupt(); m_mode = _mode; const auto zeroBlocks = EASY_GLOBALS.add_zero_blocks_to_hierarchy; 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 beginTime = std::ref(_beginTime); const auto blocksTree = std::ref(_blocksTree); m_worker.enqueue([=] { setTreeInternal1(beginTime, _blocksNumber, blocksTree, zeroBlocks, decoratedNames, hexThreadIds, timeUnits); }, m_bInterrupt); } void TreeWidgetLoader::fillTreeBlocks(const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _beginTime, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, TreeMode _mode) { interrupt(); m_mode = _mode; const auto zeroBlocks = EASY_GLOBALS.add_zero_blocks_to_hierarchy; 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 blocks = std::ref(_blocks); m_worker.enqueue([=] { setTreeInternal2(_beginTime, blocks, _left, _right, _strict, zeroBlocks, decoratedNames, hexThreadIds, timeUnits); }, m_bInterrupt); } ////////////////////////////////////////////////////////////////////////// void TreeWidgetLoader::setTreeInternal1(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units) { m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks ::profiler::timestamp_t finishtime = 0; for (const auto& threadTree : _blocksTree) { const auto node_block = easyBlocksTree(threadTree.second.children.front()).node; const auto startTime = node_block->begin(); const auto endTime = node_block->end(); if (_beginTime > startTime) _beginTime = startTime; if (finishtime < endTime) finishtime = endTime; } //const QSignalBlocker b(this); const auto u_thread = ::profiler_gui::toUnicode("thread"); int i = 0; const int total = static_cast(_blocksTree.size()); for (const auto& threadTree : _blocksTree) { if (interrupted()) break; const auto& root = threadTree.second; auto item = new TreeWidgetItem(); item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, root, u_thread, _hexThreadId)); ::profiler::timestamp_t duration = 0; if (!root.children.empty()) duration = easyBlocksTree(root.children.back()).node->end() - easyBlocksTree(root.children.front()).node->begin(); item->setTimeSmart(COL_DURATION, _units, duration); item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); //_items.push_back(item); item->setTimeSmart(COL_SELF_DURATION, _units, root.profiled_time); ::profiler::timestamp_t children_duration = 0; const auto children_items_number = setTreeInternal(root, 0, _beginTime, root.children, item, nullptr, _beginTime, finishtime + 1000000000ULL, false, children_duration, _addZeroBlocks, _units); if (children_items_number > 0) { //total_items += children_items_number + 1; //addTopLevelItem(item); //m_roots[threadTree.first] = item; m_topLevelItems.emplace_back(root.thread_id, item); } else { //_items.pop_back(); delete item; } setProgress((100 * ++i) / total); } setDone(); //return total_items; } ////////////////////////////////////////////////////////////////////////// // 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> >; void TreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTime, const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units) { //size_t blocksNumber = 0; //for (const auto& block : _blocks) // blocksNumber += calculateTotalChildrenNumber(*block.tree); // //blocksNumber += block.tree->total_children_number; //m_items.reserve(blocksNumber + _blocks.size()); // blocksNumber does not include root blocks BeginEndIndicesMap beginEndMap; RootsMap threadsMap; auto const setTree = (m_mode == TreeMode::Full) ? &TreeWidgetLoader::setTreeInternal : &TreeWidgetLoader::setTreeInternalPlain; const auto u_thread = ::profiler_gui::toUnicode("thread"); int i = 0, total = static_cast(_blocks.size()); //const QSignalBlocker b(this); for (const auto& block : _blocks) { if (interrupted()) break; auto& gui_block = easyBlock(block.tree); const auto startTime = gui_block.tree.node->begin(); const auto endTime = gui_block.tree.node->end(); if (startTime > _right || endTime < _left) { setProgress((95 * ++i) / total); continue; } ::profiler::timestamp_t duration = 0; TreeWidgetItem* thread_item = nullptr; ::profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id]; auto thread_item_it = threadsMap.find(block.root->thread_id); if (thread_item_it != threadsMap.end()) { thread_item = thread_item_it->second; } else { thread_item = new TreeWidgetItem(); thread_item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread, _hexThreadId)); if (!block.root->children.empty()) duration = easyBlocksTree(block.root->children.back()).node->end() - easyBlocksTree(block.root->children.front()).node->begin(); thread_item->setTimeSmart(COL_DURATION, _units, duration); thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND); // Sum of all children durations: thread_item->setTimeSmart(COL_SELF_DURATION, _units, block.root->profiled_time); threadsMap.insert(::std::make_pair(block.root->thread_id, thread_item)); firstCswitch = 0; auto it = ::std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [](::profiler::block_index_t ind, decltype(_left) _val) { return EASY_GLOBALS.gui_blocks[ind].tree.node->begin() < _val; }); if (it != block.root->sync.end()) { firstCswitch = it - block.root->sync.begin(); if (firstCswitch > 0) --firstCswitch; } else { firstCswitch = static_cast<::profiler::block_index_t>(block.root->sync.size()); } } bool hasContextSwitch = false; ::profiler::timestamp_t idleTime = 0; for (::profiler::block_index_t ind = firstCswitch, ncs = static_cast<::profiler::block_index_t>(block.root->sync.size()); ind < ncs; ++ind) { auto cs_index = block.root->sync[ind]; const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; if (cs->begin() > endTime) { if (!hasContextSwitch) firstCswitch = ind; break; } if (startTime <= cs->begin() && cs->end() <= endTime) { if (!hasContextSwitch) { firstCswitch = ind; hasContextSwitch = true; } idleTime += cs->duration(); } } auto item = new TreeWidgetItem(block.tree, thread_item); duration = endTime - startTime; auto name = *gui_block.tree.node->name() != 0 ? gui_block.tree.node->name() : easyDescriptor(gui_block.tree.node->id()).name(); item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); item->setTimeSmart(COL_DURATION, _units, duration); auto active_time = duration - idleTime; auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration); item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); item->setTimeMs(COL_BEGIN, startTime - _beginTime); item->setTimeMs(COL_END, endTime - _beginTime); item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); auto percentage_per_thread = ::profiler_gui::percent(duration, block.root->profiled_time); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); if (gui_block.tree.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also { const ::profiler::BlockStatistics* per_thread_stats = gui_block.tree.per_thread_stats; const ::profiler::BlockStatistics* per_parent_stats = gui_block.tree.per_parent_stats; const ::profiler::BlockStatistics* per_frame_stats = gui_block.tree.per_frame_stats; if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) { item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); } item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) { item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration()); item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration()); item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration()); item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration); } item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number); item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number)); if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) { item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration); } item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); } else { item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); item->setText(COL_PERCENT_SUM_PER_THREAD, ""); } const auto color = easyDescriptor(gui_block.tree.node->id()).color(); item->setBackgroundColor(color); #ifdef EASY_TREE_WIDGET__USE_VECTOR auto item_index = static_cast(m_items.size()); m_items.push_back(item); #endif size_t children_items_number = 0; ::profiler::timestamp_t children_duration = 0; if (!gui_block.tree.children.empty()) { m_iditems.clear(); children_items_number = (this->*setTree)(*block.root, firstCswitch, _beginTime, gui_block.tree.children, item, item, _left, _right, _strict, children_duration, _addZeroBlocks, _units); if (interrupted()) break; } int percentage = 100; auto self_duration = duration - children_duration; if (children_duration > 0 && duration > 0) { percentage = static_cast(0.5 + 100. * static_cast(self_duration) / static_cast(duration)); } item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) { //total_items += children_items_number + 1; #ifdef EASY_TREE_WIDGET__USE_VECTOR gui_block.tree_item = item_index; #endif if (gui_block.expanded) item->setExpanded(true); #ifndef EASY_TREE_WIDGET__USE_VECTOR m_items.insert(::std::make_pair(block.tree, item)); #endif } else { #ifdef EASY_TREE_WIDGET__USE_VECTOR m_items.pop_back(); #endif delete item; } setProgress((95 * ++i) / total); } i = 0; total = static_cast(threadsMap.size()); for (auto& it : threadsMap) { auto item = it.second; if (item->childCount() > 0) { //addTopLevelItem(item); //m_roots[it.first] = item; //_items.push_back(item); m_topLevelItems.emplace_back(it.first, item); //++total_items; } else { delete item; } setProgress(95 + (5 * ++i) / total); } setDone(); //return total_items; } ////////////////////////////////////////////////////////////////////////// size_t TreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, TreeWidgetItem* _parent, TreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units) { auto const setTree = m_mode == TreeMode::Full ? &TreeWidgetLoader::setTreeInternal : &TreeWidgetLoader::setTreeInternalPlain; size_t total_items = 0; for (auto child_index : _children) { if (interrupted()) break; 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 (duration == 0 && !_addZeroBlocks && easyDescriptor(child.node->id()).type() == profiler::BlockType::Block) continue; _duration += duration; if (startTime > _right || endTime < _left) continue; bool hasContextSwitch = false; ::profiler::timestamp_t idleTime = 0; for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) { auto cs_index = _threadRoot.sync[ind]; const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; if (cs->begin() > endTime) { if (!hasContextSwitch) _firstCswitch = ind; break; } if (startTime <= cs->begin() && cs->end() <= endTime) { if (!hasContextSwitch) { _firstCswitch = ind; hasContextSwitch = true; } idleTime += cs->duration(); } } auto item = new TreeWidgetItem(child_index, _parent); auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name(); item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); item->setTimeSmart(COL_DURATION, _units, duration); auto active_time = duration - idleTime; auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration); item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); item->setTimeMs(COL_BEGIN, startTime - _beginTime); item->setTimeMs(COL_END, endTime - _beginTime); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also { const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats; const ::profiler::BlockStatistics* per_parent_stats = child.per_parent_stats; const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats; auto parent_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); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage)); item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, percentage_sum); item->setText(COL_PERCENT_SUM_PER_PARENT, QString::number(percentage_sum)); if (_frame != nullptr) { if (_parent != _frame) { parent_duration = _frame->duration(); percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration); percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, parent_duration); } item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage); item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage)); item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, percentage_sum); item->setText(COL_PERCENT_SUM_PER_FRAME, QString::number(percentage_sum)); } else { item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, 0); auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); } if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) { item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); } item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) { item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration()); item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration()); item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration()); item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration); } item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number); item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number)); if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) { item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration); } item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); } else { if (_frame == nullptr) { auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time); item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread)); } else { item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, 0); } item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, 0); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); } const auto color = easyDescriptor(child.node->id()).color(); item->setBackgroundColor(color); #ifdef EASY_TREE_WIDGET__USE_VECTOR auto item_index = static_cast(m_items.size()); m_items.push_back(item); #endif size_t children_items_number = 0; ::profiler::timestamp_t children_duration = 0; if (!child.children.empty()) { m_iditems.clear(); children_items_number = (this->*setTree)(_threadRoot, _firstCswitch, _beginTime, child.children, item, _frame ? _frame : item, _left, _right, _strict, children_duration, _addZeroBlocks, _units); if (interrupted()) break; } int percentage = 100; auto self_duration = duration - children_duration; if (children_duration > 0 && duration > 0) { percentage = ::profiler_gui::percent(self_duration, duration); } item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) { total_items += children_items_number + 1; #ifdef EASY_TREE_WIDGET__USE_VECTOR gui_block.tree_item = item_index; #endif if (gui_block.expanded) item->setExpanded(true); #ifndef EASY_TREE_WIDGET__USE_VECTOR m_items.insert(::std::make_pair(child_index, item)); #endif } else { #ifdef EASY_TREE_WIDGET__USE_VECTOR m_items.pop_back(); #endif delete item; } } return total_items; } ////////////////////////////////////////////////////////////////////////// ::profiler::timestamp_t TreeWidgetLoader::calculateChildrenDurationRecursive(const ::profiler::BlocksTree::children_t& _children, ::profiler::block_id_t _id) { ::profiler::timestamp_t total_duration = 0; for (auto child_index : _children) { if (interrupted()) break; const auto& gui_block = easyBlock(child_index); total_duration += gui_block.tree.node->duration(); if (gui_block.tree.node->id() == _id) total_duration += calculateChildrenDurationRecursive(gui_block.tree.children, _id); } return total_duration; } size_t TreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, TreeWidgetItem*, TreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units) { size_t total_items = 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; _duration += duration; auto it = m_iditems.find(child.node->id()); if (it != m_iditems.end()) { ++total_items; ::profiler::timestamp_t children_duration = 0; if (!child.children.empty()) { setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, _right, _strict, children_duration, _addZeroBlocks, _units); if (interrupted()) break; } if (it->second != nullptr && child.per_frame_stats != nullptr) { auto item = it->second; //auto children_duration = calculateChildrenDurationRecursive(child.children, it->first); if (children_duration != 0) { auto self_duration = item->data(COL_SELF_DURATION, Qt::UserRole).toULongLong() - children_duration; int percentage = 100; if (child.per_frame_stats->total_duration > 0) percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration); item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); } bool hasContextSwitch = false; ::profiler::timestamp_t idleTime = 0; for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) { auto cs_index = _threadRoot.sync[ind]; const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; if (cs->begin() > endTime) { if (!hasContextSwitch) _firstCswitch = ind; break; } if (startTime <= cs->begin() && cs->end() <= endTime) { if (!hasContextSwitch) { _firstCswitch = ind; hasContextSwitch = true; } idleTime += cs->duration(); } } auto active_time = item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong() - idleTime; auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration); item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); } continue; } if (startTime > _right || endTime < _left) continue; bool hasContextSwitch = false; ::profiler::timestamp_t idleTime = 0; for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind) { auto cs_index = _threadRoot.sync[ind]; const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node; if (cs->begin() > endTime) { if (!hasContextSwitch) _firstCswitch = ind; break; } if (startTime <= cs->begin() && cs->end() <= endTime) { if (!hasContextSwitch) { _firstCswitch = ind; hasContextSwitch = true; } idleTime += cs->duration(); } } auto item = new TreeWidgetItem(child_index, _frame); auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name(); item->setText(COL_NAME, ::profiler_gui::toUnicode(name)); if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also { const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats; if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) { item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration()); item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration()); item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration()); } item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration); item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number); item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number)); auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time); item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread); item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread)); const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats; 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)); if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats) { item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration()); item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration()); item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration()); } item->setTimeSmart(COL_DURATION, _units, per_frame_stats->total_duration); item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number); item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number)); } else { item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0); item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0); } const auto color = easyDescriptor(child.node->id()).color(); item->setBackgroundColor(color); #ifdef EASY_TREE_WIDGET__USE_VECTOR auto item_index = static_cast(m_items.size()); m_items.push_back(item); #endif m_iditems[child.node->id()] = nullptr; size_t children_items_number = 0; ::profiler::timestamp_t children_duration = 0; if (!child.children.empty()) { children_items_number = setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, _right, _strict, children_duration, _addZeroBlocks, _units); if (interrupted()) break; } m_iditems[child.node->id()] = item; if (child.per_frame_stats != nullptr) { int percentage = 100; auto self_duration = child.per_frame_stats->total_duration - children_duration; if (child.per_frame_stats->total_duration > 0) percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration); item->setTimeSmart(COL_SELF_DURATION, _units, self_duration); item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage); item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage)); auto active_time = child.per_frame_stats->total_duration - idleTime; auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration); item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time); item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3)); item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent); } if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right)) { total_items += children_items_number + 1; #ifdef EASY_TREE_WIDGET__USE_VECTOR gui_block.tree_item = item_index; #endif if (gui_block.expanded) item->setExpanded(true); #ifndef EASY_TREE_WIDGET__USE_VECTOR m_items.insert(::std::make_pair(child_index, item)); #endif } else { #ifdef EASY_TREE_WIDGET__USE_VECTOR m_items.pop_back(); #endif delete item; m_iditems.erase(gui_block.tree.node->id()); } } return total_items; } //////////////////////////////////////////////////////////////////////////