diff --git a/README.md b/README.md index 5144dae..d32a5f9 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ You can see the results of measuring in simple GUI application which provides fu # Usage -First of all you can specify path to include directory which contains `include/profiler` directory. +First of all you can specify path to include directory which contains `include/profiler` directory and define macro `BUILD_WITH_EASY_PROFILER`. For linking with easy_profiler you can specify path to library. Example of usage. diff --git a/include/easy/profiler.h b/include/easy/profiler.h index 613ccd5..bd94f72 100644 --- a/include/easy/profiler.h +++ b/include/easy/profiler.h @@ -33,7 +33,7 @@ namespace profiler { const uint32_t EASY_FULL_VERSION = ((uint32_t)EASY_VERSION_MAJOR << 24) | ((uint32_t)EASY_VERSION_MINOR << 16) | (uint32_t)EASY_VERSION_REV; } -#ifndef FULL_DISABLE_PROFILER +#ifdef BUILD_WITH_EASY_PROFILER /** \defgroup profiler EasyProfiler @@ -212,7 +212,6 @@ breakdown, but if you care about that then you change set event tracing priority */ # define EASY_SET_LOW_PRIORITY_EVENT_TRACING(isLowPriority) ::profiler::setLowPriorityEventTracing(isLowPriority); -# ifndef _WIN32 /** Macro for setting temporary log-file path for Unix event tracing system. \note Default value is "/tmp/cs_profiling_info.log". @@ -226,9 +225,6 @@ breakdown, but if you care about that then you change set event tracing priority \ingroup profiler */ # define EASY_EVENT_TRACING_LOG ::profiler::getContextSwitchLogFilename(); -# endif - - // EasyProfiler settings: @@ -279,7 +275,7 @@ Otherwise, no log messages will be printed. # define EASY_LOG_ENABLED 1 -#else // #ifndef FULL_DISABLE_PROFILER +#else // #ifdef BUILD_WITH_EASY_PROFILER # define EASY_BLOCK(...) # define EASY_FUNCTION(...) @@ -303,7 +299,7 @@ Otherwise, no log messages will be printed. # define EASY_LOW_PRIORITY_EVENT_TRACING true # define EASY_LOG_ENABLED 0 -#endif // #ifndef FULL_DISABLE_PROFILER +#endif // #ifndef BUILD_WITH_EASY_PROFILER ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -515,7 +511,6 @@ namespace profiler { */ PROFILER_API void setLowPriorityEventTracing(bool _isLowPriority); -#ifndef _WIN32 /** Set temporary log-file path for Unix event tracing system. \note Default value is "/tmp/cs_profiling_info.log". @@ -529,7 +524,6 @@ namespace profiler { \ingroup profiler */ PROFILER_API const char* getContextSwitchLogFilename(); -#endif PROFILER_API void startListenSignalToCapture(); PROFILER_API void stopListenSignalToCapture(); diff --git a/include/easy/profiler_aux.h b/include/easy/profiler_aux.h index bbdafe2..a59f8ff 100644 --- a/include/easy/profiler_aux.h +++ b/include/easy/profiler_aux.h @@ -48,18 +48,16 @@ namespace profiler { FORCE_ON_WITHOUT_CHILDREN = FORCE_ON | OFF_RECURSIVE, ///< The block is ALWAYS ON but all of it's children are OFF. }; - struct passthrough_hash EASY_FINAL { - template inline size_t operator () (T _value) const { - return static_cast(_value); - } + struct passthrough_hash EASY_FINAL { + template inline size_t operator () (T _value) const { + return static_cast(_value); + } }; } ////////////////////////////////////////////////////////////////////////// -#ifndef FULL_DISABLE_PROFILER - #include #include @@ -70,6 +68,8 @@ namespace profiler { # define EASY_UNIQUE_BLOCK(x) EASY_TOKEN_CONCATENATE(unique_profiler_mark_name_, x) # define EASY_UNIQUE_DESC(x) EASY_TOKEN_CONCATENATE(unique_profiler_descriptor_, x) +#ifdef BUILD_WITH_EASY_PROFILER + namespace profiler { template struct NameSwitch EASY_FINAL { @@ -147,7 +147,7 @@ namespace profiler { # define EASY_COMPILETIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::compiletime_name(name, EASY_UNIQUE_LINE_ID) # define EASY_RUNTIME_NAME(name) ::profiler::NameSwitch<::std::is_reference::value>::runtime_name(name) -#endif // FULL_DISABLE_PROFILER +#endif // BUILD_WITH_EASY_PROFILER ////////////////////////////////////////////////////////////////////////// diff --git a/include/easy/reader.h b/include/easy/reader.h index d943a9f..a1a0e95 100644 --- a/include/easy/reader.h +++ b/include/easy/reader.h @@ -260,8 +260,8 @@ namespace profiler { clear(); } - void set(size_t _size); - void extend(size_t _size); + void set(uint64_t _size); + void extend(uint64_t _size); SerializedData& operator = (SerializedData&& that) { @@ -276,7 +276,17 @@ namespace profiler { return m_data + i; } - size_t size() const + const char* operator [] (uint64_t i) const + { + return m_data + i; + } + + bool empty() const + { + return m_size == 0; + } + + uint64_t size() const { return m_size; } @@ -299,7 +309,7 @@ namespace profiler { void swap(SerializedData& other) { char* d = other.m_data; - size_t sz = other.m_size; + uint64_t sz = other.m_size; other.m_data = m_data; other.m_size = m_size; @@ -310,7 +320,7 @@ namespace profiler { private: - void set(char* _data, size_t _size); + void set(char* _data, uint64_t _size); SerializedData(const SerializedData&) = delete; SerializedData& operator = (const SerializedData&) = delete; @@ -319,6 +329,51 @@ namespace profiler { ////////////////////////////////////////////////////////////////////////// + struct FileData + { + ::profiler::SerializedData serialized_blocks; + ::profiler::SerializedData serialized_descriptors; + ::std::vector<::profiler::thread_id_t> threads_order; + ::profiler::timestamp_t begin_time = 0ULL; + ::profiler::timestamp_t end_time = 0ULL; + int64_t cpu_frequency = 0LL; + uint32_t total_blocks_number = 0; + uint32_t total_descriptors_number = 0; + + FileData() = default; + FileData(FileData&& _other) + : serialized_blocks(::std::move(_other.serialized_blocks)) + , serialized_descriptors(::std::move(_other.serialized_descriptors)) + , threads_order(::std::move(_other.threads_order)) + , begin_time(_other.begin_time) + , end_time(_other.end_time) + , cpu_frequency(_other.cpu_frequency) + , total_blocks_number(_other.total_blocks_number) + , total_descriptors_number(_other.total_descriptors_number) + { + } + + FileData& operator = (FileData&& _other) + { + serialized_blocks = ::std::move(_other.serialized_blocks); + serialized_descriptors = ::std::move(_other.serialized_descriptors); + threads_order = ::std::move(_other.threads_order); + begin_time = _other.begin_time; + end_time = _other.end_time; + cpu_frequency = _other.cpu_frequency; + total_blocks_number = _other.total_blocks_number; + total_descriptors_number = _other.total_descriptors_number; + return *this; + } + + private: + + FileData(const FileData&) = delete; + FileData& operator = (const FileData&) = delete; + }; + + ////////////////////////////////////////////////////////////////////////// + typedef ::std::vector descriptors_list_t; } // END of namespace profiler. @@ -326,22 +381,24 @@ namespace profiler { extern "C" { PROFILER_API ::profiler::block_index_t fillTreesFromFile(::std::atomic& progress, const char* filename, - ::profiler::SerializedData& serialized_blocks, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::profiler::blocks_t& _blocks, - ::profiler::thread_blocks_tree_t& threaded_trees, - bool gather_statistics, - ::std::stringstream& _log); + ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::profiler::blocks_t& _blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + bool gather_statistics, + ::std::stringstream& _log); PROFILER_API ::profiler::block_index_t fillTreesFromStream(::std::atomic& progress, ::std::stringstream& str, - ::profiler::SerializedData& serialized_blocks, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::profiler::blocks_t& _blocks, - ::profiler::thread_blocks_tree_t& threaded_trees, - bool gather_statistics, - ::std::stringstream& _log); + ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::profiler::blocks_t& _blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + bool gather_statistics, + ::std::stringstream& _log); PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& str, ::profiler::SerializedData& serialized_descriptors, @@ -350,13 +407,15 @@ extern "C" { } inline ::profiler::block_index_t fillTreesFromFile(const char* filename, ::profiler::SerializedData& serialized_blocks, - ::profiler::SerializedData& serialized_descriptors, ::profiler::descriptors_list_t& descriptors, - ::profiler::blocks_t& _blocks, ::profiler::thread_blocks_tree_t& threaded_trees, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, ::profiler::blocks_t& _blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, bool gather_statistics, ::std::stringstream& _log) { ::std::atomic progress = ATOMIC_VAR_INIT(0); - return fillTreesFromFile(progress, filename, serialized_blocks, serialized_descriptors, descriptors, _blocks, threaded_trees, gather_statistics, _log); + return fillTreesFromFile(progress, filename, serialized_blocks, serialized_descriptors, descriptors, _blocks, threaded_trees, total_descriptors_number, gather_statistics, _log); } inline bool readDescriptionsFromStream(::std::stringstream& str, diff --git a/profiler_gui/blocks_tree_widget.cpp b/profiler_gui/blocks_tree_widget.cpp index 0a8a34f..81b6f41 100644 --- a/profiler_gui/blocks_tree_widget.cpp +++ b/profiler_gui/blocks_tree_widget.cpp @@ -614,7 +614,7 @@ void EasyTreeWidget::onBlockStatusChangeClicked(bool _checked) void EasyTreeWidget::onItemExpand(QTreeWidgetItem* _item) { - if (!EASY_GLOBALS.bind_scene_and_tree_expand_status) + if (!EASY_GLOBALS.bind_scene_and_tree_expand_status || _item->parent() == nullptr) { resizeColumnsToContents(); return; @@ -631,7 +631,7 @@ void EasyTreeWidget::onItemExpand(QTreeWidgetItem* _item) void EasyTreeWidget::onItemCollapse(QTreeWidgetItem* _item) { - if (!EASY_GLOBALS.bind_scene_and_tree_expand_status) + if (!EASY_GLOBALS.bind_scene_and_tree_expand_status || _item->parent() == nullptr) return; static_cast(_item)->guiBlock().expanded = false; diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h index 18f1596..23784a6 100644 --- a/profiler_gui/common_types.h +++ b/profiler_gui/common_types.h @@ -150,18 +150,17 @@ inline ::profiler::color_t textColorForRgb(::profiler::color_t _color) #pragma pack(push, 1) struct EasyBlockItem Q_DECL_FINAL { - //const ::profiler::BlocksTree* block; ///< Pointer to profiler block qreal x; ///< x coordinate of the item (this is made qreal=double to avoid mistakes on very wide scene) float w; ///< Width of the item ::profiler::block_index_t block; ///< Index of profiler block uint32_t children_begin; ///< Index of first child item on the next sublevel uint16_t totalHeight; ///< Total height of the item including heights of all it's children - char state; ///< 0 = no change, 1 = paint, -1 = do not paint + int8_t state; ///< 0 = no change, 1 = paint, -1 = do not paint // Possible optimizations: // 1) We can save 1 more byte per block if we will use char instead of short + real time calculations for "totalHeight" var; // 2) We can save 12 bytes per block if "x" and "w" vars will be removed (all this information exist inside BlocksTree), - // but this will make impossible to run graphics test without loading any .prof file. + // but this requires runtime x-coodinate calculation because BlocksTree has x value in nanoseconds. inline void setPos(qreal _x, float _w) { x = _x; w = _w; } inline qreal left() const { return x; } diff --git a/profiler_gui/descriptors_tree_widget.cpp b/profiler_gui/descriptors_tree_widget.cpp index 7759f1a..feebc9f 100644 --- a/profiler_gui/descriptors_tree_widget.cpp +++ b/profiler_gui/descriptors_tree_widget.cpp @@ -696,6 +696,7 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent) , m_searchButton(nullptr) { m_searchBox->setFixedWidth(200); + m_searchBox->setContentsMargins(5, 0, 0, 0); auto tb = new QToolBar(); auto refreshButton = tb->addAction(QIcon(":/Reload"), tr("Refresh blocks list")); @@ -703,26 +704,32 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent) refreshButton->setToolTip(tr("Refresh blocks list.\nConnection needed.")); connect(refreshButton, &QAction::triggered, &EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blocksRefreshRequired); - tb->addSeparator(); - m_searchButton = tb->addAction(QIcon(":/Search-next"), tr("Find next"), this, SLOT(findNext(bool))); - tb->addWidget(m_searchBox); - m_searchButton->setData(true); - m_searchButton->setMenu(new QMenu(this)); + QMenu* menu = new QMenu(this); + m_searchButton = menu->menuAction(); + m_searchButton->setText("Find next"); + m_searchButton->setIcon(QIcon(":/Search-next")); + m_searchButton->setData(true); + connect(m_searchButton, &QAction::triggered, this, &This::findNext); + auto actionGroup = new QActionGroup(this); - actionGroup->setExclusive(true); - + actionGroup->setExclusive(true); + auto a = new QAction(tr("Find next"), actionGroup); a->setCheckable(true); a->setChecked(true); connect(a, &QAction::triggered, this, &This::findNextFromMenu); - m_searchButton->menu()->addAction(a); + menu->addAction(a); a = new QAction(tr("Find previous"), actionGroup); a->setCheckable(true); connect(a, &QAction::triggered, this, &This::findPrevFromMenu); - m_searchButton->menu()->addAction(a); + menu->addAction(a); + + tb->addSeparator(); + tb->addAction(m_searchButton); + tb->addWidget(m_searchBox); auto searchbox = new QHBoxLayout(); searchbox->setContentsMargins(0, 0, 0, 0); diff --git a/profiler_gui/easy_graphics_item.cpp b/profiler_gui/easy_graphics_item.cpp index d8111fb..ef6917c 100644 --- a/profiler_gui/easy_graphics_item.cpp +++ b/profiler_gui/easy_graphics_item.cpp @@ -38,7 +38,7 @@ ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// -enum BlockItemState +enum BlockItemState : int8_t { BLOCK_ITEM_DO_NOT_PAINT = -1, BLOCK_ITEM_UNCHANGED, @@ -47,6 +47,10 @@ enum BlockItemState ////////////////////////////////////////////////////////////////////////// +const int MIN_ITEM_WIDTH = 3; +const int MIN_ITEMS_SPACING = 3; +const int MIN_SYNC_SPACING = 1; +const int NARROW_ITEM_WIDTH = 20; const QRgb BORDERS_COLOR = ::profiler::colors::Grey700 & 0x00ffffff;// 0x00686868; inline QRgb selectedItemBorderColor(::profiler::color_t _color) { @@ -69,10 +73,22 @@ const auto SELECTED_ITEM_FONT = ::profiler_gui::EFont("Helvetica", 10, QFont::Bo EasyGraphicsItem::EasyGraphicsItem(uint8_t _index, const::profiler::BlocksTreeRoot& _root) : QGraphicsItem(nullptr) - , m_threadName(_root.got_name() ? QString("%1 Thread %2").arg(_root.name()).arg(_root.thread_id) : QString("Thread %1").arg(_root.thread_id)) , m_pRoot(&_root) , m_index(_index) { + const auto u_thread = ::profiler_gui::toUnicode("thread"); + if (_root.got_name()) + { + QString rootname(::profiler_gui::toUnicode(_root.name())); + if (rootname.contains(u_thread, Qt::CaseInsensitive)) + m_threadName = ::std::move(QString("%1 %2").arg(rootname).arg(_root.thread_id)); + else + m_threadName = ::std::move(QString("%1 Thread %2").arg(rootname).arg(_root.thread_id)); + } + else + { + m_threadName = ::std::move(QString("Thread %1").arg(_root.thread_id)); + } } EasyGraphicsItem::~EasyGraphicsItem() @@ -181,6 +197,21 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* m_levels[next_level][children_begin].state = BLOCK_ITEM_DO_NOT_PAINT; }; + auto const dont_skip_children = [this, &levelsNumber](short next_level, decltype(::profiler_gui::EasyBlockItem::children_begin) children_begin) + { + if (next_level < levelsNumber && children_begin != MAX_CHILD_INDEX) + { + if (m_levelsIndexes[next_level] == MAX_CHILD_INDEX) + { + // Mark first potentially visible child item on next sublevel + m_levelsIndexes[next_level] = children_begin; + } + + // Mark children items that we want to draw them + m_levels[next_level][children_begin].state = BLOCK_ITEM_DO_PAINT; + } + }; + bool selectedItemsWasPainted = false; for (uint8_t l = 0; l < levelsNumber; ++l) { @@ -209,21 +240,29 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* continue; } - const auto x = item.left() * currentScale - dx; + auto x = item.left() * currentScale - dx; auto w = item.width() * currentScale; if (x + w <= prevRight) { // This item is not visible - if (w < 20) + if (EASY_GLOBALS.hide_narrow_children && w < NARROW_ITEM_WIDTH) skip_children(next_level, item.children_begin); + else + dont_skip_children(next_level, item.children_begin); continue; } + if (x < prevRight) + { + w -= prevRight - x; + x = prevRight; + } + const auto& itemBlock = easyBlock(item.block); const auto& itemDesc = easyDescriptor(itemBlock.tree.node->id()); int h = 0, flags = 0; - if (w < 20 || !itemBlock.expanded) + if ((EASY_GLOBALS.hide_narrow_children && w < NARROW_ITEM_WIDTH) || !itemBlock.expanded) { // Items which width is less than 20 will be painted as big rectangles which are hiding it's children @@ -254,15 +293,16 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* _painter->setPen(BORDERS_COLOR & inverseColor);// BORDERS_COLOR); } - if (w < 2) w = 2; + if (w < MIN_ITEM_WIDTH) + w = MIN_ITEM_WIDTH; // Draw rectangle rect.setRect(x, top, w, h); _painter->drawRect(rect); - prevRight = rect.right(); + prevRight = rect.right() + MIN_ITEMS_SPACING; skip_children(next_level, item.children_begin); - if (w < 20) + if (w < NARROW_ITEM_WIDTH) continue; if (item.totalHeight > ::profiler_gui::GRAPHICS_ROW_SIZE) @@ -272,18 +312,6 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* } else { - if (next_level < levelsNumber && item.children_begin != MAX_CHILD_INDEX) - { - if (m_levelsIndexes[next_level] == MAX_CHILD_INDEX) - { - // Mark first potentially visible child item on next sublevel - m_levelsIndexes[next_level] = item.children_begin; - } - - // Mark children items that we want to draw them - m_levels[next_level][item.children_begin].state = BLOCK_ITEM_DO_PAINT; - } - if (item.block == EASY_GLOBALS.selected_block) selectedItemsWasPainted = true; @@ -312,13 +340,19 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* if (dh > 0) h -= dh; + if (w < MIN_ITEM_WIDTH) + w = MIN_ITEM_WIDTH; + rect.setRect(x, top, w, h); _painter->drawRect(rect); + prevRight = rect.right() + MIN_ITEMS_SPACING; + dont_skip_children(next_level, item.children_begin); + if (w < NARROW_ITEM_WIDTH) + continue; + if (!(item.width() < 1)) flags = Qt::AlignHCenter; - - prevRight = rect.right(); } // Draw text----------------------------------- @@ -375,7 +409,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* const auto& itemBlock = easyBlock(item.block); auto top = levelY(guiblock.graphics_item_level); auto w = ::std::max(item.width() * currentScale, 1.0); - decltype(top) h = (selectedItemsWasPainted && itemBlock.expanded && w > 20) ? ::profiler_gui::GRAPHICS_ROW_SIZE : item.totalHeight; + decltype(top) h = (!itemBlock.expanded || (w < NARROW_ITEM_WIDTH && EASY_GLOBALS.hide_narrow_children)) ? item.totalHeight : ::profiler_gui::GRAPHICS_ROW_SIZE; auto dh = top + h - visibleBottom; if (dh < h) @@ -405,7 +439,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* rect.setRect(x, top, w, h); _painter->drawRect(rect); - if (!selectedItemsWasPainted && w > 20) + if (!selectedItemsWasPainted && w > NARROW_ITEM_WIDTH) { // Draw text----------------------------------- // calculating text coordinates @@ -492,8 +526,8 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* left = prevRight; } - if (width < 2) - width = 2; + if (width < MIN_ITEM_WIDTH) + width = MIN_ITEM_WIDTH; const bool self_thread = item.node->id() != 0 && EASY_GLOBALS.profiler_blocks.find(item.node->id()) != EASY_GLOBALS.profiler_blocks.end(); ::profiler::color_t color = 0; @@ -516,7 +550,7 @@ void EasyGraphicsItem::paint(QPainter* _painter, const QStyleOptionGraphicsItem* rect.setRect(left, top, width, h); _painter->drawRect(rect); - prevRight = left + width; + prevRight = left + width + MIN_SYNC_SPACING; } } } @@ -725,7 +759,7 @@ const ::profiler_gui::EasyBlockItem* EasyGraphicsItem::intersect(const QPointF& } const auto w = item.width() * currentScale; - if (i == levelIndex || w < 20 || !easyBlock(item.block).expanded) + if (i == levelIndex || (w < NARROW_ITEM_WIDTH && EASY_GLOBALS.hide_narrow_children) || !easyBlock(item.block).expanded) { return &item; } diff --git a/profiler_gui/globals.cpp b/profiler_gui/globals.cpp index 572acbb..a660d27 100644 --- a/profiler_gui/globals.cpp +++ b/profiler_gui/globals.cpp @@ -51,6 +51,7 @@ namespace profiler_gui { , enable_event_indicators(true) , enable_statistics(true) , draw_graphics_items_borders(true) + , hide_narrow_children(true) , display_only_relevant_stats(true) , collapse_items_on_tree_close(false) , all_items_expanded_by_default(true) diff --git a/profiler_gui/globals.h b/profiler_gui/globals.h index 292fdab..744aaeb 100644 --- a/profiler_gui/globals.h +++ b/profiler_gui/globals.h @@ -101,6 +101,7 @@ namespace profiler_gui { bool enable_event_indicators; ///< Enable event indicators painting (These are narrow rectangles at the bottom of each thread) bool enable_statistics; ///< Enable gathering and using statistics (Disable if you want to consume less memory) bool draw_graphics_items_borders; ///< Draw borders for graphics blocks or not + bool hide_narrow_children; ///< Hide children for narrow graphics blocks bool display_only_relevant_stats; ///< Display only relevant information in ProfTreeWidget (excludes min, max, average times if there are only 1 calls number) bool collapse_items_on_tree_close; ///< Collapse all items which were displayed in the hierarchy tree after tree close/reset bool all_items_expanded_by_default; ///< Expand all items after file is opened diff --git a/profiler_gui/icons/attribution.txt b/profiler_gui/icons/attribution.txt index 25e902b..2acd148 100644 --- a/profiler_gui/icons/attribution.txt +++ b/profiler_gui/icons/attribution.txt @@ -1,20 +1,23 @@ -logo.svg - Icon made by Freepik from www.flaticon.com -off.svg - Icon made by Freepik from www.flaticon.com -open-folder.svg - Icon made by Freepik from www.flaticon.com -reload.svg - Icon made by Freepik from www.flaticon.com -expand.svg - Icon made by Freepik from www.flaticon.com -collapse.svg - Icon made by Freepik from www.flaticon.com -colors.svg - Icon made by Freepik from www.flaticon.com -colors-black.svg - Icon made by Freepik from www.flaticon.com -save.svg - Icon made by Freepik from www.flaticon.com -statistics.svg - Icon made by Freepik from www.flaticon.com -statistics2.svg - Icon made by Freepik from www.flaticon.com -lan.svg - Icon made by Freepik from www.flaticon.com -lan_on.svg - Icon made by Freepik from www.flaticon.com -wifi.svg - Icon made by Freepik from www.flaticon.com -wifi_on.svg - Icon made by Freepik from www.flaticon.com -play.svg - Icon made by Google from www.flaticon.com -delete.svg - Icon made by Freepik from www.flaticon.com -list.svg - Icon made by Freepik from www.flaticon.com -search-prev.svg - Icon made by Freepik from www.flaticon.com -search-next.svg - Icon made by Freepik from www.flaticon.com \ No newline at end of file +logo.svg - Icon made by Freepik from www.flaticon.com +off.svg - Icon made by Freepik from www.flaticon.com +open-folder.svg - Icon made by Freepik from www.flaticon.com +open-folder2.svg - Icon made by Freepik from www.flaticon.com +reload-folder2.svg - Icon made by Freepik from www.flaticon.com +reload.svg - Icon made by Freepik from www.flaticon.com +expand.svg - Icon made by Freepik from www.flaticon.com +collapse.svg - Icon made by Freepik from www.flaticon.com +colors.svg - Icon made by Freepik from www.flaticon.com +colors-black.svg - Icon made by Freepik from www.flaticon.com +save.svg - Icon made by Freepik from www.flaticon.com +statistics.svg - Icon made by Freepik from www.flaticon.com +statistics2.svg - Icon made by Freepik from www.flaticon.com +lan.svg - Icon made by Freepik from www.flaticon.com +lan_on.svg - Icon made by Freepik from www.flaticon.com +wifi.svg - Icon made by Freepik from www.flaticon.com +wifi_on.svg - Icon made by Freepik from www.flaticon.com +play.svg - Icon made by Google from www.flaticon.com +delete.svg - Icon made by Freepik from www.flaticon.com +list.svg - Icon made by Freepik from www.flaticon.com +search-prev.svg - Icon made by Freepik from www.flaticon.com +search-next.svg - Icon made by Freepik from www.flaticon.com +settings.svg - Icon made by Freepik from www.flaticon.com \ No newline at end of file diff --git a/profiler_gui/icons/open-folder2.svg b/profiler_gui/icons/open-folder2.svg new file mode 100644 index 0000000..b6454a9 --- /dev/null +++ b/profiler_gui/icons/open-folder2.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/reload-folder2.svg b/profiler_gui/icons/reload-folder2.svg new file mode 100644 index 0000000..d3f047c --- /dev/null +++ b/profiler_gui/icons/reload-folder2.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/save.svg b/profiler_gui/icons/save.svg index c3b92ad..85a9cb5 100644 --- a/profiler_gui/icons/save.svg +++ b/profiler_gui/icons/save.svg @@ -1,43 +1,74 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + +image/svg+xml \ No newline at end of file diff --git a/profiler_gui/icons/settings.svg b/profiler_gui/icons/settings.svg new file mode 100644 index 0000000..07714c0 --- /dev/null +++ b/profiler_gui/icons/settings.svg @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index 311583c..eac659d 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -53,11 +53,14 @@ #include #include #include +#include +#include #include #include #include #include #include +#include #include "main_window.h" #include "blocks_tree_widget.h" @@ -77,10 +80,11 @@ ////////////////////////////////////////////////////////////////////////// const int LOADER_TIMER_INTERVAL = 40; +const auto NETWORK_CACHE_FILE = "easy_profiler_stream.cache"; ////////////////////////////////////////////////////////////////////////// -EasyMainWindow::EasyMainWindow() : Parent() +EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("127.0.0.1"), m_lastPort(::profiler::DEFAULT_PORT) { { QIcon icon(":/logo"); if (!icon.isNull()) QApplication::setWindowIcon(icon); } @@ -118,9 +122,27 @@ EasyMainWindow::EasyMainWindow() : Parent() addDockWidget(Qt::BottomDockWidgetArea, m_descTreeWidget); #endif - auto toolbar = addToolBar("MainToolBar"); - toolbar->setObjectName("ProfilerGUI_MainToolBar"); - toolbar->addAction(QIcon(":/Delete"), tr("Clear all"), this, SLOT(onDeleteClicked(bool))); + + loadSettings(); + + + auto toolbar = addToolBar("FileToolbar"); + toolbar->setObjectName("ProfilerGUI_FileToolbar"); + toolbar->setContentsMargins(1, 0, 1, 0); + + toolbar->addAction(QIcon(":/Open"), tr("Open"), this, SLOT(onOpenFileClicked(bool))); + toolbar->addAction(QIcon(":/Reopen"), tr("Reload last file"), this, SLOT(onReloadFileClicked(bool))); + m_saveAction = toolbar->addAction(QIcon(":/Save"), tr("Save"), this, SLOT(onSaveFileClicked(bool))); + m_deleteAction = toolbar->addAction(QIcon(":/Delete"), tr("Clear all"), this, SLOT(onDeleteClicked(bool))); + + m_saveAction->setEnabled(false); + m_deleteAction->setEnabled(false); + + + + toolbar = addToolBar("ProfileToolbar"); + toolbar->setObjectName("ProfilerGUI_ProfileToolbar"); + toolbar->setContentsMargins(1, 0, 1, 0); toolbar->addAction(QIcon(":/List"), tr("Blocks"), this, SLOT(onEditBlocksClicked(bool))); m_captureAction = toolbar->addAction(QIcon(":/Start"), tr("Capture"), this, SLOT(onCaptureClicked(bool))); @@ -129,18 +151,22 @@ EasyMainWindow::EasyMainWindow() : Parent() toolbar->addSeparator(); m_connectAction = toolbar->addAction(QIcon(":/Connection"), tr("Connect"), this, SLOT(onConnectClicked(bool))); - toolbar->addWidget(new QLabel(" IP:")); + auto lbl = new QLabel("IP:", toolbar); + lbl->setContentsMargins(5, 0, 1, 0); + toolbar->addWidget(lbl); m_ipEdit = new QLineEdit(); QRegExp rx("^0*(2(5[0-5]|[0-4]\\d)|1?\\d{1,2})(\\.0*(2(5[0-5]|[0-4]\\d)|1?\\d{1,2})){3}$"); m_ipEdit->setValidator(new QRegExpValidator(rx, m_ipEdit)); - m_ipEdit->setText("127.0.0.1"); + m_ipEdit->setText(m_lastAddress); m_ipEdit->setFixedWidth(m_ipEdit->fontMetrics().width(QString("255.255.255.255")) + 20); toolbar->addWidget(m_ipEdit); - toolbar->addWidget(new QLabel(" Port:")); + lbl = new QLabel("Port:", toolbar); + lbl->setContentsMargins(5, 0, 1, 0); + toolbar->addWidget(lbl); m_portEdit = new QLineEdit(); m_portEdit->setValidator(new QIntValidator(1, 65535, m_portEdit)); - m_portEdit->setText(QString::number(::profiler::DEFAULT_PORT)); + m_portEdit->setText(QString::number(m_lastPort)); m_portEdit->setFixedWidth(m_portEdit->fontMetrics().width(QString("000000")) + 10); toolbar->addWidget(m_portEdit); @@ -148,86 +174,23 @@ EasyMainWindow::EasyMainWindow() : Parent() connect(m_portEdit, &QLineEdit::returnPressed, [this](){ onConnectClicked(true); }); - loadSettings(); - if (!m_lastAddress.isEmpty()) - m_ipEdit->setText(m_lastAddress); - if (m_lastPort != 0) - m_portEdit->setText(QString::number(m_lastPort)); + toolbar = addToolBar("SetupToolbar"); + toolbar->setObjectName("ProfilerGUI_SetupToolbar"); + toolbar->setContentsMargins(1, 0, 1, 0); - auto menu = menuBar()->addMenu("&File"); - menu->addAction(QIcon(":/Open"), "&Open", this, SLOT(onOpenFileClicked(bool))); - menu->addAction(QIcon(":/Reload"), "&Reload", this, SLOT(onReloadFileClicked(bool))); - menu->addSeparator(); - menu->addAction(QIcon(":/Exit"), "&Exit", this, SLOT(onExitClicked(bool))); + toolbar->addAction(QIcon(":/Expand"), "Expand all", this, SLOT(onExpandAllClicked(bool))); + toolbar->addAction(QIcon(":/Collapse"), "Collapse all", this, SLOT(onCollapseAllClicked(bool))); + toolbar->addSeparator(); + auto menu = new QMenu("Settings", this); + QToolButton* toolButton = new QToolButton(toolbar); + toolButton->setIcon(QIcon(":/Settings")); + toolButton->setMenu(menu); + toolButton->setPopupMode(QToolButton::InstantPopup); + toolbar->addWidget(toolButton); - - menu = menuBar()->addMenu("&View"); - - menu->addAction(QIcon(":/Expand"), "Expand all", this, SLOT(onExpandAllClicked(bool))); - menu->addAction(QIcon(":/Collapse"), "Collapse all", this,SLOT(onCollapseAllClicked(bool))); - - menu->addSeparator(); - - auto action = menu->addAction("Draw items' borders"); - action->setCheckable(true); - action->setChecked(EASY_GLOBALS.draw_graphics_items_borders); - connect(action, &QAction::triggered, this, &This::onDrawBordersChanged); - - action = menu->addAction("Collapse items on tree reset"); - action->setCheckable(true); - action->setChecked(EASY_GLOBALS.collapse_items_on_tree_close); - connect(action, &QAction::triggered, this, &This::onCollapseItemsAfterCloseChanged); - - action = menu->addAction("Expand all on file open"); - action->setCheckable(true); - action->setChecked(EASY_GLOBALS.all_items_expanded_by_default); - connect(action, &QAction::triggered, this, &This::onAllItemsExpandedByDefaultChange); - - action = menu->addAction("Bind scene and tree expand"); - action->setCheckable(true); - action->setChecked(EASY_GLOBALS.bind_scene_and_tree_expand_status); - connect(action, &QAction::triggered, this, &This::onBindExpandStatusChange); - - action = menu->addAction("Draw event indicators"); - action->setCheckable(true); - action->setChecked(EASY_GLOBALS.enable_event_indicators); - connect(action, &QAction::triggered, this, &This::onEventIndicatorsChange); - - menu->addSeparator(); - auto submenu = menu->addMenu("Chronometer text"); - auto actionGroup = new QActionGroup(this); - actionGroup->setExclusive(true); - - action = new QAction("At top", actionGroup); - action->setCheckable(true); - action->setData(static_cast(::profiler_gui::ChronoTextPosition_Top)); - if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Top) - action->setChecked(true); - submenu->addAction(action); - connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); - - action = new QAction("At center", actionGroup); - action->setCheckable(true); - action->setData(static_cast(::profiler_gui::ChronoTextPosition_Center)); - if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Center) - action->setChecked(true); - submenu->addAction(action); - connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); - - action = new QAction("At bottom", actionGroup); - action->setCheckable(true); - action->setData(static_cast(::profiler_gui::ChronoTextPosition_Bottom)); - if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Bottom) - action->setChecked(true); - submenu->addAction(action); - connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); - - - - menu = menuBar()->addMenu("&Settings"); - action = menu->addAction("Statistics enabled"); + auto action = menu->addAction("Statistics enabled"); action->setCheckable(true); action->setChecked(EASY_GLOBALS.enable_statistics); connect(action, &QAction::triggered, this, &This::onEnableDisableStatistics); @@ -244,8 +207,69 @@ EasyMainWindow::EasyMainWindow() : Parent() SET_ICON(action, ":/Stats-off"); } + menu->addSeparator(); - submenu = menu->addMenu("&Remote"); + auto submenu = menu->addMenu("View"); + action = submenu->addAction("Draw items' borders"); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.draw_graphics_items_borders); + connect(action, &QAction::triggered, this, &This::onDrawBordersChanged); + + action = submenu->addAction("Hide narrow children"); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.hide_narrow_children); + connect(action, &QAction::triggered, this, &This::onHideNarrowChildrenChanged); + + action = submenu->addAction("Collapse items on tree reset"); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.collapse_items_on_tree_close); + connect(action, &QAction::triggered, this, &This::onCollapseItemsAfterCloseChanged); + + action = submenu->addAction("Expand all on file open"); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.all_items_expanded_by_default); + connect(action, &QAction::triggered, this, &This::onAllItemsExpandedByDefaultChange); + + action = submenu->addAction("Bind scene and tree expand"); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.bind_scene_and_tree_expand_status); + connect(action, &QAction::triggered, this, &This::onBindExpandStatusChange); + + action = submenu->addAction("Draw event indicators"); + action->setCheckable(true); + action->setChecked(EASY_GLOBALS.enable_event_indicators); + connect(action, &QAction::triggered, this, &This::onEventIndicatorsChange); + + submenu->addSeparator(); + auto actionGroup = new QActionGroup(this); + actionGroup->setExclusive(true); + + action = new QAction("Chrono text at top", actionGroup); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::ChronoTextPosition_Top)); + if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Top) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); + + action = new QAction("Chrono text at center", actionGroup); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::ChronoTextPosition_Center)); + if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Center) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); + + action = new QAction("Chrono text at bottom", actionGroup); + action->setCheckable(true); + action->setData(static_cast(::profiler_gui::ChronoTextPosition_Bottom)); + if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Bottom) + action->setChecked(true); + submenu->addAction(action); + connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged); + + + submenu = menu->addMenu("Remote"); m_eventTracingEnableAction = submenu->addAction("Event tracing enabled"); m_eventTracingEnableAction->setCheckable(true); m_eventTracingEnableAction->setEnabled(false); @@ -257,13 +281,13 @@ EasyMainWindow::EasyMainWindow() : Parent() m_eventTracingPriorityAction->setEnabled(false); connect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange); - menu->addSeparator(); - submenu = menu->addMenu("&Encoding"); + + submenu = menu->addMenu("Encoding"); actionGroup = new QActionGroup(this); actionGroup->setExclusive(true); auto default_codec_mib = QTextCodec::codecForLocale()->mibEnum(); - foreach (int mib, QTextCodec::availableMibs()) + foreach(int mib, QTextCodec::availableMibs()) { auto codec = QTextCodec::codecForMib(mib)->name(); @@ -352,7 +376,77 @@ void EasyMainWindow::onReloadFileClicked(bool) ////////////////////////////////////////////////////////////////////////// -void EasyMainWindow::onDeleteClicked(bool) +void EasyMainWindow::onSaveFileClicked(bool) +{ + if (m_serializedBlocks.empty()) + return; + + const auto i = m_lastFile.lastIndexOf(QChar('/')); + const auto j = m_lastFile.lastIndexOf(QChar('\\')); + auto k = ::std::max(i, j); + + QString dir; + if (k > 0) + dir = m_lastFile.mid(0, ++k); + + auto filename = QFileDialog::getSaveFileName(this, "Save profiler log", dir, "Profiler Log File (*.prof);;All Files (*.*)"); + if (!filename.isEmpty()) + { + bool inOk = false, outOk = false; + int8_t retry1 = -1; + while (++retry1 < 4) + { + ::std::ifstream inFile(m_bNetworkFileRegime ? NETWORK_CACHE_FILE : m_lastFile.toStdString().c_str(), ::std::fstream::binary); + if (!inFile.is_open()) + { + ::std::this_thread::sleep_for(::std::chrono::milliseconds(500)); + continue; + } + + inOk = true; + + int8_t retry2 = -1; + while (++retry2 < 4) + { + ::std::ofstream outFile(filename.toStdString(), ::std::fstream::binary); + if (!outFile.is_open()) + { + ::std::this_thread::sleep_for(::std::chrono::milliseconds(500)); + continue; + } + + outFile << inFile.rdbuf(); + outOk = true; + break; + } + + break; + } + + if (outOk) + { + if (m_bNetworkFileRegime) + QFile::remove(QString(NETWORK_CACHE_FILE)); + m_lastFile = filename; + m_bNetworkFileRegime = false; + } + else if (inOk) + { + QMessageBox::warning(this, "Warning", "Can not open destination file.\nSaving incomplete.", QMessageBox::Close); + } + else + { + if (m_bNetworkFileRegime) + QMessageBox::warning(this, "Warning", "Can not open network cache file.\nSaving incomplete.", QMessageBox::Close); + else + QMessageBox::warning(this, "Warning", "Can not open source file.\nSaving incomplete.", QMessageBox::Close); + } + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::clear() { static_cast(m_treeWidget->widget())->clearSilent(true); static_cast(m_graphicsView->widget())->clear(); @@ -371,6 +465,18 @@ void EasyMainWindow::onDeleteClicked(bool) m_serializedBlocks.clear(); m_serializedDescriptors.clear(); + + m_saveAction->setEnabled(false); + m_deleteAction->setEnabled(false); + + m_bNetworkFileRegime = false; +} + +void EasyMainWindow::onDeleteClicked(bool) +{ + auto button = QMessageBox::question(this, "Clear all profiled data", "All profiled data is going to be deleted!\nContinue?", QMessageBox::Yes, QMessageBox::No); + if (button == QMessageBox::Yes) + clear(); } ////////////////////////////////////////////////////////////////////////// @@ -433,6 +539,12 @@ void EasyMainWindow::onDrawBordersChanged(bool _checked) static_cast(m_graphicsView->widget())->view()->scene()->update(); } +void EasyMainWindow::onHideNarrowChildrenChanged(bool _checked) +{ + EASY_GLOBALS.hide_narrow_children = _checked; + static_cast(m_graphicsView->widget())->view()->scene()->update(); +} + void EasyMainWindow::onCollapseItemsAfterCloseChanged(bool _checked) { EASY_GLOBALS.collapse_items_on_tree_close = _checked; @@ -551,6 +663,10 @@ void EasyMainWindow::loadSettings() if (!flag.isNull()) EASY_GLOBALS.draw_graphics_items_borders = flag.toBool(); + flag = settings.value("hide_narrow_children"); + if (!flag.isNull()) + EASY_GLOBALS.hide_narrow_children = flag.toBool(); + flag = settings.value("collapse_items_on_tree_close"); if (!flag.isNull()) EASY_GLOBALS.collapse_items_on_tree_close = flag.toBool(); @@ -607,6 +723,7 @@ void EasyMainWindow::saveSettingsAndGeometry() settings.setValue("port", (quint32)m_lastPort); settings.setValue("chrono_text_position", static_cast(EASY_GLOBALS.chrono_text_position)); settings.setValue("draw_graphics_items_borders", EASY_GLOBALS.draw_graphics_items_borders); + settings.setValue("hide_narrow_children", EASY_GLOBALS.hide_narrow_children); settings.setValue("collapse_items_on_tree_close", EASY_GLOBALS.collapse_items_on_tree_close); settings.setValue("all_items_expanded_by_default", EASY_GLOBALS.all_items_expanded_by_default); settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status); @@ -687,12 +804,14 @@ void EasyMainWindow::onFileReaderTimeout() { static_cast(m_treeWidget->widget())->clearSilent(true); - ::profiler::SerializedData serialized_blocks, serialized_descriptors; + ::profiler::SerializedData serialized_blocks; + ::profiler::SerializedData serialized_descriptors; ::profiler::descriptors_list_t descriptors; ::profiler::blocks_t blocks; ::profiler::thread_blocks_tree_t threads_map; QString filename; - m_reader.get(serialized_blocks, serialized_descriptors, descriptors, blocks, threads_map, filename); + uint32_t descriptorsNumberInFile = 0; + m_reader.get(serialized_blocks, serialized_descriptors, descriptors, blocks, threads_map, descriptorsNumberInFile, filename); if (threads_map.size() > 0xff) { @@ -703,10 +822,12 @@ void EasyMainWindow::onFileReaderTimeout() qWarning() << "Warning: Currently, maximum number of displayed threads is 255! Some threads will not be displayed."; } - if (m_reader.isFile()) + m_bNetworkFileRegime = !m_reader.isFile(); + if (!m_bNetworkFileRegime) m_lastFile = ::std::move(filename); m_serializedBlocks = ::std::move(serialized_blocks); m_serializedDescriptors = ::std::move(serialized_descriptors); + m_descriptorsNumberInFile = descriptorsNumberInFile; EASY_GLOBALS.selected_thread = 0; ::profiler_gui::set_max(EASY_GLOBALS.selected_block); EASY_GLOBALS.profiler_blocks.swap(threads_map); @@ -728,6 +849,9 @@ void EasyMainWindow::onFileReaderTimeout() #endif if (m_dialogDescTree != nullptr) m_dialogDescTree->build(); + + m_saveAction->setEnabled(true); + m_deleteAction->setEnabled(true); } else { @@ -803,7 +927,8 @@ void EasyFileReader::load(const QString& _filename) m_isFile = true; m_filename = _filename; m_thread = ::std::move(::std::thread([this](bool _enableStatistics) { - m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors, m_descriptors, m_blocks, m_blocksTree, _enableStatistics, m_errorMessage), ::std::memory_order_release); + m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors, + m_descriptors, m_blocks, m_blocksTree, m_descriptorsNumberInFile, _enableStatistics, m_errorMessage), ::std::memory_order_release); m_progress.store(100, ::std::memory_order_release); m_bDone.store(true, ::std::memory_order_release); }, EASY_GLOBALS.enable_statistics)); @@ -817,7 +942,13 @@ void EasyFileReader::load(::std::stringstream& _stream) m_filename.clear(); m_stream.swap(_stream); m_thread = ::std::move(::std::thread([this](bool _enableStatistics) { - m_size.store(fillTreesFromStream(m_progress, m_stream, m_serializedBlocks, m_serializedDescriptors, m_descriptors, m_blocks, m_blocksTree, _enableStatistics, m_errorMessage), ::std::memory_order_release); + ::std::ofstream cache_file(NETWORK_CACHE_FILE, ::std::fstream::binary); + if (cache_file.is_open()) { + cache_file << m_stream.str(); + cache_file.close(); + } + m_size.store(fillTreesFromStream(m_progress, m_stream, m_serializedBlocks, m_serializedDescriptors, m_descriptors, + m_blocks, m_blocksTree, m_descriptorsNumberInFile, _enableStatistics, m_errorMessage), ::std::memory_order_release); m_progress.store(100, ::std::memory_order_release); m_bDone.store(true, ::std::memory_order_release); }, EASY_GLOBALS.enable_statistics)); @@ -837,14 +968,15 @@ void EasyFileReader::interrupt() m_descriptors.clear(); m_blocks.clear(); m_blocksTree.clear(); + m_descriptorsNumberInFile = 0; { decltype(m_stream) dummy; dummy.swap(m_stream); } { decltype(m_errorMessage) dummy; dummy.swap(m_errorMessage); } } void EasyFileReader::get(::profiler::SerializedData& _serializedBlocks, ::profiler::SerializedData& _serializedDescriptors, - ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks, ::profiler::thread_blocks_tree_t& _tree, - QString& _filename) + ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks, + ::profiler::thread_blocks_tree_t& _tree, uint32_t& _descriptorsNumberInFile, QString& _filename) { if (done()) { @@ -854,6 +986,7 @@ void EasyFileReader::get(::profiler::SerializedData& _serializedBlocks, ::profil m_blocks.swap(_blocks); m_blocksTree.swap(_tree); m_filename.swap(_filename); + _descriptorsNumberInFile = m_descriptorsNumberInFile; } } @@ -992,28 +1125,99 @@ void EasyMainWindow::onGetBlockDescriptionsClicked(bool) if (m_listener.size() != 0) { // Read descriptions from stream + decltype(EASY_GLOBALS.descriptors) descriptors; decltype(m_serializedDescriptors) serializedDescriptors; ::std::stringstream errorMessage; if (readDescriptionsFromStream(m_listener.data(), serializedDescriptors, descriptors, errorMessage)) { - if (EASY_GLOBALS.descriptors.size() > descriptors.size()) - onDeleteClicked(true); // Clear all contents because new descriptors list conflicts with old one + // Merge old and new descriptions - EASY_GLOBALS.descriptors.swap(descriptors); - m_serializedDescriptors.swap(serializedDescriptors); - - if (m_descTreeDialog != nullptr) + bool cancel = false; + const bool doFlush = m_descriptorsNumberInFile > descriptors.size(); + if (doFlush && !m_serializedBlocks.empty()) { -#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 - static_cast(m_descTreeWidget->widget())->build(); -#endif - m_dialogDescTree->build(); - m_descTreeDialog->raise(); + auto button = QMessageBox::question(this, "Information", + QString("New blocks description number = %1\nis less than the old one = %2.\nTo avoid possible conflicts\nall profiled data will be deleted.\nContinue?") + .arg(descriptors.size()) + .arg(m_descriptorsNumberInFile), + QMessageBox::Yes, QMessageBox::No); + + if (button == QMessageBox::Yes) + clear(); // Clear all contents because new descriptors list conflicts with old one + else + cancel = true; } - else + + if (!cancel) { - onEditBlocksClicked(true); + if (!doFlush && m_descriptorsNumberInFile < EASY_GLOBALS.descriptors.size()) + { + // There are dynamically added descriptors, add them to the new list too + + auto newnumber = static_cast(descriptors.size()); + auto size = static_cast(EASY_GLOBALS.descriptors.size()); + auto diff = newnumber - size; + decltype(newnumber) failnumber = 0; + + descriptors.reserve(descriptors.size() + EASY_GLOBALS.descriptors.size() - m_descriptorsNumberInFile); + for (auto i = m_descriptorsNumberInFile; i < size; ++i) + { + auto id = EASY_GLOBALS.descriptors[i]->id(); + if (id < newnumber) + descriptors.push_back(descriptors[id]); + else + ++failnumber; + } + + if (failnumber != 0) + { + // There are some errors... + + // revert changes + descriptors.resize(newnumber); + + // clear all profiled data to avoid conflicts + auto button = QMessageBox::question(this, "Information", + "There are errors while merging block descriptions lists.\nTo avoid possible conflicts\nall profiled data will be deleted.\nContinue?", + QMessageBox::Yes, QMessageBox::No); + + if (button == QMessageBox::Yes) + clear(); // Clear all contents because new descriptors list conflicts with old one + else + cancel = true; + } + + if (!cancel && diff != 0) + { + for (auto& b : EASY_GLOBALS.gui_blocks) + { + if (b.tree.node->id() >= m_descriptorsNumberInFile) + b.tree.node->setId(b.tree.node->id() + diff); + } + + m_descriptorsNumberInFile = newnumber; + } + } + + if (!cancel) + { + EASY_GLOBALS.descriptors.swap(descriptors); + m_serializedDescriptors.swap(serializedDescriptors); + + if (m_descTreeDialog != nullptr) + { +#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 + static_cast(m_descTreeWidget->widget())->build(); +#endif + m_dialogDescTree->build(); + m_descTreeDialog->raise(); + } + else + { + onEditBlocksClicked(true); + } + } } } else diff --git a/profiler_gui/main_window.h b/profiler_gui/main_window.h index 7e5c26e..3675d27 100644 --- a/profiler_gui/main_window.h +++ b/profiler_gui/main_window.h @@ -69,6 +69,7 @@ class EasyFileReader Q_DECL_FINAL ::std::stringstream m_stream; ///< ::std::stringstream m_errorMessage; ///< QString m_filename; ///< + uint32_t m_descriptorsNumberInFile = 0; ///< ::std::thread m_thread; ///< ::std::atomic_bool m_bDone; ///< ::std::atomic m_progress; ///< @@ -91,7 +92,7 @@ public: void interrupt(); void get(::profiler::SerializedData& _serializedBlocks, ::profiler::SerializedData& _serializedDescriptors, ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks, ::profiler::thread_blocks_tree_t& _tree, - QString& _filename); + uint32_t& _descriptorsNumberInFile, QString& _filename); QString getError(); @@ -180,12 +181,17 @@ protected: class QLineEdit* m_ipEdit = nullptr; class QLineEdit* m_portEdit = nullptr; + class QAction* m_saveAction = nullptr; + class QAction* m_deleteAction = nullptr; + class QAction* m_captureAction = nullptr; class QAction* m_connectAction = nullptr; class QAction* m_eventTracingEnableAction = nullptr; class QAction* m_eventTracingPriorityAction = nullptr; + uint32_t m_descriptorsNumberInFile = 0; uint16_t m_lastPort = 0; + bool m_bNetworkFileRegime = false; public: @@ -200,6 +206,7 @@ protected slots: void onOpenFileClicked(bool); void onReloadFileClicked(bool); + void onSaveFileClicked(bool); void onDeleteClicked(bool); void onExitClicked(bool); void onEncodingChanged(bool); @@ -207,6 +214,7 @@ protected slots: void onEventIndicatorsChange(bool); void onEnableDisableStatistics(bool); void onDrawBordersChanged(bool); + void onHideNarrowChildrenChanged(bool); void onCollapseItemsAfterCloseChanged(bool); void onAllItemsExpandedByDefaultChange(bool); void onBindExpandStatusChange(bool); @@ -230,6 +238,8 @@ private: // Private non-virtual methods + void clear(); + void loadFile(const QString& filename); void readStream(::std::stringstream& data); diff --git a/profiler_gui/resources.qrc b/profiler_gui/resources.qrc index 3396613..8bc879d 100644 --- a/profiler_gui/resources.qrc +++ b/profiler_gui/resources.qrc @@ -2,7 +2,8 @@ icons/logo.svg icons/off.svg - icons/open-folder.svg + icons/open-folder2.svg + icons/reload-folder2.svg icons/reload.svg icons/expand.svg icons/collapse.svg @@ -22,5 +23,6 @@ icons/list.svg icons/search-next.svg icons/search-prev.svg + icons/settings.svg diff --git a/profiler_gui/tree_widget_loader.cpp b/profiler_gui/tree_widget_loader.cpp index f258999..39af6c4 100644 --- a/profiler_gui/tree_widget_loader.cpp +++ b/profiler_gui/tree_widget_loader.cpp @@ -168,6 +168,7 @@ void FillTreeClass::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI } //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) @@ -178,10 +179,21 @@ void FillTreeClass::setTreeInternal1(T& _safelocker, Items& _items, ThreadedI const auto& root = threadTree.second; auto item = new EasyTreeWidgetItem(); - if (root.got_name()) - item->setText(COL_NAME, QString("%1 Thread %2").arg(root.name()).arg(root.thread_id)); - else - item->setText(COL_NAME, QString("Thread %1").arg(root.thread_id)); + QString threadName; + if (root.got_name()) + { + QString rootname(::profiler_gui::toUnicode(root.name())); + if (rootname.contains(u_thread, Qt::CaseInsensitive)) + threadName = ::std::move(QString("%1 %2").arg(rootname).arg(root.thread_id)); + else + threadName = ::std::move(QString("%1 Thread %2").arg(rootname).arg(root.thread_id)); + } + else + { + threadName = ::std::move(QString("Thread %1").arg(root.thread_id)); + } + + item->setText(COL_NAME, threadName); ::profiler::timestamp_t duration = 0; if (!root.children.empty()) @@ -239,6 +251,7 @@ void FillTreeClass::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI RootsMap threadsMap; + 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) @@ -266,10 +279,21 @@ void FillTreeClass::setTreeInternal2(T& _safelocker, Items& _items, ThreadedI { thread_item = new EasyTreeWidgetItem(); - if (block.root->got_name()) - thread_item->setText(COL_NAME, QString("%1 Thread %2").arg(block.root->name()).arg(block.root->thread_id)); - else - thread_item->setText(COL_NAME, QString("Thread %1").arg(block.root->thread_id)); + QString threadName; + if (block.root->got_name()) + { + QString rootname(::profiler_gui::toUnicode(block.root->name())); + if (rootname.contains(u_thread, Qt::CaseInsensitive)) + threadName = ::std::move(QString("%1 %2").arg(rootname).arg(block.root->thread_id)); + else + threadName = ::std::move(QString("%1 Thread %2").arg(rootname).arg(block.root->thread_id)); + } + else + { + threadName = ::std::move(QString("Thread %1").arg(block.root->thread_id)); + } + + thread_item->setText(COL_NAME, threadName); if (!block.root->children.empty()) duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin(); diff --git a/reader/main.cpp b/reader/main.cpp index fa04d06..ab78a01 100644 --- a/reader/main.cpp +++ b/reader/main.cpp @@ -114,7 +114,9 @@ int main(int argc, char* argv[]) ::profiler::descriptors_list_t descriptors; ::profiler::blocks_t blocks; ::std::stringstream errorMessage; - auto blocks_counter = fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, descriptors, blocks, threaded_trees, true, errorMessage); + uint32_t descriptorsNumberInFile = 0; + auto blocks_counter = fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, descriptors, blocks, + threaded_trees, descriptorsNumberInFile, true, errorMessage); if (blocks_counter == 0) std::cout << "Can not read blocks from file " << filename.c_str() << "\nReason: " << errorMessage.str(); diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 7106410..1686389 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -22,4 +22,4 @@ endif(UNIX) target_link_libraries(${PROJECT_NAME} easy_profiler ${SPEC_LIB}) target_link_libraries(${DISABLED_PROFILER_NAME} easy_profiler ${SPEC_LIB}) -target_compile_definitions(${DISABLED_PROFILER_NAME} PRIVATE FULL_DISABLE_PROFILER) +target_compile_definitions(${PROJECT_NAME} PRIVATE BUILD_WITH_EASY_PROFILER) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 51fa9e1..22de932 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES ) add_definitions( -D_BUILD_PROFILER + -DBUILD_WITH_EASY_PROFILER ) if(WIN32) diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index 542012c..7553946 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -117,7 +117,6 @@ extern "C" { PROFILER_API void setLowPriorityEventTracing(bool) { } #endif -#ifndef _WIN32 PROFILER_API void setContextSwitchLogFilename(const char* name) { return MANAGER.setContextSwitchLogFilename(name); @@ -127,7 +126,6 @@ extern "C" { { return MANAGER.getContextSwitchLogFilename(); } -#endif PROFILER_API void startListenSignalToCapture() { @@ -474,6 +472,9 @@ void ProfileManager::endBlock() if (!m_isEnabled.load(std::memory_order_acquire)) return; + if (THREAD_STORAGE == nullptr) + THREAD_STORAGE = &threadStorage(getCurrentThreadId()); + if (THREAD_STORAGE->blocks.openedList.empty()) return; diff --git a/src/profile_manager.h b/src/profile_manager.h index 1226597..5baccab 100644 --- a/src/profile_manager.h +++ b/src/profile_manager.h @@ -345,9 +345,7 @@ class ProfileManager std::atomic_bool m_isEnabled; std::atomic_bool m_isEventTracingEnabled; -#ifndef _WIN32 std::string m_csInfoFilename = "/tmp/cs_profiling_info.log"; -#endif uint32_t dumpBlocksToStream(profiler::OStream& _outputStream); void setBlockStatus(profiler::block_id_t _id, profiler::EasyBlockStatus _status); @@ -381,7 +379,6 @@ public: uint32_t dumpBlocksToFile(const char* filename); const char* registerThread(const char* name, profiler::ThreadGuard& threadGuard); -#ifndef _WIN32 void setContextSwitchLogFilename(const char* name) { m_csInfoFilename = name; @@ -391,7 +388,6 @@ public: { return m_csInfoFilename.c_str(); } -#endif void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin = true); void storeContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, bool _lockSpin = true); diff --git a/src/reader.cpp b/src/reader.cpp index 69120b4..6178e41 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -60,6 +60,13 @@ const uint32_t COMPATIBLE_VERSIONS[] = { }; const uint16_t COMPATIBLE_VERSIONS_NUM = sizeof(COMPATIBLE_VERSIONS) / sizeof(uint32_t); +#undef EASY_FULL_VER + +const int64_t TIME_FACTOR = 1000000000LL; +const uint32_t PROFILER_SIGNATURE = ('E' << 24) | ('a' << 16) | ('s' << 8) | 'y'; + +////////////////////////////////////////////////////////////////////////// + bool isCompatibleVersion(uint32_t _version) { if (_version == ::profiler::EASY_FULL_VERSION) @@ -67,18 +74,29 @@ bool isCompatibleVersion(uint32_t _version) return COMPATIBLE_VERSIONS_NUM > 1 && ::std::binary_search(COMPATIBLE_VERSIONS + 1, COMPATIBLE_VERSIONS + COMPATIBLE_VERSIONS_NUM, _version); } -#undef EASY_FULL_VER +inline void write(::std::stringstream& _stream, const char* _value, size_t _size) +{ + _stream.write(_value, _size); +} + +template +inline void write(::std::stringstream& _stream, const T& _value) +{ + _stream.write((const char*)&_value, sizeof(T)); +} + +////////////////////////////////////////////////////////////////////////// namespace profiler { - void SerializedData::set(char* _data, size_t _size) + void SerializedData::set(char* _data, uint64_t _size) { delete [] m_data; m_data = _data; m_size = _size; } - void SerializedData::set(size_t _size) + void SerializedData::set(uint64_t _size) { if (_size != 0) set(new char[_size], _size); @@ -86,7 +104,7 @@ namespace profiler { set(nullptr, 0); } - void SerializedData::extend(size_t _size) + void SerializedData::extend(uint64_t _size) { auto olddata = m_data; auto oldsize = m_size; @@ -234,23 +252,24 @@ void validate_pointers(::std::atomic& _progress, const char* _oldbase, ::pr ////////////////////////////////////////////////////////////////////////// -const int64_t TIME_FACTOR = 1000000000LL; -const uint32_t PROFILER_SIGNATURE = ('E' << 24) | ('a' << 16) | ('s' << 8) | 'y'; - -////////////////////////////////////////////////////////////////////////// - extern "C" { PROFILER_API ::profiler::block_index_t fillTreesFromFile(::std::atomic& progress, const char* filename, - ::profiler::SerializedData& serialized_blocks, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::profiler::blocks_t& blocks, - ::profiler::thread_blocks_tree_t& threaded_trees, - bool gather_statistics, - ::std::stringstream& _log) + ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::profiler::blocks_t& blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + bool gather_statistics, + ::std::stringstream& _log) { - progress.store(0); + auto oldprogress = progress.exchange(0, ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return 0; + } ::std::ifstream inFile(filename, ::std::fstream::binary); if (!inFile.is_open()) @@ -265,7 +284,8 @@ extern "C" { stringstream_parent& s = str; auto oldbuf = s.rdbuf(inFile.rdbuf()); - auto result = fillTreesFromStream(progress, str, serialized_blocks, serialized_descriptors, descriptors, blocks, threaded_trees, gather_statistics, _log); + auto result = fillTreesFromStream(progress, str, serialized_blocks, serialized_descriptors, descriptors, blocks, + threaded_trees, total_descriptors_number, gather_statistics, _log); s.rdbuf(oldbuf); return result; @@ -274,17 +294,23 @@ extern "C" { ////////////////////////////////////////////////////////////////////////// PROFILER_API ::profiler::block_index_t fillTreesFromStream(::std::atomic& progress, ::std::stringstream& inFile, - ::profiler::SerializedData& serialized_blocks, - ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors, - ::profiler::blocks_t& blocks, - ::profiler::thread_blocks_tree_t& threaded_trees, - bool gather_statistics, - ::std::stringstream& _log) + ::profiler::SerializedData& serialized_blocks, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::profiler::blocks_t& blocks, + ::profiler::thread_blocks_tree_t& threaded_trees, + uint32_t& total_descriptors_number, + bool gather_statistics, + ::std::stringstream& _log) { EASY_FUNCTION(::profiler::colors::Cyan); - progress.store(0); + auto oldprogress = progress.exchange(0, ::std::memory_order_release); + if (oldprogress < 0) + { + _log << "Reading was interrupted"; + return 0; + } uint32_t signature = 0; inFile.read((char*)&signature, sizeof(uint32_t)); @@ -305,7 +331,8 @@ extern "C" { int64_t cpu_frequency = 0LL; inFile.read((char*)&cpu_frequency, sizeof(int64_t)); - ::profiler::timestamp_t begin_time = 0, end_time = 0; + ::profiler::timestamp_t begin_time = 0ULL; + ::profiler::timestamp_t end_time = 0ULL; inFile.read((char*)&begin_time, sizeof(::profiler::timestamp_t)); inFile.read((char*)&end_time, sizeof(::profiler::timestamp_t)); if (cpu_frequency != 0) @@ -317,7 +344,7 @@ extern "C" { } uint32_t total_blocks_number = 0; - inFile.read((char*)&total_blocks_number, sizeof(decltype(total_blocks_number))); + inFile.read((char*)&total_blocks_number, sizeof(uint32_t)); if (total_blocks_number == 0) { _log << "Profiled blocks number == 0"; @@ -332,8 +359,8 @@ extern "C" { return 0; } - uint32_t total_descriptors_number = 0; - inFile.read((char*)&total_descriptors_number, sizeof(decltype(total_descriptors_number))); + total_descriptors_number = 0; + inFile.read((char*)&total_descriptors_number, sizeof(uint32_t)); if (total_descriptors_number == 0) { _log << "Blocks description number == 0";