tree_widget_loader.cpp 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. /************************************************************************
  2. * file name : tree_widget_loader.h
  3. * ----------------- :
  4. * creation time : 2016/08/18
  5. * author : Victor Zarubkin
  6. * email : [email protected]
  7. * ----------------- :
  8. * description : The file contains implementation of EasyTreeWidgetLoader which aim is
  9. * : to load EasyProfiler blocks hierarchy in separate thread.
  10. * ----------------- :
  11. * change log : * 2016/08/18 Victor Zarubkin: moved sources from blocks_tree_widget.h/.cpp
  12. * : and renamed Prof* to Easy*.
  13. * :
  14. * : *
  15. * ----------------- :
  16. * license : Lightweight profiler library for c++
  17. * : Copyright(C) 2016-2017 Sergey Yagovtsev, Victor Zarubkin
  18. * :
  19. * : Licensed under either of
  20. * : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
  21. * : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
  22. * : at your option.
  23. * :
  24. * : The MIT License
  25. * :
  26. * : Permission is hereby granted, free of charge, to any person obtaining a copy
  27. * : of this software and associated documentation files (the "Software"), to deal
  28. * : in the Software without restriction, including without limitation the rights
  29. * : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  30. * : of the Software, and to permit persons to whom the Software is furnished
  31. * : to do so, subject to the following conditions:
  32. * :
  33. * : The above copyright notice and this permission notice shall be included in all
  34. * : copies or substantial portions of the Software.
  35. * :
  36. * : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  37. * : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  38. * : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  39. * : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  40. * : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  41. * : USE OR OTHER DEALINGS IN THE SOFTWARE.
  42. * :
  43. * : The Apache License, Version 2.0 (the "License")
  44. * :
  45. * : You may not use this file except in compliance with the License.
  46. * : You may obtain a copy of the License at
  47. * :
  48. * : http://www.apache.org/licenses/LICENSE-2.0
  49. * :
  50. * : Unless required by applicable law or agreed to in writing, software
  51. * : distributed under the License is distributed on an "AS IS" BASIS,
  52. * : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  53. * : See the License for the specific language governing permissions and
  54. * : limitations under the License.
  55. ************************************************************************/
  56. #include "tree_widget_loader.h"
  57. #include "tree_widget_item.h"
  58. #include "globals.h"
  59. #ifdef _WIN32
  60. #include <Windows.h>
  61. #ifdef __MINGW32__
  62. #include <processthreadsapi.h>
  63. #endif
  64. #endif
  65. #ifdef max
  66. #undef max
  67. #endif
  68. #ifdef min
  69. #undef min
  70. #endif
  71. //////////////////////////////////////////////////////////////////////////
  72. EasyTreeWidgetLoader::EasyTreeWidgetLoader()
  73. : m_bDone(ATOMIC_VAR_INIT(false))
  74. , m_bInterrupt(ATOMIC_VAR_INIT(false))
  75. , m_progress(ATOMIC_VAR_INIT(0))
  76. , m_mode(EasyTreeMode_Full)
  77. {
  78. }
  79. EasyTreeWidgetLoader::~EasyTreeWidgetLoader()
  80. {
  81. interrupt(true);
  82. }
  83. bool EasyTreeWidgetLoader::done() const
  84. {
  85. return m_bDone.load();
  86. }
  87. void EasyTreeWidgetLoader::setDone()
  88. {
  89. m_bDone.store(true);
  90. //m_progress.store(100);
  91. }
  92. void EasyTreeWidgetLoader::setProgress(int _progress)
  93. {
  94. m_progress.store(_progress);
  95. }
  96. bool EasyTreeWidgetLoader::interrupted() const
  97. {
  98. return m_bInterrupt.load();
  99. }
  100. int EasyTreeWidgetLoader::progress() const
  101. {
  102. return m_progress.load();
  103. }
  104. void EasyTreeWidgetLoader::takeTopLevelItems(ThreadedItems& _output)
  105. {
  106. if (done())
  107. {
  108. _output = ::std::move(m_topLevelItems);
  109. m_topLevelItems.clear();
  110. }
  111. }
  112. void EasyTreeWidgetLoader::takeItems(Items& _output)
  113. {
  114. if (done())
  115. {
  116. _output = ::std::move(m_items);
  117. m_items.clear();
  118. }
  119. }
  120. void EasyTreeWidgetLoader::interrupt(bool _wait)
  121. {
  122. m_bInterrupt.store(true);
  123. if (m_thread.joinable())
  124. m_thread.join();
  125. m_bInterrupt.store(false);
  126. m_bDone.store(false);
  127. m_progress.store(0);
  128. if (!_wait)
  129. {
  130. auto deleter_thread = ::std::thread([](decltype(m_topLevelItems) _items)
  131. {
  132. #ifdef _WIN32
  133. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
  134. #endif
  135. for (auto item : _items)
  136. delete item.second;
  137. }, ::std::move(m_topLevelItems));
  138. deleter_thread.detach();
  139. }
  140. else
  141. {
  142. for (auto item : m_topLevelItems)
  143. delete item.second;
  144. }
  145. m_items.clear();
  146. m_topLevelItems.clear();
  147. m_iditems.clear();
  148. }
  149. void EasyTreeWidgetLoader::fillTree(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _colorizeRows, EasyTreeMode _mode)
  150. {
  151. interrupt();
  152. m_mode = _mode;
  153. m_thread = ::std::thread(&EasyTreeWidgetLoader::setTreeInternal1, this,
  154. ::std::ref(_beginTime), _blocksNumber, ::std::ref(_blocksTree), _colorizeRows,
  155. EASY_GLOBALS.add_zero_blocks_to_hierarchy, EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.hex_thread_id, EASY_GLOBALS.time_units);
  156. }
  157. void EasyTreeWidgetLoader::fillTreeBlocks(const::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _beginTime, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _colorizeRows, EasyTreeMode _mode)
  158. {
  159. interrupt();
  160. m_mode = _mode;
  161. m_thread = ::std::thread(&EasyTreeWidgetLoader::setTreeInternal2, this,
  162. _beginTime, ::std::ref(_blocks), _left, _right, _strict, _colorizeRows,
  163. EASY_GLOBALS.add_zero_blocks_to_hierarchy, EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.hex_thread_id, EASY_GLOBALS.time_units);
  164. }
  165. //////////////////////////////////////////////////////////////////////////
  166. void EasyTreeWidgetLoader::setTreeInternal1(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _colorizeRows, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units)
  167. {
  168. m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks
  169. ::profiler::timestamp_t finishtime = 0;
  170. for (const auto& threadTree : _blocksTree)
  171. {
  172. const auto node_block = blocksTree(threadTree.second.children.front()).node;
  173. const auto startTime = node_block->begin();
  174. const auto endTime = node_block->end();
  175. if (_beginTime > startTime)
  176. _beginTime = startTime;
  177. if (finishtime < endTime)
  178. finishtime = endTime;
  179. }
  180. //const QSignalBlocker b(this);
  181. const auto u_thread = ::profiler_gui::toUnicode("thread");
  182. int i = 0;
  183. const int total = static_cast<int>(_blocksTree.size());
  184. for (const auto& threadTree : _blocksTree)
  185. {
  186. if (interrupted())
  187. break;
  188. const auto& root = threadTree.second;
  189. auto item = new EasyTreeWidgetItem();
  190. item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, root, u_thread, _hexThreadId));
  191. ::profiler::timestamp_t duration = 0;
  192. if (!root.children.empty())
  193. duration = blocksTree(root.children.back()).node->end() - blocksTree(root.children.front()).node->begin();
  194. item->setTimeSmart(COL_DURATION, _units, duration);
  195. item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
  196. item->setTextColor(::profiler_gui::SELECTED_THREAD_FOREGROUND);
  197. //_items.push_back(item);
  198. item->setTimeSmart(COL_SELF_DURATION, _units, root.profiled_time);
  199. ::profiler::timestamp_t children_duration = 0;
  200. const auto children_items_number = setTreeInternal(root, 0, _beginTime, root.children, item, nullptr, _beginTime, finishtime + 1000000000ULL, false, children_duration, _colorizeRows, _addZeroBlocks, _units);
  201. if (children_items_number > 0)
  202. {
  203. //total_items += children_items_number + 1;
  204. //addTopLevelItem(item);
  205. //m_roots[threadTree.first] = item;
  206. m_topLevelItems.emplace_back(root.thread_id, item);
  207. }
  208. else
  209. {
  210. //_items.pop_back();
  211. delete item;
  212. }
  213. setProgress((100 * ++i) / total);
  214. }
  215. setDone();
  216. //return total_items;
  217. }
  218. //////////////////////////////////////////////////////////////////////////
  219. // auto calculateTotalChildrenNumber(const ::profiler::BlocksTree& _tree) -> decltype(_tree.children.size())
  220. // {
  221. // auto children_number = _tree.children.size();
  222. // for (auto i : _tree.children)
  223. // children_number += calculateTotalChildrenNumber(blocksTree(i));
  224. // return children_number;
  225. // }
  226. typedef ::std::unordered_map<::profiler::thread_id_t, ::profiler::block_index_t, ::profiler::passthrough_hash<::profiler::thread_id_t> > BeginEndIndicesMap;
  227. void EasyTreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTime, const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _colorizeRows, bool _addZeroBlocks, bool _decoratedThreadNames, bool _hexThreadId, ::profiler_gui::TimeUnits _units)
  228. {
  229. //size_t blocksNumber = 0;
  230. //for (const auto& block : _blocks)
  231. // blocksNumber += calculateTotalChildrenNumber(*block.tree);
  232. // //blocksNumber += block.tree->total_children_number;
  233. //m_items.reserve(blocksNumber + _blocks.size()); // blocksNumber does not include root blocks
  234. BeginEndIndicesMap beginEndMap;
  235. RootsMap threadsMap;
  236. auto const setTree = (m_mode == EasyTreeMode_Full) ? &EasyTreeWidgetLoader::setTreeInternal : &EasyTreeWidgetLoader::setTreeInternalPlain;
  237. const auto u_thread = ::profiler_gui::toUnicode("thread");
  238. int i = 0, total = static_cast<int>(_blocks.size());
  239. //const QSignalBlocker b(this);
  240. for (const auto& block : _blocks)
  241. {
  242. if (interrupted())
  243. break;
  244. auto& gui_block = easyBlock(block.tree);
  245. const auto startTime = gui_block.tree.node->begin();
  246. const auto endTime = gui_block.tree.node->end();
  247. if (startTime > _right || endTime < _left)
  248. {
  249. setProgress((90 * ++i) / total);
  250. continue;
  251. }
  252. ::profiler::timestamp_t duration = 0;
  253. EasyTreeWidgetItem* thread_item = nullptr;
  254. ::profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id];
  255. auto thread_item_it = threadsMap.find(block.root->thread_id);
  256. if (thread_item_it != threadsMap.end())
  257. {
  258. thread_item = thread_item_it->second;
  259. }
  260. else
  261. {
  262. thread_item = new EasyTreeWidgetItem();
  263. thread_item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread, _hexThreadId));
  264. if (!block.root->children.empty())
  265. duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin();
  266. thread_item->setTimeSmart(COL_DURATION, _units, duration);
  267. thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
  268. thread_item->setTextColor(::profiler_gui::SELECTED_THREAD_FOREGROUND);
  269. // Sum of all children durations:
  270. thread_item->setTimeSmart(COL_SELF_DURATION, _units, block.root->profiled_time);
  271. threadsMap.insert(::std::make_pair(block.root->thread_id, thread_item));
  272. firstCswitch = 0;
  273. auto it = ::std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [](::profiler::block_index_t ind, decltype(_left) _val)
  274. {
  275. return EASY_GLOBALS.gui_blocks[ind].tree.node->begin() < _val;
  276. });
  277. if (it != block.root->sync.end())
  278. {
  279. firstCswitch = it - block.root->sync.begin();
  280. if (firstCswitch > 0)
  281. --firstCswitch;
  282. }
  283. else
  284. {
  285. firstCswitch = static_cast<::profiler::block_index_t>(block.root->sync.size());
  286. }
  287. }
  288. bool hasContextSwitch = false;
  289. ::profiler::timestamp_t idleTime = 0;
  290. for (::profiler::block_index_t ind = firstCswitch, ncs = static_cast<::profiler::block_index_t>(block.root->sync.size()); ind < ncs; ++ind)
  291. {
  292. auto cs_index = block.root->sync[ind];
  293. const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
  294. if (cs->begin() > endTime)
  295. {
  296. if (!hasContextSwitch)
  297. firstCswitch = ind;
  298. break;
  299. }
  300. if (startTime <= cs->begin() && cs->end() <= endTime)
  301. {
  302. if (!hasContextSwitch)
  303. {
  304. firstCswitch = ind;
  305. hasContextSwitch = true;
  306. }
  307. idleTime += cs->duration();
  308. }
  309. }
  310. auto item = new EasyTreeWidgetItem(block.tree, thread_item);
  311. duration = endTime - startTime;
  312. auto name = *gui_block.tree.node->name() != 0 ? gui_block.tree.node->name() : easyDescriptor(gui_block.tree.node->id()).name();
  313. item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
  314. item->setTimeSmart(COL_DURATION, _units, duration);
  315. auto active_time = duration - idleTime;
  316. auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
  317. item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
  318. item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
  319. item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
  320. item->setTimeMs(COL_BEGIN, startTime - _beginTime);
  321. item->setTimeMs(COL_END, endTime - _beginTime);
  322. item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
  323. auto percentage_per_thread = ::profiler_gui::percent(duration, block.root->profiled_time);
  324. item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
  325. item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
  326. if (gui_block.tree.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
  327. {
  328. const ::profiler::BlockStatistics* per_thread_stats = gui_block.tree.per_thread_stats;
  329. const ::profiler::BlockStatistics* per_parent_stats = gui_block.tree.per_parent_stats;
  330. const ::profiler::BlockStatistics* per_frame_stats = gui_block.tree.per_frame_stats;
  331. if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
  332. {
  333. item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration());
  334. item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration());
  335. item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
  336. item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
  337. }
  338. item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
  339. item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
  340. percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time);
  341. item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
  342. item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
  343. if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
  344. {
  345. item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration());
  346. item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration());
  347. item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration());
  348. item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration);
  349. }
  350. item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number);
  351. item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number));
  352. if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
  353. {
  354. item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration());
  355. item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration());
  356. item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
  357. item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration);
  358. }
  359. item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
  360. item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
  361. }
  362. else
  363. {
  364. item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
  365. item->setText(COL_PERCENT_SUM_PER_THREAD, "");
  366. }
  367. const auto color = easyDescriptor(gui_block.tree.node->id()).color();
  368. //const auto bgColor = ::profiler_gui::fromProfilerRgb(::profiler::colors::get_red(color), ::profiler::colors::get_green(color), ::profiler::colors::get_blue(color));
  369. const auto fgColor = ::profiler_gui::textColorForRgb(color);//0x00ffffff - bgColor;
  370. item->setBackgroundColor(color);
  371. item->setTextColor(fgColor);
  372. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  373. auto item_index = static_cast<unsigned int>(m_items.size());
  374. m_items.push_back(item);
  375. #endif
  376. size_t children_items_number = 0;
  377. ::profiler::timestamp_t children_duration = 0;
  378. if (!gui_block.tree.children.empty())
  379. {
  380. m_iditems.clear();
  381. children_items_number = (this->*setTree)(*block.root, firstCswitch, _beginTime, gui_block.tree.children, item, item, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
  382. if (interrupted())
  383. break;
  384. }
  385. int percentage = 100;
  386. auto self_duration = duration - children_duration;
  387. if (children_duration > 0 && duration > 0)
  388. {
  389. percentage = static_cast<int>(0.5 + 100. * static_cast<double>(self_duration) / static_cast<double>(duration));
  390. }
  391. item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
  392. item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
  393. item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
  394. if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
  395. {
  396. //total_items += children_items_number + 1;
  397. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  398. gui_block.tree_item = item_index;
  399. #endif
  400. if (_colorizeRows)
  401. item->colorize(_colorizeRows);
  402. if (gui_block.expanded)
  403. item->setExpanded(true);
  404. #ifndef EASY_TREE_WIDGET__USE_VECTOR
  405. m_items.insert(::std::make_pair(block.tree, item));
  406. #endif
  407. }
  408. else
  409. {
  410. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  411. m_items.pop_back();
  412. #endif
  413. delete item;
  414. }
  415. setProgress((90 * ++i) / total);
  416. }
  417. i = 0;
  418. total = static_cast<int>(threadsMap.size());
  419. for (auto& it : threadsMap)
  420. {
  421. auto item = it.second;
  422. if (item->childCount() > 0)
  423. {
  424. //addTopLevelItem(item);
  425. //m_roots[it.first] = item;
  426. //_items.push_back(item);
  427. m_topLevelItems.emplace_back(it.first, item);
  428. //++total_items;
  429. }
  430. else
  431. {
  432. delete item;
  433. }
  434. setProgress(90 + (10 * ++i) / total);
  435. }
  436. setDone();
  437. //return total_items;
  438. }
  439. //////////////////////////////////////////////////////////////////////////
  440. size_t EasyTreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, EasyTreeWidgetItem* _parent, EasyTreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _colorizeRows, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units)
  441. {
  442. auto const setTree = m_mode == EasyTreeMode_Full ? &EasyTreeWidgetLoader::setTreeInternal : &EasyTreeWidgetLoader::setTreeInternalPlain;
  443. size_t total_items = 0;
  444. for (auto child_index : _children)
  445. {
  446. if (interrupted())
  447. break;
  448. auto& gui_block = easyBlock(child_index);
  449. const auto& child = gui_block.tree;
  450. const auto startTime = child.node->begin();
  451. const auto endTime = child.node->end();
  452. const auto duration = endTime - startTime;
  453. if (duration == 0 && !_addZeroBlocks)
  454. continue;
  455. _duration += duration;
  456. if (startTime > _right || endTime < _left)
  457. continue;
  458. bool hasContextSwitch = false;
  459. ::profiler::timestamp_t idleTime = 0;
  460. for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
  461. {
  462. auto cs_index = _threadRoot.sync[ind];
  463. const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
  464. if (cs->begin() > endTime)
  465. {
  466. if (!hasContextSwitch)
  467. _firstCswitch = ind;
  468. break;
  469. }
  470. if (startTime <= cs->begin() && cs->end() <= endTime)
  471. {
  472. if (!hasContextSwitch)
  473. {
  474. _firstCswitch = ind;
  475. hasContextSwitch = true;
  476. }
  477. idleTime += cs->duration();
  478. }
  479. }
  480. auto item = new EasyTreeWidgetItem(child_index, _parent);
  481. auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name();
  482. item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
  483. item->setTimeSmart(COL_DURATION, _units, duration);
  484. auto active_time = duration - idleTime;
  485. auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
  486. item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
  487. item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
  488. item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
  489. item->setTimeMs(COL_BEGIN, startTime - _beginTime);
  490. item->setTimeMs(COL_END, endTime - _beginTime);
  491. item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
  492. if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
  493. {
  494. const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats;
  495. const ::profiler::BlockStatistics* per_parent_stats = child.per_parent_stats;
  496. const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats;
  497. auto parent_duration = _parent->duration();
  498. auto percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration);
  499. auto percentage_sum = ::profiler_gui::percent(per_parent_stats->total_duration, parent_duration);
  500. item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage);
  501. item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage));
  502. item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, percentage_sum);
  503. item->setText(COL_PERCENT_SUM_PER_PARENT, QString::number(percentage_sum));
  504. if (_frame != nullptr)
  505. {
  506. if (_parent != _frame)
  507. {
  508. parent_duration = _frame->duration();
  509. percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration);
  510. percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, parent_duration);
  511. }
  512. item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage);
  513. item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage));
  514. item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, percentage_sum);
  515. item->setText(COL_PERCENT_SUM_PER_FRAME, QString::number(percentage_sum));
  516. }
  517. else
  518. {
  519. item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
  520. item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, 0);
  521. auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time);
  522. item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
  523. item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
  524. }
  525. if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
  526. {
  527. item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration());
  528. item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration());
  529. item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
  530. item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
  531. }
  532. item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
  533. item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
  534. auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time);
  535. item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
  536. item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
  537. if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
  538. {
  539. item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration());
  540. item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration());
  541. item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration());
  542. item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration);
  543. }
  544. item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number);
  545. item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number));
  546. if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
  547. {
  548. item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration());
  549. item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration());
  550. item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
  551. item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration);
  552. }
  553. item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
  554. item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
  555. }
  556. else
  557. {
  558. if (_frame == nullptr)
  559. {
  560. auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time);
  561. item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
  562. item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
  563. }
  564. else
  565. {
  566. item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, 0);
  567. }
  568. item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, 0);
  569. item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
  570. }
  571. const auto color = easyDescriptor(child.node->id()).color();
  572. //const auto bgColor = ::profiler_gui::fromProfilerRgb(::profiler::colors::get_red(color), ::profiler::colors::get_green(color), ::profiler::colors::get_blue(color));
  573. const auto fgColor = ::profiler_gui::textColorForRgb(color);// 0x00ffffff - bgColor;
  574. item->setBackgroundColor(color);
  575. item->setTextColor(fgColor);
  576. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  577. auto item_index = static_cast<uint32_t>(m_items.size());
  578. m_items.push_back(item);
  579. #endif
  580. size_t children_items_number = 0;
  581. ::profiler::timestamp_t children_duration = 0;
  582. if (!child.children.empty())
  583. {
  584. m_iditems.clear();
  585. children_items_number = (this->*setTree)(_threadRoot, _firstCswitch, _beginTime, child.children, item, _frame ? _frame : item, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
  586. if (interrupted())
  587. break;
  588. }
  589. int percentage = 100;
  590. auto self_duration = duration - children_duration;
  591. if (children_duration > 0 && duration > 0)
  592. {
  593. percentage = ::profiler_gui::percent(self_duration, duration);
  594. }
  595. item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
  596. item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
  597. item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
  598. if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
  599. {
  600. total_items += children_items_number + 1;
  601. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  602. gui_block.tree_item = item_index;
  603. #endif
  604. if (_colorizeRows)
  605. item->colorize(_colorizeRows);
  606. if (gui_block.expanded)
  607. item->setExpanded(true);
  608. #ifndef EASY_TREE_WIDGET__USE_VECTOR
  609. m_items.insert(::std::make_pair(child_index, item));
  610. #endif
  611. }
  612. else
  613. {
  614. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  615. m_items.pop_back();
  616. #endif
  617. delete item;
  618. }
  619. }
  620. return total_items;
  621. }
  622. //////////////////////////////////////////////////////////////////////////
  623. ::profiler::timestamp_t EasyTreeWidgetLoader::calculateChildrenDurationRecursive(const ::profiler::BlocksTree::children_t& _children, ::profiler::block_id_t _id)
  624. {
  625. ::profiler::timestamp_t total_duration = 0;
  626. for (auto child_index : _children)
  627. {
  628. if (interrupted())
  629. break;
  630. const auto& gui_block = easyBlock(child_index);
  631. total_duration += gui_block.tree.node->duration();
  632. if (gui_block.tree.node->id() == _id)
  633. total_duration += calculateChildrenDurationRecursive(gui_block.tree.children, _id);
  634. }
  635. return total_duration;
  636. }
  637. size_t EasyTreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, EasyTreeWidgetItem*, EasyTreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _colorizeRows, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units)
  638. {
  639. size_t total_items = 0;
  640. for (auto child_index : _children)
  641. {
  642. if (interrupted())
  643. break;
  644. const auto& gui_block = easyBlock(child_index);
  645. const auto& child = gui_block.tree;
  646. const auto startTime = child.node->begin();
  647. const auto endTime = child.node->end();
  648. const auto duration = endTime - startTime;
  649. _duration += duration;
  650. auto it = m_iditems.find(child.node->id());
  651. if (it != m_iditems.end())
  652. {
  653. ++total_items;
  654. ::profiler::timestamp_t children_duration = 0;
  655. if (!child.children.empty())
  656. {
  657. setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
  658. if (interrupted())
  659. break;
  660. }
  661. if (it->second != nullptr && child.per_frame_stats != nullptr)
  662. {
  663. auto item = it->second;
  664. //auto children_duration = calculateChildrenDurationRecursive(child.children, it->first);
  665. if (children_duration != 0)
  666. {
  667. auto self_duration = item->data(COL_SELF_DURATION, Qt::UserRole).toULongLong() - children_duration;
  668. int percentage = 100;
  669. if (child.per_frame_stats->total_duration > 0)
  670. percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration);
  671. item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
  672. item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
  673. item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
  674. }
  675. bool hasContextSwitch = false;
  676. ::profiler::timestamp_t idleTime = 0;
  677. for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
  678. {
  679. auto cs_index = _threadRoot.sync[ind];
  680. const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
  681. if (cs->begin() > endTime)
  682. {
  683. if (!hasContextSwitch)
  684. _firstCswitch = ind;
  685. break;
  686. }
  687. if (startTime <= cs->begin() && cs->end() <= endTime)
  688. {
  689. if (!hasContextSwitch)
  690. {
  691. _firstCswitch = ind;
  692. hasContextSwitch = true;
  693. }
  694. idleTime += cs->duration();
  695. }
  696. }
  697. auto active_time = item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong() - idleTime;
  698. auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration);
  699. item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
  700. item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
  701. item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
  702. }
  703. continue;
  704. }
  705. if (startTime > _right || endTime < _left)
  706. continue;
  707. bool hasContextSwitch = false;
  708. ::profiler::timestamp_t idleTime = 0;
  709. for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
  710. {
  711. auto cs_index = _threadRoot.sync[ind];
  712. const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
  713. if (cs->begin() > endTime)
  714. {
  715. if (!hasContextSwitch)
  716. _firstCswitch = ind;
  717. break;
  718. }
  719. if (startTime <= cs->begin() && cs->end() <= endTime)
  720. {
  721. if (!hasContextSwitch)
  722. {
  723. _firstCswitch = ind;
  724. hasContextSwitch = true;
  725. }
  726. idleTime += cs->duration();
  727. }
  728. }
  729. auto item = new EasyTreeWidgetItem(child_index, _frame);
  730. auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name();
  731. item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
  732. if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
  733. {
  734. const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats;
  735. if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
  736. {
  737. item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration());
  738. item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration());
  739. item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
  740. }
  741. item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
  742. item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
  743. item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
  744. auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time);
  745. item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
  746. item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
  747. const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats;
  748. const auto percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, _frame->duration());
  749. item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage_sum);
  750. item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage_sum));
  751. if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
  752. {
  753. item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration());
  754. item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration());
  755. item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
  756. }
  757. item->setTimeSmart(COL_DURATION, _units, per_frame_stats->total_duration);
  758. item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
  759. item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
  760. }
  761. else
  762. {
  763. item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
  764. item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
  765. }
  766. const auto color = easyDescriptor(child.node->id()).color();
  767. const auto fgColor = ::profiler_gui::textColorForRgb(color);// 0x00ffffff - bgColor;
  768. item->setBackgroundColor(color);
  769. item->setTextColor(fgColor);
  770. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  771. auto item_index = static_cast<uint32_t>(m_items.size());
  772. m_items.push_back(item);
  773. #endif
  774. m_iditems[child.node->id()] = nullptr;
  775. size_t children_items_number = 0;
  776. ::profiler::timestamp_t children_duration = 0;
  777. if (!child.children.empty())
  778. {
  779. children_items_number = setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
  780. if (interrupted())
  781. break;
  782. }
  783. m_iditems[child.node->id()] = item;
  784. if (child.per_frame_stats != nullptr)
  785. {
  786. int percentage = 100;
  787. auto self_duration = child.per_frame_stats->total_duration - children_duration;
  788. if (child.per_frame_stats->total_duration > 0)
  789. percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration);
  790. item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
  791. item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
  792. item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
  793. auto active_time = child.per_frame_stats->total_duration - idleTime;
  794. auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration);
  795. item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
  796. item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
  797. item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
  798. }
  799. if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
  800. {
  801. total_items += children_items_number + 1;
  802. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  803. gui_block.tree_item = item_index;
  804. #endif
  805. if (_colorizeRows)
  806. item->colorize(_colorizeRows);
  807. if (gui_block.expanded)
  808. item->setExpanded(true);
  809. #ifndef EASY_TREE_WIDGET__USE_VECTOR
  810. m_items.insert(::std::make_pair(child_index, item));
  811. #endif
  812. }
  813. else
  814. {
  815. #ifdef EASY_TREE_WIDGET__USE_VECTOR
  816. m_items.pop_back();
  817. #endif
  818. delete item;
  819. m_iditems.erase(gui_block.tree.node->id());
  820. }
  821. }
  822. return total_items;
  823. }
  824. //////////////////////////////////////////////////////////////////////////