From 8fd70c5420cbc6beb7d5f71e6cd97c7622653612 Mon Sep 17 00:00:00 2001 From: Victor Zarubkin Date: Sun, 25 Sep 2016 11:49:49 +0300 Subject: [PATCH] Many new features: * Reading blocks from stream without temporary file; * Reading blocks description from stream; * Control blocks statuses over network: connect to the application, refresh blocks list, change block's status, launch profiling. --- include/profiler/easy_net.h | 44 +- include/profiler/reader.h | 35 +- profiler_gui/descriptors_tree_widget.cpp | 133 +++- profiler_gui/descriptors_tree_widget.h | 11 +- profiler_gui/globals.cpp | 1 + profiler_gui/globals.h | 1 + profiler_gui/globals_qobjects.h | 2 + profiler_gui/icons/attribution.txt | 5 +- profiler_gui/icons/list.svg | 63 ++ profiler_gui/icons/search-next.svg | 57 ++ profiler_gui/icons/search-prev.svg | 57 ++ profiler_gui/main_window.cpp | 917 ++++++++++++++--------- profiler_gui/main_window.h | 90 ++- profiler_gui/resources.qrc | 3 + sample/main.cpp | 24 +- src/event_trace_win.cpp | 15 +- src/profile_manager.cpp | 191 +++-- src/profile_manager.h | 2 +- src/reader.cpp | 681 +++++++++-------- 19 files changed, 1543 insertions(+), 789 deletions(-) create mode 100644 profiler_gui/icons/list.svg create mode 100644 profiler_gui/icons/search-next.svg create mode 100644 profiler_gui/icons/search-prev.svg diff --git a/include/profiler/easy_net.h b/include/profiler/easy_net.h index 73cb75d..7124730 100644 --- a/include/profiler/easy_net.h +++ b/include/profiler/easy_net.h @@ -14,13 +14,21 @@ const uint32_t EASY_MESSAGE_SIGN = 20160909; enum MessageType : uint8_t { MESSAGE_TYPE_ZERO, + MESSAGE_TYPE_REQUEST_START_CAPTURE, MESSAGE_TYPE_REPLY_START_CAPTURING, MESSAGE_TYPE_REQUEST_STOP_CAPTURE, - MESSAGE_TYPE_REPLY_PREPARE_BLOCKS, - MESSAGE_TYPE_REPLY_END_SEND_BLOCKS, + MESSAGE_TYPE_REPLY_BLOCKS, - MESSAGE_TYPE_ACCEPTED_CONNECTION + MESSAGE_TYPE_REPLY_BLOCKS_END, + + MESSAGE_TYPE_ACCEPTED_CONNECTION, + + MESSAGE_TYPE_REQUEST_BLOCKS_DESCRIPTION, + MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION, + MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END, + + MESSAGE_TYPE_EDIT_BLOCK_STATUS, }; struct Message @@ -41,14 +49,36 @@ struct DataMessage : public Message { uint32_t size = 0;//bytes - DataMessage(): - Message(MESSAGE_TYPE_REPLY_BLOCKS) + DataMessage(MessageType _t = MESSAGE_TYPE_REPLY_BLOCKS) : + Message(_t) {} - DataMessage(uint32_t _s): - Message(MESSAGE_TYPE_REPLY_BLOCKS) + DataMessage(uint32_t _s, MessageType _t = MESSAGE_TYPE_REPLY_BLOCKS) : + Message(_t) , size(_s) {} + + const char* data() const + { + return reinterpret_cast(this) + sizeof(DataMessage); + } +}; + +struct BlockStatusMessage : public Message +{ + uint32_t id; + uint8_t status; + + BlockStatusMessage(uint32_t _id, uint8_t _status) + : Message(MESSAGE_TYPE_EDIT_BLOCK_STATUS) + , id(_id) + , status(_status) + { + } + +private: + + BlockStatusMessage() = delete; }; #pragma pack(pop) diff --git a/include/profiler/reader.h b/include/profiler/reader.h index 204c86a..2e9ec9b 100644 --- a/include/profiler/reader.h +++ b/include/profiler/reader.h @@ -323,13 +323,28 @@ namespace profiler { } // END of 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); +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); + + 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); + + PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& str, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors); +} inline ::profiler::block_index_t fillTreesFromFile(const char* filename, ::profiler::SerializedData& serialized_blocks, ::profiler::SerializedData& serialized_descriptors, ::profiler::descriptors_list_t& descriptors, @@ -340,6 +355,12 @@ inline ::profiler::block_index_t fillTreesFromFile(const char* filename, ::profi return fillTreesFromFile(progress, filename, serialized_blocks, serialized_descriptors, descriptors, _blocks, threaded_trees, gather_statistics); } +inline bool readDescriptionsFromStream(::std::stringstream& str, ::profiler::SerializedData& serialized_descriptors, ::profiler::descriptors_list_t& descriptors) +{ + ::std::atomic progress = ATOMIC_VAR_INIT(0); + return readDescriptionsFromStream(progress, str, serialized_descriptors, descriptors); +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// #endif // PROFILER_READER____H diff --git a/profiler_gui/descriptors_tree_widget.cpp b/profiler_gui/descriptors_tree_widget.cpp index e2b8ebf..7759f1a 100644 --- a/profiler_gui/descriptors_tree_widget.cpp +++ b/profiler_gui/descriptors_tree_widget.cpp @@ -40,7 +40,7 @@ #include #include #include -#include +#include #include #include #include @@ -222,6 +222,7 @@ void EasyDescTreeWidget::contextMenuEvent(QContextMenuEvent* _event) _event->accept(); QMenu menu; + menu.setToolTipsVisible(true); auto action = menu.addAction("Expand all"); SET_ICON(action, ":/Expand"); connect(action, &QAction::triggered, this, &This::expandAll); @@ -253,22 +254,27 @@ void EasyDescTreeWidget::contextMenuEvent(QContextMenuEvent* _event) menu.addSeparator(); auto submenu = menu.addMenu("Change status"); + submenu->setToolTipsVisible(true); -#define ADD_STATUS_ACTION(NameValue, StatusValue)\ +#define ADD_STATUS_ACTION(NameValue, StatusValue, ToolTipValue)\ action = submenu->addAction(NameValue);\ action->setCheckable(true);\ action->setChecked(desc.status() == StatusValue);\ action->setData(static_cast(StatusValue));\ + action->setToolTip(ToolTipValue);\ connect(action, &QAction::triggered, this, &This::onBlockStatusChangeClicked) - ADD_STATUS_ACTION("Off", ::profiler::OFF); - ADD_STATUS_ACTION("On", ::profiler::ON); - ADD_STATUS_ACTION("Force-On", ::profiler::FORCE_ON); - ADD_STATUS_ACTION("Off-recursive", ::profiler::OFF_RECURSIVE); - ADD_STATUS_ACTION("On-without-children", ::profiler::ON_WITHOUT_CHILDREN); - ADD_STATUS_ACTION("Force-On-without-children", ::profiler::FORCE_ON_WITHOUT_CHILDREN); - + ADD_STATUS_ACTION("Off", ::profiler::OFF, "Do not profile this block."); + ADD_STATUS_ACTION("On", ::profiler::ON, "Profile this block\nif parent enabled children."); + ADD_STATUS_ACTION("Force-On", ::profiler::FORCE_ON, "Always profile this block even\nif it's parent disabled children."); + ADD_STATUS_ACTION("Off-recursive", ::profiler::OFF_RECURSIVE, "Do not profile neither this block\nnor it's children."); + ADD_STATUS_ACTION("On-without-children", ::profiler::ON_WITHOUT_CHILDREN, "Profile this block, but\ndo not profile it's children."); + ADD_STATUS_ACTION("Force-On-without-children", ::profiler::FORCE_ON_WITHOUT_CHILDREN, "Always profile this block, but\ndo not profile it's children."); #undef ADD_STATUS_ACTION + + submenu->setEnabled(EASY_GLOBALS.connected); + if (!EASY_GLOBALS.connected) + submenu->setTitle(QString("%1 (connection needed)").arg(submenu->title())); } menu.exec(QCursor::pos()); @@ -340,8 +346,6 @@ struct FileItems void EasyDescTreeWidget::build() { - clearSilent(false); - auto f = font(); f.setBold(true); @@ -421,6 +425,9 @@ void EasyDescTreeWidget::onItemExpand(QTreeWidgetItem*) void EasyDescTreeWidget::onDoubleClick(QTreeWidgetItem* _item, int _column) { + if (!EASY_GLOBALS.connected) + return; + if (_column >= DESC_COL_TYPE && _item->parent() != nullptr) { auto item = static_cast(_item); @@ -460,7 +467,7 @@ void EasyDescTreeWidget::onCurrentItemChange(QTreeWidgetItem* _item, QTreeWidget void EasyDescTreeWidget::onBlockStatusChangeClicked(bool _checked) { - if (!_checked) + if (!_checked || !EASY_GLOBALS.connected) return; auto item = currentItem(); @@ -686,14 +693,41 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent) , m_tree(new EasyDescTreeWidget()) , m_searchBox(new QLineEdit()) , m_foundNumber(new QLabel("Found 0 matches")) + , m_searchButton(nullptr) { - m_searchBox->setMinimumWidth(64); + m_searchBox->setFixedWidth(200); + + auto tb = new QToolBar(); + auto refreshButton = tb->addAction(QIcon(":/Reload"), tr("Refresh blocks list")); + refreshButton->setEnabled(EASY_GLOBALS.connected); + 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)); + + auto actionGroup = new QActionGroup(this); + 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); + + a = new QAction(tr("Find previous"), actionGroup); + a->setCheckable(true); + connect(a, &QAction::triggered, this, &This::findPrevFromMenu); + m_searchButton->menu()->addAction(a); auto searchbox = new QHBoxLayout(); searchbox->setContentsMargins(0, 0, 0, 0); - searchbox->addWidget(new QLabel("Search:")); - searchbox->addWidget(m_searchBox); - searchbox->addStretch(50); + searchbox->addWidget(tb); + searchbox->addStretch(100); searchbox->addWidget(m_foundNumber, Qt::AlignRight); auto lay = new QVBoxLayout(this); @@ -702,6 +736,7 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent) lay->addWidget(m_tree); connect(m_searchBox, &QLineEdit::returnPressed, this, &This::onSeachBoxReturnPressed); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::connectionChanged, refreshButton, &QAction::setEnabled); } EasyDescWidget::~EasyDescWidget() @@ -713,16 +748,10 @@ void EasyDescWidget::keyPressEvent(QKeyEvent* _event) { if (_event->key() == Qt::Key_F3) { - int matches = 0; if (_event->modifiers() & Qt::ShiftModifier) - matches = m_tree->findPrev(m_searchBox->text()); + findPrev(true); else - matches = m_tree->findNext(m_searchBox->text()); - - if (matches == 1) - m_foundNumber->setText(QString("Found 1 match")); - else - m_foundNumber->setText(QString("Found %1 matches").arg(matches)); + findNext(true); } _event->accept(); @@ -730,12 +759,14 @@ void EasyDescWidget::keyPressEvent(QKeyEvent* _event) void EasyDescWidget::build() { + clear(); m_tree->build(); } void EasyDescWidget::clear() { m_tree->clearSilent(true); + m_foundNumber->setText(QString("Found 0 matches")); } void EasyDescWidget::onSeachBoxReturnPressed() @@ -748,4 +779,58 @@ void EasyDescWidget::onSeachBoxReturnPressed() m_foundNumber->setText(QString("Found %1 matches").arg(matches)); } +void EasyDescWidget::findNext(bool) +{ + auto matches = m_tree->findNext(m_searchBox->text()); + + if (matches == 1) + m_foundNumber->setText(QString("Found 1 match")); + else + m_foundNumber->setText(QString("Found %1 matches").arg(matches)); +} + +void EasyDescWidget::findPrev(bool) +{ + auto matches = m_tree->findPrev(m_searchBox->text()); + + if (matches == 1) + m_foundNumber->setText(QString("Found 1 match")); + else + m_foundNumber->setText(QString("Found %1 matches").arg(matches)); +} + +void EasyDescWidget::findNextFromMenu(bool _checked) +{ + if (!_checked) + return; + + if (m_searchButton->data().toBool() == false) + { + m_searchButton->setData(true); + m_searchButton->setText(tr("Find next")); + m_searchButton->setIcon(QIcon(":/Search-next")); + disconnect(m_searchButton, &QAction::triggered, this, &This::findPrev); + connect(m_searchButton, &QAction::triggered, this, &This::findNext); + } + + findNext(true); +} + +void EasyDescWidget::findPrevFromMenu(bool _checked) +{ + if (!_checked) + return; + + if (m_searchButton->data().toBool() == true) + { + m_searchButton->setData(false); + m_searchButton->setText(tr("Find prev")); + m_searchButton->setIcon(QIcon(":/Search-prev")); + disconnect(m_searchButton, &QAction::triggered, this, &This::findNext); + connect(m_searchButton, &QAction::triggered, this, &This::findPrev); + } + + findPrev(true); +} + ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/descriptors_tree_widget.h b/profiler_gui/descriptors_tree_widget.h index d2512dd..780662c 100644 --- a/profiler_gui/descriptors_tree_widget.h +++ b/profiler_gui/descriptors_tree_widget.h @@ -135,9 +135,10 @@ class EasyDescWidget : public QWidget private: - EasyDescTreeWidget* m_tree; - class QLineEdit* m_searchBox; - class QLabel* m_foundNumber; + EasyDescTreeWidget* m_tree; + class QLineEdit* m_searchBox; + class QLabel* m_foundNumber; + class QAction* m_searchButton; public: @@ -157,6 +158,10 @@ public: private slots: void onSeachBoxReturnPressed(); + void findNext(bool); + void findPrev(bool); + void findNextFromMenu(bool); + void findPrevFromMenu(bool); }; // END of class EasyDescWidget. diff --git a/profiler_gui/globals.cpp b/profiler_gui/globals.cpp index f4d24ee..572acbb 100644 --- a/profiler_gui/globals.cpp +++ b/profiler_gui/globals.cpp @@ -47,6 +47,7 @@ namespace profiler_gui { : selected_thread(0U) , selected_block(::profiler_gui::numeric_max()) , chrono_text_position(ChronoTextPosition_Center) + , connected(false) , enable_event_indicators(true) , enable_statistics(true) , draw_graphics_items_borders(true) diff --git a/profiler_gui/globals.h b/profiler_gui/globals.h index 72bfa52..292fdab 100644 --- a/profiler_gui/globals.h +++ b/profiler_gui/globals.h @@ -97,6 +97,7 @@ namespace profiler_gui { ::profiler::thread_id_t selected_thread; ///< Current selected thread id ::profiler::block_index_t selected_block; ///< Current selected profiler block index ChronometerTextPosition chrono_text_position; ///< Selected interval text position + bool connected; ///< Is connected to source (to be able to capture profiling information) 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 diff --git a/profiler_gui/globals_qobjects.h b/profiler_gui/globals_qobjects.h index 20cc2b8..1addf26 100644 --- a/profiler_gui/globals_qobjects.h +++ b/profiler_gui/globals_qobjects.h @@ -51,6 +51,8 @@ namespace profiler_gui { void selectedBlockChanged(uint32_t _block_index); void itemsExpandStateChanged(); void blockStatusChanged(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status); + void connectionChanged(bool _connected); + void blocksRefreshRequired(bool); }; // END of class EasyGlobalSignals. diff --git a/profiler_gui/icons/attribution.txt b/profiler_gui/icons/attribution.txt index 37a75b0..25e902b 100644 --- a/profiler_gui/icons/attribution.txt +++ b/profiler_gui/icons/attribution.txt @@ -14,4 +14,7 @@ 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 \ No newline at end of file +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 diff --git a/profiler_gui/icons/list.svg b/profiler_gui/icons/list.svg new file mode 100644 index 0000000..af6bf94 --- /dev/null +++ b/profiler_gui/icons/list.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/search-next.svg b/profiler_gui/icons/search-next.svg new file mode 100644 index 0000000..a1f3ae7 --- /dev/null +++ b/profiler_gui/icons/search-next.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/icons/search-prev.svg b/profiler_gui/icons/search-prev.svg new file mode 100644 index 0000000..b629110 --- /dev/null +++ b/profiler_gui/icons/search-prev.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index 84cc107..7ce38e6 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -122,6 +122,7 @@ EasyMainWindow::EasyMainWindow() : Parent() toolbar->setObjectName("ProfilerGUI_MainToolBar"); toolbar->addAction(QIcon(":/Delete"), tr("Clear all"), this, SLOT(onDeleteClicked(bool))); + toolbar->addAction(QIcon(":/List"), tr("Blocks"), this, SLOT(onEditBlocksClicked(bool))); m_captureAction = toolbar->addAction(QIcon(":/Start"), tr("Capture"), this, SLOT(onCaptureClicked(bool))); m_captureAction->setEnabled(false); @@ -262,14 +263,6 @@ EasyMainWindow::EasyMainWindow() : Parent() - menu = menuBar()->addMenu("&Edit"); - m_editBlocksAction = menu->addAction(tr("Edit blocks"), this, SLOT(onEditBlocksClicked(bool))); - m_editBlocksAction->setEnabled(false); - action = menu->addAction(tr("Clear all"), this, SLOT(onDeleteClicked(bool))); - SET_ICON(action, ":/Delete"); - - - menu = menuBar()->addMenu("&Settings"); action = menu->addAction("Statistics enabled"); action->setCheckable(true); @@ -310,7 +303,7 @@ EasyMainWindow::EasyMainWindow() : Parent() connect(graphicsView->view(), &EasyGraphicsView::intervalChanged, treeWidget, &EasyTreeWidget::setTreeBlocks); connect(&m_readerTimer, &QTimer::timeout, this, &This::onFileReaderTimeout); - connect(&m_downloadedTimer, &QTimer::timeout, this, &This::onDownloadTimeout); + connect(&m_listenerTimer, &QTimer::timeout, this, &This::onListenerTimerTimeout); m_progress = new QProgressDialog("Loading file...", "Cancel", 0, 100, this); @@ -321,14 +314,6 @@ EasyMainWindow::EasyMainWindow() : Parent() //m_progress->hide(); connect(m_progress, &QProgressDialog::canceled, this, &This::onFileReaderCancel); - - m_downloadingProgress = new QProgressDialog("Capturing frames...", "Stop", 0, 100, this); - m_downloadingProgress->setFixedWidth(300); - m_downloadingProgress->setWindowTitle("EasyProfiler"); - m_downloadingProgress->setModal(true); - m_downloadingProgress->setValue(100); - //m_downloadedTimer.start(10); - loadGeometry(); if(QCoreApplication::arguments().size() > 1) @@ -336,172 +321,14 @@ EasyMainWindow::EasyMainWindow() : Parent() auto opened_filename = QCoreApplication::arguments().at(1); loadFile(opened_filename); } -} -void EasyMainWindow::listen() -{ - profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE); - - - //std::this_thread::sleep_for(std::chrono::seconds(2)); - static const int buffer_size = 8 * 1024 * 1024; - char*buffer = new char[buffer_size]; - int seek = 0; - int bytes = 0; - - static auto timeBegin = std::chrono::system_clock::now(); - bool isListen = true; - while (isListen) - { - if ((bytes - seek) == 0) - { - bytes = m_easySocket.receive(buffer, buffer_size); - - if(bytes == -1) - { - if(m_easySocket.state() == EasySocket::CONNECTION_STATE_DISCONNECTED) - isListen = false; - - seek = 0; - bytes = 0; - - continue; - } - - seek = 0; - } - - char *buf = &buffer[seek]; - - if (bytes == 0){ - isListen = false; - continue; - } - - if (bytes > 0) - { - profiler::net::Message* message = (profiler::net::Message*)buf; - if (!message->isEasyNetMessage()) - continue; - - switch (message->type) - { - case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION: - { - qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION"; - //m_easySocket.send(&request, sizeof(request)); - seek += sizeof(profiler::net::Message); - break; - } - - case profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING: - { - qInfo() << "Receive MESSAGE_TYPE_REPLY_START_CAPTURING"; - seek += sizeof(profiler::net::Message); - break; - } - - case profiler::net::MESSAGE_TYPE_REPLY_PREPARE_BLOCKS: - { - qInfo() << "Receive MESSAGE_TYPE_REPLY_PREPARE_BLOCKS"; - m_isClientPreparedBlocks = true; - seek += sizeof(profiler::net::Message); - break; - } - - case profiler::net::MESSAGE_TYPE_REPLY_END_SEND_BLOCKS: - { - qInfo() << "Receive MESSAGE_TYPE_REPLY_END_SEND_BLOCKS"; - seek += sizeof(profiler::net::Message); - - auto timeEnd = std::chrono::system_clock::now(); - auto dT = std::chrono::duration_cast(timeEnd - timeBegin); - auto dTsec = std::chrono::duration_cast(timeEnd - timeBegin); - qInfo() << "recieve" << m_receivedProfileData.str().size() << dT.count() << "ms" << double(m_receivedProfileData.str().size())*1000.0 / double(dT.count()) / 1024.0 << "kBytes/sec"; - m_recFrames = false; - - - qInfo() << "Write FILE"; - std::string tempfilename = "test_rec.prof"; - std::ofstream of(tempfilename, std::fstream::binary); - of << m_receivedProfileData.str(); - of.close(); - - m_receivedProfileData.str(std::string()); - m_receivedProfileData.clear(); - //loadFile(QString(tempfilename.c_str())); - m_recFrames = false; - isListen = false; - - continue; - } - break; - - case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS: - { - qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS"; - - seek += sizeof(profiler::net::DataMessage); - profiler::net::DataMessage* dm = (profiler::net::DataMessage*)message; - timeBegin = std::chrono::system_clock::now(); - - int neededSize = dm->size; - - - buf = &buffer[seek]; - m_receivedProfileData.write(buf, bytes - seek); - neededSize -= bytes - seek; - seek = 0; - bytes = 0; - - - int loaded = 0; - while (neededSize > 0) - { - bytes = m_easySocket.receive(buffer, buffer_size); - - if (bytes == -1) - { - if(m_easySocket.state() == EasySocket::CONNECTION_STATE_DISCONNECTED) - { - isListen = false; - neededSize = 0; - } - continue; - } - - buf = &buffer[0]; - int toWrite = std::min(bytes, neededSize); - m_receivedProfileData.write(buf, toWrite); - neededSize -= toWrite; - loaded += toWrite; - seek = toWrite; - - m_downloadedBytes.store((100 * loaded / (neededSize + 1)), ::std::memory_order_release); - } - - break; - } - - default: - //qInfo() << "Receive unknown " << message->type; - break; - } - } - } - - delete [] buffer; + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blockStatusChanged, this, &This::onBlockStatusChange); + connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blocksRefreshRequired, this, &This::onGetBlockDescriptionsClicked); } EasyMainWindow::~EasyMainWindow() { - if (m_descTreeDialog != nullptr) - delete m_descTreeDialog; - delete m_progress; - - if (m_thread.joinable()) - m_thread.join(); } ////////////////////////////////////////////////////////////////////////// @@ -527,6 +354,16 @@ void EasyMainWindow::loadFile(const QString& filename) m_reader.load(filename); } +void EasyMainWindow::readStream(::std::stringstream& data) +{ + m_progress->setLabelText(tr("Reading from stream...")); + + m_progress->setValue(0); + m_progress->show(); + m_readerTimer.start(LOADER_TIMER_INTERVAL); + m_reader.load(data); +} + ////////////////////////////////////////////////////////////////////////// void EasyMainWindow::onReloadFileClicked(bool) @@ -548,7 +385,6 @@ void EasyMainWindow::onDeleteClicked(bool) #endif if (m_dialogDescTree != nullptr) m_dialogDescTree->clear(); - m_editBlocksAction->setEnabled(false); EASY_GLOBALS.selected_thread = 0; ::profiler_gui::set_max(EASY_GLOBALS.selected_block); @@ -672,25 +508,24 @@ void EasyMainWindow::onEditBlocksClicked(bool) } m_descTreeDialog = new QDialog(); + m_descTreeDialog->setAttribute(Qt::WA_DeleteOnClose, true); m_descTreeDialog->setWindowTitle("EasyProfiler"); m_descTreeDialog->resize(800, 600); connect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose); - m_dialogDescTree = new EasyDescWidget(); - m_dialogDescTree->build(); - auto l = new QVBoxLayout(m_descTreeDialog); + m_dialogDescTree = new EasyDescWidget(m_descTreeDialog); l->addWidget(m_dialogDescTree); - m_descTreeDialog->setLayout(l); + + m_dialogDescTree->build(); m_descTreeDialog->show(); } void EasyMainWindow::onDescTreeDialogClose(int) { - m_dialogDescTree = nullptr; disconnect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose); - delete m_descTreeDialog; + m_dialogDescTree = nullptr; m_descTreeDialog = nullptr; } @@ -699,6 +534,14 @@ void EasyMainWindow::onDescTreeDialogClose(int) void EasyMainWindow::closeEvent(QCloseEvent* close_event) { saveSettingsAndGeometry(); + + if (m_descTreeDialog != nullptr) + { + m_descTreeDialog->reject(); + m_descTreeDialog = nullptr; + m_dialogDescTree = nullptr; + } + Parent::closeEvent(close_event); } @@ -789,19 +632,62 @@ void EasyMainWindow::saveSettingsAndGeometry() ////////////////////////////////////////////////////////////////////////// -void EasyMainWindow::onDownloadTimeout() +void EasyMainWindow::onListenerTimerTimeout() { - if (!m_downloading){ - m_downloadingProgress->setValue(100); - //m_downloadingProgress->hide(); - m_downloadedTimer.stop(); - } - else{ - m_downloadingProgress->setValue(m_downloadedBytes.load(::std::memory_order_acquire)); - } - + if (!m_listener.connected()) + m_listenerDialog->reject(); } +void EasyMainWindow::onListenerDialogClose(int) +{ + m_listenerTimer.stop(); + disconnect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose); + m_listenerDialog = nullptr; + + switch (m_listener.regime()) + { + case LISTENER_CAPTURE: + { + m_listenerDialog = new QMessageBox(QMessageBox::Information, "Receiving data...", "This process may take some time.", QMessageBox::NoButton, this); + m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true); + m_listenerDialog->show(); + + m_listener.stopCapture(); + + m_listenerDialog->reject(); + m_listenerDialog = nullptr; + + if (m_listener.size() != 0) + { + readStream(m_listener.data()); + m_listener.clearData(); + } + + break; + } + + case LISTENER_DESCRIBE: + { + break; + } + + default: + return; + } + + if (!m_listener.connected()) + { + QMessageBox::warning(this, "Warning", "Application was disconnected", QMessageBox::Close); + EASY_GLOBALS.connected = false; + m_captureAction->setEnabled(false); + SET_ICON(m_connectAction, ":/Connection"); + + emit EASY_GLOBALS.events.connectionChanged(false); + } +} + +////////////////////////////////////////////////////////////////////////// + void EasyMainWindow::onFileReaderTimeout() { if (m_reader.done()) @@ -820,11 +706,15 @@ void EasyMainWindow::onFileReaderTimeout() if (threads_map.size() > 0xff) { - qWarning() << "Warning: file " << filename << " contains " << threads_map.size() << " threads!"; + if (m_reader.isFile()) + qWarning() << "Warning: file " << filename << " contains " << threads_map.size() << " threads!"; + else + qWarning() << "Warning: input stream contains " << threads_map.size() << " threads!"; qWarning() << "Warning: Currently, maximum number of displayed threads is 255! Some threads will not be displayed."; } - m_lastFile = ::std::move(filename); + if (m_reader.isFile()) + m_lastFile = ::std::move(filename); m_serializedBlocks = ::std::move(serialized_blocks); m_serializedDescriptors = ::std::move(serialized_descriptors); EASY_GLOBALS.selected_thread = 0; @@ -846,14 +736,17 @@ void EasyMainWindow::onFileReaderTimeout() #if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 static_cast(m_descTreeWidget->widget())->build(); #endif - m_editBlocksAction->setEnabled(true); if (m_dialogDescTree != nullptr) m_dialogDescTree->build(); } - else + else if (m_reader.isFile()) { qWarning() << "Warning: Can not open file " << m_reader.filename() << " or file is corrupted"; } + else + { + qWarning() << "Warning: Can not read from stream: bad data"; + } m_reader.interrupt(); @@ -892,6 +785,11 @@ EasyFileReader::~EasyFileReader() interrupt(); } +const bool EasyFileReader::isFile() const +{ + return m_isFile; +} + bool EasyFileReader::done() const { return m_bDone.load(::std::memory_order_acquire); @@ -916,6 +814,7 @@ void EasyFileReader::load(const QString& _filename) { interrupt(); + 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), ::std::memory_order_release); @@ -924,6 +823,20 @@ void EasyFileReader::load(const QString& _filename) }, EASY_GLOBALS.enable_statistics)); } +void EasyFileReader::load(::std::stringstream& _stream) +{ + interrupt(); + + m_isFile = false; + 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), ::std::memory_order_release); + m_progress.store(100, ::std::memory_order_release); + m_bDone.store(true, ::std::memory_order_release); + }, EASY_GLOBALS.enable_statistics)); +} + void EasyFileReader::interrupt() { m_progress.store(-100, ::std::memory_order_release); @@ -938,6 +851,9 @@ void EasyFileReader::interrupt() m_descriptors.clear(); m_blocks.clear(); m_blocksTree.clear(); + + decltype(m_stream) dummy; + dummy.swap(m_stream); } void EasyFileReader::get(::profiler::SerializedData& _serializedBlocks, ::profiler::SerializedData& _serializedDescriptors, @@ -959,205 +875,508 @@ void EasyFileReader::get(::profiler::SerializedData& _serializedBlocks, ::profil void EasyMainWindow::onConnectClicked(bool) { - if(m_isConnected) + if(EASY_GLOBALS.connected) return; - m_easySocket.flush(); - m_easySocket.init(); - int res = m_easySocket.setAddress(m_ipEdit->text().toStdString().c_str(), m_portEdit->text().toUShort()); - - //TODO: flush socket after disconenct - res = m_easySocket.connect(); - if (res == -1) + if (!m_listener.connect(m_ipEdit->text().toStdString().c_str(), m_portEdit->text().toUShort())) { QMessageBox::warning(this, "Warning", "Cannot connect with application", QMessageBox::Close); return; } qInfo() << "Connected successfully"; - m_isConnected = true; + EASY_GLOBALS.connected = true; m_captureAction->setEnabled(true); SET_ICON(m_connectAction, ":/Connection-on"); + + emit EASY_GLOBALS.events.connectionChanged(true); } void EasyMainWindow::onCaptureClicked(bool) { - if (!m_isConnected) + if (!EASY_GLOBALS.connected) { QMessageBox::warning(this, "Warning", "No connection with profiling app", QMessageBox::Close); return; } - profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE); - m_easySocket.send(&request, sizeof(request)); - - m_thread = std::thread(&This::listen, this); - - QMessageBox::information(this, "Capturing frames...", "Close this window to stop capturing.", QMessageBox::Close); - - request.type = profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE; - m_easySocket.send(&request, sizeof(request)); - - m_thread.join(); - - m_downloading = false; - - if(m_easySocket.state() == EasySocket::CONNECTION_STATE_DISCONNECTED) + if (m_listener.regime() != LISTENER_IDLE) { - QMessageBox::warning(this,"Warning" ,"Application was disconnected",QMessageBox::Close); - m_isConnected = false; - m_captureAction->setEnabled(false); - SET_ICON(m_connectAction, ":/Connection"); + if (m_listener.regime() == LISTENER_CAPTURE) + QMessageBox::warning(this, "Warning", "Already capturing frames.\nFinish old capturing session first.", QMessageBox::Close); + else + QMessageBox::warning(this, "Warning", "Capturing blocks description.\nFinish old capturing session first.", QMessageBox::Close); return; } - std::string tempfilename = "test_rec.prof"; - loadFile(QString(tempfilename.c_str())); + m_listener.startCapture(); + m_listenerTimer.start(250); + + m_listenerDialog = new QMessageBox(QMessageBox::Information, "Capturing frames...", "Close this dialog to stop capturing.", QMessageBox::Close, this); + m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true); + connect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose); + m_listenerDialog->show(); } -void EasyMainWindow::handleResults(const QString &s) +void EasyMainWindow::onGetBlockDescriptionsClicked(bool) { - -} - -void EasyMainWindow::readTcpData() -{ - static qint64 necessarySize = 0; - static qint64 loadedSize = 0; - static auto timeBegin = std::chrono::system_clock::now(); - while(m_server->bytesAvailable()) + if (!EASY_GLOBALS.connected) { - auto bytesExpected = necessarySize - loadedSize; - QByteArray data; - if (m_recFrames){ - data = m_server->read(qMin(bytesExpected, m_server->bytesAvailable())); - } + QMessageBox::warning(this, "Warning", "No connection with profiling app", QMessageBox::Close); + return; + } + + if (m_listener.regime() != LISTENER_IDLE) + { + if (m_listener.regime() == LISTENER_DESCRIBE) + QMessageBox::warning(this, "Warning", "Already capturing blocks description.\nFinish old capturing session first.", QMessageBox::Close); else + QMessageBox::warning(this, "Warning", "Capturing capturing frames.\nFinish old capturing session first.", QMessageBox::Close); + return; + } + + m_listenerDialog = new QMessageBox(QMessageBox::Information, "Waiting for blocks...", "This may take some time.", QMessageBox::NoButton, this); + m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true); + m_listenerDialog->show(); + + m_listener.requestBlocksDescription(); + + m_listenerDialog->reject(); + m_listenerDialog = nullptr; + + if (m_listener.size() != 0) + { + // Read descriptions from stream + decltype(EASY_GLOBALS.descriptors) descriptors; + decltype(m_serializedDescriptors) serializedDescriptors; + if (readDescriptionsFromStream(m_listener.data(), serializedDescriptors, descriptors)) { - data = m_server->readAll(); - } + if (EASY_GLOBALS.descriptors.size() > descriptors.size()) + onDeleteClicked(true); // Clear all contents because new descriptors list conflicts with old one + EASY_GLOBALS.descriptors.swap(descriptors); + m_serializedDescriptors.swap(serializedDescriptors); - profiler::net::Message* message = (profiler::net::Message*)data.data(); - //qInfo() << "rec size: " << data.size() << " " << QString(data);; - if(!m_recFrames && !message->isEasyNetMessage()){ - return; - } - else if (m_recFrames){ - - if (m_receivedProfileData.str().size() == necessarySize) + if (m_descTreeDialog != nullptr) { - m_recFrames = false; - - auto timeEnd = std::chrono::system_clock::now(); - auto dT = std::chrono::duration_cast(timeEnd - timeBegin); - auto dTsec = std::chrono::duration_cast(timeEnd - timeBegin); - qInfo() << "recieve" << m_receivedProfileData.str().size() << dT.count() << "ms" << double(m_receivedProfileData.str().size())*1000.0 / double(dT.count()) / 1024.0 << "kBytes/sec"; - m_recFrames = false; - - - qInfo() << "Write FILE"; - std::string tempfilename = "test_rec.prof"; - std::ofstream of(tempfilename, std::fstream::binary); - of << m_receivedProfileData.str(); - of.close(); - - m_receivedProfileData.str(std::string()); - m_receivedProfileData.clear(); - loadFile(QString(tempfilename.c_str())); - m_recFrames = false; - - +#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 + static_cast(m_descTreeWidget->widget())->build(); +#endif + m_dialogDescTree->build(); + m_descTreeDialog->raise(); } - - if (m_recFrames) + else { - m_receivedProfileData.write(data.data(), data.size()); - loadedSize += data.size(); + onEditBlocksClicked(true); + } + } + + m_listener.clearData(); + } + + if (!m_listener.connected()) + { + QMessageBox::warning(this, "Warning", "Application was disconnected", QMessageBox::Close); + EASY_GLOBALS.connected = false; + m_captureAction->setEnabled(false); + SET_ICON(m_connectAction, ":/Connection"); + + emit EASY_GLOBALS.events.connectionChanged(false); + } +} + +////////////////////////////////////////////////////////////////////////// + +void EasyMainWindow::onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status) +{ + if (EASY_GLOBALS.connected) + m_listener.sendBlockStatus(_id, _status); +} + +////////////////////////////////////////////////////////////////////////// + +EasySocketListener::EasySocketListener() : m_receivedSize(0), m_regime(LISTENER_IDLE) +{ + m_bInterrupt = ATOMIC_VAR_INIT(false); + m_bConnected = ATOMIC_VAR_INIT(false); +} + +EasySocketListener::~EasySocketListener() +{ + m_bInterrupt.store(true, ::std::memory_order_release); + if (m_thread.joinable()) + m_thread.join(); +} + +bool EasySocketListener::connected() const +{ + return m_bConnected.load(::std::memory_order_acquire); +} + +EasyListenerRegime EasySocketListener::regime() const +{ + return m_regime; +} + +uint64_t EasySocketListener::size() const +{ + return m_receivedSize; +} + +::std::stringstream& EasySocketListener::data() +{ + return m_receivedData; +} + +void EasySocketListener::clearData() +{ + decltype(m_receivedData) dummy; + dummy.swap(m_receivedData); + m_receivedSize = 0; +} + +bool EasySocketListener::connect(const char* _ipaddress, uint16_t _port) +{ + if (connected()) + return true; + + m_easySocket.flush(); + m_easySocket.init(); + int res = m_easySocket.setAddress(_ipaddress, _port); + res = m_easySocket.connect(); + + bool isConnected = res == 0; + m_bConnected.store(isConnected, ::std::memory_order_release); + + return isConnected; +} + +void EasySocketListener::startCapture() +{ + clearData(); + + profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE); + m_easySocket.send(&request, sizeof(request)); + + m_regime = LISTENER_CAPTURE; + m_thread = ::std::move(::std::thread(&EasySocketListener::listenCapture, this)); +} + +void EasySocketListener::stopCapture() +{ + if (!m_thread.joinable() || m_regime != LISTENER_CAPTURE) + return; + + profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE); + m_easySocket.send(&request, sizeof(request)); + + m_thread.join(); + + m_regime = LISTENER_IDLE; +} + +void EasySocketListener::requestBlocksDescription() +{ + clearData(); + + profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_BLOCKS_DESCRIPTION); + m_easySocket.send(&request, sizeof(request)); + + m_regime = LISTENER_DESCRIBE; + listenDescription(); + m_regime = LISTENER_IDLE; +} + +void EasySocketListener::sendBlockStatus(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status) +{ + profiler::net::BlockStatusMessage message(_id, static_cast(_status)); + m_easySocket.send(&message, sizeof(message)); +} + +////////////////////////////////////////////////////////////////////////// + +void EasySocketListener::listenCapture() +{ + // TODO: Merge functions listenCapture() and listenDescription() + + static const int buffer_size = 8 * 1024 * 1024; + char* buffer = new char[buffer_size]; + int seek = 0, bytes = 0; + auto timeBegin = ::std::chrono::system_clock::now(); + + bool isListen = true, disconnected = false; + while (isListen && !m_bInterrupt.load(::std::memory_order_acquire)) + { + if ((bytes - seek) == 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.state() == EasySocket::CONNECTION_STATE_DISCONNECTED) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + disconnected = true; + } + + seek = 0; + bytes = 0; + continue; } - + + seek = 0; } - switch (message->type) { - case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION: + if (bytes == 0) + { + isListen = false; + break; + } + + char* buf = buffer + seek; + + if (bytes > 0) + { + auto message = reinterpret_cast(buf); + if (!message->isEasyNetMessage()) + continue; + + switch (message->type) { - qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION"; + case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION: + { + qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION"; + //m_easySocket.send(&request, sizeof(request)); + seek += sizeof(profiler::net::Message); + break; + } + + case profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING: + { + qInfo() << "Receive MESSAGE_TYPE_REPLY_START_CAPTURING"; + seek += sizeof(profiler::net::Message); + break; + } + + case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_END: + { + qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS_END"; + seek += sizeof(profiler::net::Message); + + const auto dt = ::std::chrono::duration_cast(::std::chrono::system_clock::now() - timeBegin); + const auto bytesNumber = m_receivedData.str().size(); + qInfo() << "recieved " << bytesNumber << " bytes, " << dt.count() << " ms, average speed = " << double(bytesNumber) * 1e3 / double(dt.count()) / 1024. << " kBytes/sec"; + + seek = 0; + bytes = 0; + + isListen = false; + + break; + } + + case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS: + { + qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS"; + + seek += sizeof(profiler::net::DataMessage); + profiler::net::DataMessage* dm = (profiler::net::DataMessage*)message; + timeBegin = std::chrono::system_clock::now(); + + int neededSize = dm->size; + + + buf = buffer + seek; + auto bytesNumber = ::std::min((int)dm->size, bytes - seek); + m_receivedSize += bytesNumber; + m_receivedData.write(buf, bytesNumber); + neededSize -= bytesNumber; + + if (neededSize == 0) + seek += bytesNumber; + else + { + seek = 0; + bytes = 0; + } + + + int loaded = 0; + while (neededSize > 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.state() == EasySocket::CONNECTION_STATE_DISCONNECTED) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + disconnected = true; + neededSize = 0; + } + + break; + } + + buf = buffer; + int toWrite = ::std::min(bytes, neededSize); + m_receivedSize += toWrite; + m_receivedData.write(buf, toWrite); + neededSize -= toWrite; + loaded += toWrite; + seek = toWrite; + } + + break; + } + + default: + //qInfo() << "Receive unknown " << message->type; + break; } - break; - case profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING: - { - qInfo() << "Receive MESSAGE_TYPE_REPLY_START_CAPTURING"; - - m_isClientCaptured = true; - } - break; - case profiler::net::MESSAGE_TYPE_REPLY_PREPARE_BLOCKS: - { - qInfo() << "Receive MESSAGE_TYPE_REPLY_PREPARE_BLOCKS"; - m_isClientPreparedBlocks = true; - } - break; - case profiler::net::MESSAGE_TYPE_REPLY_END_SEND_BLOCKS: - { - qInfo() << "Receive MESSAGE_TYPE_REPLY_END_SEND_BLOCKS"; - - } - break; - case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS: - { - qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS"; - m_recFrames = true; - profiler::net::DataMessage* dm = (profiler::net::DataMessage*)message; - necessarySize = dm->size; - loadedSize = 0; - m_receivedProfileData.write(data.data()+sizeof(profiler::net::DataMessage),data.size() - sizeof(profiler::net::DataMessage)); - loadedSize += data.size() - sizeof(profiler::net::DataMessage); - //std::this_thread::sleep_for(std::chrono::seconds(2)); - - timeBegin = std::chrono::system_clock::now(); - } break; - - default: - //qInfo() << "Receive unknown " << message->type; - break; - } } + if (disconnected) + clearData(); + delete [] buffer; } -void EasyMainWindow::onConnected() +void EasySocketListener::listenDescription() { - qInfo() << "onConnected()"; + // TODO: Merge functions listenDescription() and listenCapture() - m_isConnected = true; - m_captureAction->setEnabled(true); -} -void EasyMainWindow::onErrorConnection(QAbstractSocket::SocketError socketError) -{ - qInfo() << m_server->error(); + static const int buffer_size = 8 * 1024 * 1024; + char* buffer = new char[buffer_size]; + int seek = 0, bytes = 0; + + bool isListen = true, disconnected = false; + while (isListen && !m_bInterrupt.load(::std::memory_order_acquire)) + { + if ((bytes - seek) == 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.state() == EasySocket::CONNECTION_STATE_DISCONNECTED) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + disconnected = true; + } + + seek = 0; + bytes = 0; + + continue; + } + + seek = 0; + } + + if (bytes == 0) + { + isListen = false; + break; + } + + char* buf = buffer + seek; + + if (bytes > 0) + { + auto message = reinterpret_cast(buf); + if (!message->isEasyNetMessage()) + continue; + + switch (message->type) + { + case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION: + { + qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION"; + seek += sizeof(profiler::net::Message); + break; + } + + case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END: + { + qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END"; + seek += sizeof(profiler::net::Message); + + seek = 0; + bytes = 0; + + isListen = false; + + break; + } + + case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION: + { + qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS"; + + seek += sizeof(profiler::net::DataMessage); + profiler::net::DataMessage* dm = (profiler::net::DataMessage*)message; + int neededSize = dm->size; + + buf = buffer + seek; + auto bytesNumber = ::std::min((int)dm->size, bytes - seek); + m_receivedSize += bytesNumber; + m_receivedData.write(buf, bytesNumber); + neededSize -= bytesNumber; + + if (neededSize == 0) + seek += bytesNumber; + else{ + seek = 0; + bytes = 0; + } + + int loaded = 0; + while (neededSize > 0) + { + bytes = m_easySocket.receive(buffer, buffer_size); + + if (bytes == -1) + { + if (m_easySocket.state() == EasySocket::CONNECTION_STATE_DISCONNECTED) + { + m_bConnected.store(false, ::std::memory_order_release); + isListen = false; + disconnected = true; + neededSize = 0; + } + + break; + } + + buf = buffer; + int toWrite = ::std::min(bytes, neededSize); + m_receivedSize += toWrite; + m_receivedData.write(buf, toWrite); + neededSize -= toWrite; + loaded += toWrite; + seek = toWrite; + } + + break; + } + + default: + break; + } + } + } + + if (disconnected) + clearData(); + + delete[] buffer; } -void EasyMainWindow::onDisconnect() -{ - qInfo() << "onDisconnect()"; - m_isConnected = false; - m_captureAction->setEnabled(false); -} +////////////////////////////////////////////////////////////////////////// -void EasyMainWindow::onNewConnection() -{ - //m_client = m_server->nextPendingConnection(); - - //qInfo() << "New connection!" << m_client; - - //connect(m_client, SIGNAL(disconnected()), this, SLOT(onDisconnection())) ; - //connect(m_client, SIGNAL(readyRead()), this, SLOT(readTcpData()) ); -} - -void EasyMainWindow::onDisconnection() -{ - //m_client = nullptr; -} diff --git a/profiler_gui/main_window.h b/profiler_gui/main_window.h index 47dee44..07e284d 100644 --- a/profiler_gui/main_window.h +++ b/profiler_gui/main_window.h @@ -37,7 +37,6 @@ #include #include -#include #include "profiler/easy_socket.h" #include "profiler/reader.h" @@ -65,23 +64,27 @@ class EasyFileReader Q_DECL_FINAL ::profiler::descriptors_list_t m_descriptors; ///< ::profiler::blocks_t m_blocks; ///< ::profiler::thread_blocks_tree_t m_blocksTree; ///< + ::std::stringstream m_stream; ///< QString m_filename; ///< ::std::thread m_thread; ///< ::std::atomic_bool m_bDone; ///< ::std::atomic m_progress; ///< ::std::atomic m_size; ///< + bool m_isFile = false; ///< public: EasyFileReader(); ~EasyFileReader(); + const bool isFile() const; bool done() const; int progress() const; unsigned int size() const; const QString& filename() const; void load(const QString& _filename); + void load(::std::stringstream& _stream); 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, @@ -91,6 +94,52 @@ public: ////////////////////////////////////////////////////////////////////////// +enum EasyListenerRegime : uint8_t +{ + LISTENER_IDLE = 0, + LISTENER_CAPTURE, + LISTENER_DESCRIBE +}; + +class EasySocketListener Q_DECL_FINAL +{ + EasySocket m_easySocket; ///< + ::std::stringstream m_receivedData; ///< + ::std::thread m_thread; ///< + uint64_t m_receivedSize; ///< + ::std::atomic_bool m_bInterrupt; ///< + ::std::atomic_bool m_bConnected; ///< + EasyListenerRegime m_regime; ///< + +public: + + EasySocketListener(); + ~EasySocketListener(); + + bool connected() const; + EasyListenerRegime regime() const; + uint64_t size() const; + + ::std::stringstream& data(); + void clearData(); + + bool connect(const char* _ipaddress, uint16_t _port); + + void startCapture(); + void stopCapture(); + void requestBlocksDescription(); + + void sendBlockStatus(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status); + +private: + + void listenCapture(); + void listenDescription(); + +}; // END of class EasySocketListener. + +////////////////////////////////////////////////////////////////////////// + class EasyMainWindow : public QMainWindow { Q_OBJECT @@ -103,37 +152,24 @@ protected: QString m_lastFile; QDockWidget* m_treeWidget = nullptr; QDockWidget* m_graphicsView = nullptr; - class QProgressDialog* m_downloadingProgress = nullptr; #if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0 QDockWidget* m_descTreeWidget = nullptr; #endif class QProgressDialog* m_progress = nullptr; - class QAction* m_editBlocksAction = nullptr; class QDialog* m_descTreeDialog = nullptr; class EasyDescWidget* m_dialogDescTree = nullptr; + class QMessageBox* m_listenerDialog = nullptr; QTimer m_readerTimer; - QTimer m_downloadedTimer; + QTimer m_listenerTimer; ::profiler::SerializedData m_serializedBlocks; ::profiler::SerializedData m_serializedDescriptors; EasyFileReader m_reader; - - QTcpSocket* m_server = nullptr; - - std::stringstream m_receivedProfileData; - bool m_recFrames = false; + EasySocketListener m_listener; class QLineEdit* m_ipEdit = nullptr; class QLineEdit* m_portEdit = nullptr; - bool m_isConnected = false; - - std::thread m_thread; - - EasySocket m_easySocket; - - bool m_downloading = false; - ::std::atomic m_downloadedBytes; class QAction* m_captureAction = nullptr; class QAction* m_connectAction = nullptr; @@ -147,8 +183,6 @@ public: void closeEvent(QCloseEvent* close_event) override; - void listen(); - protected slots: void onOpenFileClicked(bool); @@ -166,34 +200,28 @@ protected slots: void onExpandAllClicked(bool); void onCollapseAllClicked(bool); void onFileReaderTimeout(); - void onDownloadTimeout(); + void onListenerTimerTimeout(); void onFileReaderCancel(); void onEditBlocksClicked(bool); void onDescTreeDialogClose(int); + void onListenerDialogClose(int); void onCaptureClicked(bool); - - void readTcpData(); - void onNewConnection(); - void onDisconnection(); - void onConnected(); - void onErrorConnection(QAbstractSocket::SocketError socketError); - void onDisconnect(); + void onGetBlockDescriptionsClicked(bool); void onConnectClicked(bool); - void handleResults(const QString &s); + void onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status); + private: // Private non-virtual methods void loadFile(const QString& filename); + void readStream(::std::stringstream& data); void loadSettings(); void loadGeometry(); void saveSettingsAndGeometry(); - bool m_isClientPreparedBlocks = false; - bool m_isClientCaptured = false; - }; // END of class EasyMainWindow. ////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/resources.qrc b/profiler_gui/resources.qrc index bf37828..3396613 100644 --- a/profiler_gui/resources.qrc +++ b/profiler_gui/resources.qrc @@ -19,5 +19,8 @@ icons/lan_on.svg icons/play.svg icons/delete.svg + icons/list.svg + icons/search-next.svg + icons/search-prev.svg diff --git a/sample/main.cpp b/sample/main.cpp index 0b7848b..4c0ee35 100644 --- a/sample/main.cpp +++ b/sample/main.cpp @@ -18,6 +18,8 @@ int MODELLING_STEPS = 1500; int RENDER_STEPS = 1500; int RESOURCE_LOADING_COUNT = 50; +//#define SAMPLE_NETWORK_TEST + void localSleep(int magic=200000) { //PROFILER_BEGIN_FUNCTION_BLOCK_GROUPED(profiler::colors::Blue); @@ -134,7 +136,11 @@ void loadingResourcesThread(){ //std::unique_lock lk(cv_m); //cv.wait(lk, []{return g_i == 1; }); EASY_THREAD("Resource loading"); +#ifdef SAMPLE_NETWORK_TEST + while (true) { +#else for(int i = 0; i < RESOURCE_LOADING_COUNT; i++){ +#endif loadingResources(); EASY_EVENT("Resources Loading!", profiler::colors::Cyan); localSleep(1200000); @@ -146,7 +152,11 @@ void modellingThread(){ //std::unique_lock lk(cv_m); //cv.wait(lk, []{return g_i == 1; }); EASY_THREAD("Modelling"); +#ifdef SAMPLE_NETWORK_TEST + while (true) { +#else for (int i = 0; i < MODELLING_STEPS; i++){ +#endif modellingStep(); localSleep(1200000); //std::this_thread::sleep_for(std::chrono::milliseconds(20)); @@ -157,7 +167,11 @@ void renderThread(){ //std::unique_lock lk(cv_m); //cv.wait(lk, []{return g_i == 1; }); EASY_THREAD("Render"); +#ifdef SAMPLE_NETWORK_TEST + while (true) { +#else for (int i = 0; i < RENDER_STEPS; i++){ +#endif frame(); localSleep(1200000); //std::this_thread::sleep_for(std::chrono::milliseconds(20)); @@ -187,15 +201,19 @@ int main(int argc, char* argv[]) std::cout << "Resource loading count: " << RESOURCE_LOADING_COUNT << std::endl; auto start = std::chrono::system_clock::now(); + +#ifndef SAMPLE_NETWORK_TEST EASY_PROFILER_ENABLE; +#endif + EASY_MAIN_THREAD; profiler::startListenSignalToCapture(); std::vector threads; for (int i=0; i < 3; i++) { - threads.emplace_back(std::thread(loadingResourcesThread)); - threads.emplace_back(std::thread(renderThread)); - threads.emplace_back(std::thread(modellingThread)); + threads.emplace_back(loadingResourcesThread); + threads.emplace_back(renderThread); + threads.emplace_back(modellingThread); } cv_m.lock(); diff --git a/src/event_trace_win.cpp b/src/event_trace_win.cpp index ce41680..bdf8668 100644 --- a/src/event_trace_win.cpp +++ b/src/event_trace_win.cpp @@ -52,6 +52,8 @@ //extern ProfileManager& MANAGER; #define MANAGER ProfileManager::instance() +::std::atomic_uint64_t TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL); + namespace profiler { const decltype(EVENT_DESCRIPTOR::Opcode) SWITCH_CONTEXT_OPCODE = 36; @@ -109,6 +111,8 @@ namespace profiler { auto _contextSwitchEvent = reinterpret_cast(_traceEvent->UserData); const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart); + if (time > TRACING_END_TIME.load(::std::memory_order_acquire)) + return; const char* process_name = ""; @@ -277,8 +281,11 @@ namespace profiler { but if that would not work, return to using shell command "logman stop". */ - static Properties p; // static is safe because we are guarded by spin-lock m_spin - p = m_properties; // Use copy of m_properties to make sure m_properties will not be changed + // static is safe because we are guarded by spin-lock m_spin + static Properties p = ([]{ Properties prp; strncpy(prp.sessionName, KERNEL_LOGGER_NAME, sizeof(prp.sessionName)); return prp; })(); + p.base = m_properties.base; // Use copy of m_properties to make sure m_properties will not be changed + + // Stop another session ControlTrace(NULL, KERNEL_LOGGER_NAME, reinterpret_cast(&p), EVENT_TRACE_CONTROL_STOP); // Console window variant: @@ -394,6 +401,8 @@ namespace profiler { if (!m_bEnabled) return; + TRACING_END_TIME.store(getCurrentTime(), ::std::memory_order_release); + ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP); CloseTrace(m_openedHandle); @@ -407,6 +416,8 @@ namespace profiler { PROCESS_INFO_TABLE.clear(); THREAD_PROCESS_INFO_TABLE.clear(); THREAD_PROCESS_INFO_TABLE[0U] = nullptr; + + TRACING_END_TIME.store(~0ULL, ::std::memory_order_release); } } // END of namespace profiler. diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index 48b0418..e4964b7 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -670,22 +670,23 @@ void ProfileManager::startListenSignalToCapture() { if (!m_isAlreadyListened) { - m_stopListen.store(false); - m_listenThread = std::thread(&ProfileManager::startListen, this); + m_stopListen.store(false, std::memory_order_release); + m_listenThread = std::thread(&ProfileManager::listen, this); m_isAlreadyListened = true; - } } void ProfileManager::stopListenSignalToCapture() { - m_stopListen.store(true); + m_stopListen.store(true, std::memory_order_release); m_isAlreadyListened = false; } ////////////////////////////////////////////////////////////////////////// -void ProfileManager::startListen() +//#define EASY_DEBUG_NET_PRINT + +void ProfileManager::listen() { EASY_THREAD("EasyProfiler.Listen"); @@ -694,7 +695,7 @@ void ProfileManager::startListen() socket.bind(profiler::DEFAULT_PORT); int bytes = 0; - while (!m_stopListen.load()) + while (!m_stopListen.load(std::memory_order_acquire)) { bool hasConnect = false; @@ -703,14 +704,17 @@ void ProfileManager::startListen() EASY_EVENT("ClientConnected", profiler::colors::White, profiler::OFF); hasConnect = true; + +#ifdef EASY_DEBUG_NET_PRINT printf("Client Accepted!\n"); +#endif replyMessage.type = profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION; bytes = socket.send(&replyMessage, sizeof(replyMessage)); hasConnect = bytes > 0; - while (hasConnect && !m_stopListen.load()) + while (hasConnect && !m_stopListen.load(std::memory_order_acquire)) { char buffer[256] = {}; @@ -727,62 +731,129 @@ void ProfileManager::startListen() continue; } - switch (message->type) { - case profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE: + switch (message->type) { - printf("RECEIVED MESSAGE_TYPE_REQUEST_START_CAPTURE\n"); - ProfileManager::setEnabled(true); - EASY_EVENT("StartCapture", profiler::colors::Green, profiler::OFF); + case profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE: + { +#ifdef EASY_DEBUG_NET_PRINT + printf("RECEIVED MESSAGE_TYPE_REQUEST_START_CAPTURE\n"); +#endif - replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING; - bytes = socket.send(&replyMessage, sizeof(replyMessage)); - hasConnect = bytes > 0; + ProfileManager::setEnabled(true); + EASY_EVENT("StartCapture", profiler::colors::Green, profiler::OFF); + + replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING; + bytes = socket.send(&replyMessage, sizeof(replyMessage)); + hasConnect = bytes > 0; + + break; + } + + case profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE: + { +#ifdef EASY_DEBUG_NET_PRINT + printf("RECEIVED MESSAGE_TYPE_REQUEST_STOP_CAPTURE\n"); +#endif + + EASY_EVENT("StopCapture", profiler::colors::Red, profiler::OFF); + ProfileManager::setEnabled(false); + + //TODO + //if connection aborted - ignore this part + + profiler::net::DataMessage dm; + profiler::OStream os; + dumpBlocksToStream(os); + dm.size = (uint32_t)os.stream().str().length(); + + int packet_size = int(sizeof(dm)) + int(dm.size); + + char *sendbuf = new char[packet_size]; + + memset(sendbuf, 0, packet_size); + memcpy(sendbuf, &dm, sizeof(dm)); + memcpy(sendbuf + sizeof(dm), os.stream().str().c_str(), dm.size); + + bytes = socket.send(sendbuf, packet_size); + hasConnect = bytes > 0; + + /*std::string tempfilename = "test_snd.prof"; + std::ofstream of(tempfilename, std::fstream::binary); + of.write((const char*)os.stream().str().c_str(), dm.size); + of.close();*/ + + delete[] sendbuf; + + replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_END; + bytes = socket.send(&replyMessage, sizeof(replyMessage)); + hasConnect = bytes > 0; + + break; + } + + case profiler::net::MESSAGE_TYPE_REQUEST_BLOCKS_DESCRIPTION: + { +#ifdef EASY_DEBUG_NET_PRINT + printf("RECEIVED MESSAGE_TYPE_REQUEST_BLOCKS_DESCRIPTION\n"); +#endif + + profiler::OStream os; + + // Write block descriptors + m_storedSpin.lock(); + os.write(static_cast(m_descriptors.size())); + os.write(m_usedMemorySize); + for (const auto descriptor : m_descriptors) + { + const auto name_size = descriptor->nameSize(); + const auto filename_size = descriptor->filenameSize(); + const auto size = static_cast(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size); + + os.write(size); + os.write(*descriptor); + os.write(name_size); + os.write(descriptor->name(), name_size); + os.write(descriptor->filename(), filename_size); + } + m_storedSpin.unlock(); + // END of Write block descriptors. + + profiler::net::DataMessage dm((uint32_t)os.stream().str().length(), profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION); + int packet_size = int(sizeof(dm)) + int(dm.size); + + char *sendbuf = new char[packet_size]; + + memset(sendbuf, 0, packet_size); + memcpy(sendbuf, &dm, sizeof(dm)); + memcpy(sendbuf + sizeof(dm), os.stream().str().c_str(), dm.size); + + bytes = socket.send(sendbuf, packet_size); + hasConnect = bytes > 0; + + delete[] sendbuf; + + replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END; + bytes = socket.send(&replyMessage, sizeof(replyMessage)); + hasConnect = bytes > 0; + + break; + } + + case profiler::net::MESSAGE_TYPE_EDIT_BLOCK_STATUS: + { +#ifdef EASY_DEBUG_NET_PRINT + printf("RECEIVED MESSAGE_TYPE_EDIT_BLOCK_STATUS\n"); +#endif + + auto data = reinterpret_cast(message); + setBlockStatus(data->id, static_cast<::profiler::EasyBlockStatus>(data->status)); + + break; + } + + default: + break; } - break; - case profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE: - { - printf("RECEIVED MESSAGE_TYPE_REQUEST_STOP_CAPTURE\n"); - EASY_EVENT("StopCapture", profiler::colors::Red, profiler::OFF); - ProfileManager::setEnabled(false); - - replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_PREPARE_BLOCKS; - bytes = socket.send(&replyMessage, sizeof(replyMessage)); - hasConnect = bytes > 0; - - //TODO - //if connection aborted - ignore this part - - profiler::net::DataMessage dm; - profiler::OStream os; - dumpBlocksToStream(os); - dm.size = (uint32_t)os.stream().str().length(); - - int packet_size = int(sizeof(dm)) + int(dm.size); - - char *sendbuf = new char[packet_size]; - - memset(sendbuf, 0, packet_size); - memcpy(sendbuf, &dm, sizeof(dm)); - memcpy(sendbuf + sizeof(dm), os.stream().str().c_str(), dm.size); - - bytes = socket.send(sendbuf, packet_size); - hasConnect = bytes > 0; - - /*std::string tempfilename = "test_snd.prof"; - std::ofstream of(tempfilename, std::fstream::binary); - of.write((const char*)os.stream().str().c_str(), dm.size); - of.close();*/ - - delete[] sendbuf; - replyMessage.type = profiler::net::MESSAGE_TYPE_REPLY_END_SEND_BLOCKS; - bytes = socket.send(&replyMessage, sizeof(replyMessage)); - hasConnect = bytes > 0; - } - break; - default: - break; - } - //nn_freemsg (buf); } diff --git a/src/profile_manager.h b/src/profile_manager.h index 092deff..b1c2fee 100644 --- a/src/profile_manager.h +++ b/src/profile_manager.h @@ -367,7 +367,7 @@ class ProfileManager std::thread m_listenThread; bool m_isAlreadyListened = false; - void startListen(); + void listen(); int m_socket = 0;//TODO crossplatform diff --git a/src/reader.cpp b/src/reader.cpp index 034bd16..6baf56b 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -45,6 +45,7 @@ #include "profiler/reader.h" #include "hashed_cstr.h" #include +#include #include #include #include @@ -219,389 +220,467 @@ void validate_pointers(::std::atomic& _progress, const char* _oldbase, ::pr const int64_t TIME_FACTOR = 1000000000LL; -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) -{ - EASY_FUNCTION(::profiler::colors::Cyan); +////////////////////////////////////////////////////////////////////////// - ::std::ifstream inFile(filename, ::std::fstream::binary); - progress.store(0); +extern "C" { - if (!inFile.is_open()) - return 0; - - int64_t cpu_frequency = 0LL; - inFile.read((char*)&cpu_frequency, sizeof(int64_t)); - - ::profiler::timestamp_t begin_time = 0, end_time = 0; - inFile.read((char*)&begin_time, sizeof(::profiler::timestamp_t)); - inFile.read((char*)&end_time, sizeof(::profiler::timestamp_t)); - if (cpu_frequency != 0) + 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) { - begin_time *= TIME_FACTOR; - begin_time /= cpu_frequency; - end_time *= TIME_FACTOR; - end_time /= cpu_frequency; + ::std::ifstream inFile(filename, ::std::fstream::binary); + progress.store(0); + if (!inFile.is_open()) + return 0; + ::std::stringstream str; + str.set_rdbuf(inFile.rdbuf()); + return fillTreesFromStream(progress, str, serialized_blocks, serialized_descriptors, descriptors, blocks, threaded_trees, gather_statistics); } - uint32_t total_blocks_number = 0; - inFile.read((char*)&total_blocks_number, sizeof(decltype(total_blocks_number))); - if (total_blocks_number == 0) - return 0; + ////////////////////////////////////////////////////////////////////////// - uint64_t memory_size = 0; - inFile.read((char*)&memory_size, sizeof(decltype(memory_size))); - if (memory_size == 0) - return 0; - - uint32_t total_descriptors_number = 0; - inFile.read((char*)&total_descriptors_number, sizeof(decltype(total_descriptors_number))); - if (total_descriptors_number == 0) - return 0; - - uint64_t descriptors_memory_size = 0; - inFile.read((char*)&descriptors_memory_size, sizeof(decltype(descriptors_memory_size))); - if (descriptors_memory_size == 0) - return 0; - - descriptors.reserve(total_descriptors_number); - //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; - serialized_descriptors.set(descriptors_memory_size); - //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); - - uint64_t i = 0; - while (!inFile.eof() && descriptors.size() < total_descriptors_number) + 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) { - uint16_t sz = 0; - inFile.read((char*)&sz, sizeof(sz)); - if (sz == 0) + EASY_FUNCTION(::profiler::colors::Cyan); + + progress.store(0); + + int64_t cpu_frequency = 0LL; + inFile.read((char*)&cpu_frequency, sizeof(int64_t)); + + ::profiler::timestamp_t begin_time = 0, end_time = 0; + inFile.read((char*)&begin_time, sizeof(::profiler::timestamp_t)); + inFile.read((char*)&end_time, sizeof(::profiler::timestamp_t)); + if (cpu_frequency != 0) { - descriptors.push_back(nullptr); - continue; + begin_time *= TIME_FACTOR; + begin_time /= cpu_frequency; + end_time *= TIME_FACTOR; + end_time /= cpu_frequency; } - //if (i + sz > descriptors_memory_size) { - // printf("FILE CORRUPTED\n"); - // return 0; - //} + uint32_t total_blocks_number = 0; + inFile.read((char*)&total_blocks_number, sizeof(decltype(total_blocks_number))); + if (total_blocks_number == 0) + return 0; - char* data = serialized_descriptors[i]; - inFile.read(data, sz); - auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); - descriptors.push_back(descriptor); + uint64_t memory_size = 0; + inFile.read((char*)&memory_size, sizeof(decltype(memory_size))); + if (memory_size == 0) + return 0; - i += sz; - auto oldprogress = progress.exchange(static_cast(15 * i / descriptors_memory_size), ::std::memory_order_release); - if (oldprogress < 0) - return 0; // Loading interrupted - } + uint32_t total_descriptors_number = 0; + inFile.read((char*)&total_descriptors_number, sizeof(decltype(total_descriptors_number))); + if (total_descriptors_number == 0) + return 0; - typedef ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::profiler::passthrough_hash> PerThreadStats; - PerThreadStats thread_statistics, parent_statistics, frame_statistics; - IdMap identification_table; + uint64_t descriptors_memory_size = 0; + inFile.read((char*)&descriptors_memory_size, sizeof(decltype(descriptors_memory_size))); + if (descriptors_memory_size == 0) + return 0; - blocks.reserve(total_blocks_number); - //olddata = append_regime ? serialized_blocks.data() : nullptr; - serialized_blocks.set(memory_size); - //validate_pointers(progress, olddata, serialized_blocks, blocks, blocks.size()); + descriptors.reserve(total_descriptors_number); + //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; + serialized_descriptors.set(descriptors_memory_size); + //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); - i = 0; - uint32_t read_number = 0; - ::profiler::block_index_t blocks_counter = 0; - ::std::vector name; - while (!inFile.eof() && read_number < total_blocks_number) - { - EASY_BLOCK("Read thread data", ::profiler::colors::DarkGreen); - - ::profiler::thread_id_t thread_id = 0; - inFile.read((char*)&thread_id, sizeof(decltype(thread_id))); - - auto& root = threaded_trees[thread_id]; - - uint16_t name_size = 0; - inFile.read((char*)&name_size, sizeof(uint16_t)); - if (name_size != 0) + uint64_t i = 0; + while (!inFile.eof() && descriptors.size() < total_descriptors_number) { - name.resize(name_size); - inFile.read(name.data(), name_size); - root.thread_name = name.data(); - } - - uint32_t blocks_number_in_thread = 0; - inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); - auto threshold = read_number + blocks_number_in_thread; - while (!inFile.eof() && read_number < threshold) - { - EASY_BLOCK("Read context switch", ::profiler::colors::Green); - - ++read_number; - uint16_t sz = 0; inFile.read((char*)&sz, sizeof(sz)); if (sz == 0) - return 0; + { + descriptors.push_back(nullptr); + continue; + } - char* data = serialized_blocks[i]; + //if (i + sz > descriptors_memory_size) { + // printf("FILE CORRUPTED\n"); + // return 0; + //} + + char* data = serialized_descriptors[i]; inFile.read(data, sz); + auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); + descriptors.push_back(descriptor); + i += sz; - auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); - auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); - auto t_end = t_begin + 1; - - if (cpu_frequency != 0) - { - *t_begin *= TIME_FACTOR; - *t_begin /= cpu_frequency; - *t_end *= TIME_FACTOR; - *t_end /= cpu_frequency; - } - - if (*t_end > begin_time) - { - if (*t_begin < begin_time) - *t_begin = begin_time; - - blocks.emplace_back(); - ::profiler::BlocksTree& tree = blocks.back(); - tree.node = baseData; - const auto block_index = blocks_counter++; - - root.sync.emplace_back(block_index); - } - - auto oldprogress = progress.exchange(20 + static_cast(70 * i / memory_size), ::std::memory_order_release); + auto oldprogress = progress.exchange(static_cast(15 * i / descriptors_memory_size), ::std::memory_order_release); if (oldprogress < 0) return 0; // Loading interrupted } - if (inFile.eof()) - break; + typedef ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::profiler::passthrough_hash> PerThreadStats; + PerThreadStats thread_statistics, parent_statistics, frame_statistics; + IdMap identification_table; - blocks_number_in_thread = 0; - inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); - threshold = read_number + blocks_number_in_thread; - while (!inFile.eof() && read_number < threshold) + blocks.reserve(total_blocks_number); + //olddata = append_regime ? serialized_blocks.data() : nullptr; + serialized_blocks.set(memory_size); + //validate_pointers(progress, olddata, serialized_blocks, blocks, blocks.size()); + + i = 0; + uint32_t read_number = 0; + ::profiler::block_index_t blocks_counter = 0; + ::std::vector name; + while (!inFile.eof() && read_number < total_blocks_number) { - EASY_BLOCK("Read block", ::profiler::colors::Green); + EASY_BLOCK("Read thread data", ::profiler::colors::DarkGreen); - ++read_number; + ::profiler::thread_id_t thread_id = 0; + inFile.read((char*)&thread_id, sizeof(decltype(thread_id))); - uint16_t sz = 0; - inFile.read((char*)&sz, sizeof(sz)); - if (sz == 0) - return 0; + auto& root = threaded_trees[thread_id]; - char* data = serialized_blocks[i]; - inFile.read(data, sz); - i += sz; - auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); - auto desc = descriptors[baseData->id()]; - if (desc == nullptr) - return 0; - - auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); - auto t_end = t_begin + 1; - - if (cpu_frequency != 0) + uint16_t name_size = 0; + inFile.read((char*)&name_size, sizeof(uint16_t)); + if (name_size != 0) { - *t_begin *= TIME_FACTOR; - *t_begin /= cpu_frequency; - *t_end *= TIME_FACTOR; - *t_end /= cpu_frequency; + name.resize(name_size); + inFile.read(name.data(), name_size); + root.thread_name = name.data(); } - if (*t_end >= begin_time) + uint32_t blocks_number_in_thread = 0; + inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); + auto threshold = read_number + blocks_number_in_thread; + while (!inFile.eof() && read_number < threshold) { - if (*t_begin < begin_time) - *t_begin = begin_time; + EASY_BLOCK("Read context switch", ::profiler::colors::Green); - blocks.emplace_back(); - ::profiler::BlocksTree& tree = blocks.back(); - tree.node = baseData; - const auto block_index = blocks_counter++; + ++read_number; - auto& per_parent_statistics = parent_statistics[thread_id]; - auto& per_thread_statistics = thread_statistics[thread_id]; + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + return 0; - if (*tree.node->name() != 0) + char* data = serialized_blocks[i]; + inFile.read(data, sz); + i += sz; + auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); + auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); + auto t_end = t_begin + 1; + + if (cpu_frequency != 0) { - // If block has runtime name then generate new id for such block. - // Blocks with the same name will have same id. - - IdMap::key_type key(tree.node->name()); - auto it = identification_table.find(key); - if (it != identification_table.end()) - { - // There is already block with such name, use it's id - baseData->setId(it->second); - } - else - { - // There were no blocks with such name, generate new id and save it in the table for further usage. - auto id = static_cast<::profiler::block_id_t>(descriptors.size()); - identification_table.emplace(key, id); - if (descriptors.capacity() == descriptors.size()) - descriptors.reserve((descriptors.size() * 3) >> 1); - descriptors.push_back(descriptors[baseData->id()]); - baseData->setId(id); - } + *t_begin *= TIME_FACTOR; + *t_begin /= cpu_frequency; + *t_end *= TIME_FACTOR; + *t_end /= cpu_frequency; } - if (!root.children.empty()) + if (*t_end > begin_time) { - auto& back = blocks[root.children.back()]; - auto t1 = back.node->end(); - auto mt0 = tree.node->begin(); - if (mt0 < t1)//parent - starts earlier than last ends + if (*t_begin < begin_time) + *t_begin = begin_time; + + blocks.emplace_back(); + ::profiler::BlocksTree& tree = blocks.back(); + tree.node = baseData; + const auto block_index = blocks_counter++; + + root.sync.emplace_back(block_index); + } + + auto oldprogress = progress.exchange(20 + static_cast(70 * i / memory_size), ::std::memory_order_release); + if (oldprogress < 0) + return 0; // Loading interrupted + } + + if (inFile.eof()) + break; + + blocks_number_in_thread = 0; + inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread))); + threshold = read_number + blocks_number_in_thread; + while (!inFile.eof() && read_number < threshold) + { + EASY_BLOCK("Read block", ::profiler::colors::Green); + + ++read_number; + + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) + return 0; + + char* data = serialized_blocks[i]; + inFile.read(data, sz); + i += sz; + auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); + auto desc = descriptors[baseData->id()]; + if (desc == nullptr) + return 0; + + auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); + auto t_end = t_begin + 1; + + if (cpu_frequency != 0) + { + *t_begin *= TIME_FACTOR; + *t_begin /= cpu_frequency; + *t_end *= TIME_FACTOR; + *t_end /= cpu_frequency; + } + + if (*t_end >= begin_time) + { + if (*t_begin < begin_time) + *t_begin = begin_time; + + blocks.emplace_back(); + ::profiler::BlocksTree& tree = blocks.back(); + tree.node = baseData; + const auto block_index = blocks_counter++; + + auto& per_parent_statistics = parent_statistics[thread_id]; + auto& per_thread_statistics = thread_statistics[thread_id]; + + if (*tree.node->name() != 0) { - //auto lower = ::std::lower_bound(root.children.begin(), root.children.end(), tree); - /**/ - EASY_BLOCK("Find children", ::profiler::colors::Blue); - auto rlower1 = ++root.children.rbegin(); - for (; rlower1 != root.children.rend() && !(mt0 > blocks[*rlower1].node->begin()); ++rlower1); - auto lower = rlower1.base(); - ::std::move(lower, root.children.end(), ::std::back_inserter(tree.children)); + // If block has runtime name then generate new id for such block. + // Blocks with the same name will have same id. - root.children.erase(lower, root.children.end()); - EASY_END_BLOCK; - - ::profiler::timestamp_t children_duration = 0; - if (gather_statistics) + IdMap::key_type key(tree.node->name()); + auto it = identification_table.find(key); + if (it != identification_table.end()) { - EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta); - per_parent_statistics.clear(); - - //per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows - //per_parent_statistics.reserve(tree.children.size() * 2); // this gives no speed-up on Windows - // TODO: check this behavior on Linux - - for (auto i : tree.children) - { - auto& child = blocks[i]; - child.per_parent_stats = update_statistics(per_parent_statistics, child, i, block_index); - - children_duration += child.node->duration(); - if (tree.depth < child.depth) - tree.depth = child.depth; - } + // There is already block with such name, use it's id + baseData->setId(it->second); } else { - for (auto i : tree.children) - { - const auto& child = blocks[i]; - children_duration += child.node->duration(); - if (tree.depth < child.depth) - tree.depth = child.depth; - } + // There were no blocks with such name, generate new id and save it in the table for further usage. + auto id = static_cast<::profiler::block_id_t>(descriptors.size()); + identification_table.emplace(key, id); + if (descriptors.capacity() == descriptors.size()) + descriptors.reserve((descriptors.size() * 3) >> 1); + descriptors.push_back(descriptors[baseData->id()]); + baseData->setId(id); } + } - ++tree.depth; + if (!root.children.empty()) + { + auto& back = blocks[root.children.back()]; + auto t1 = back.node->end(); + auto mt0 = tree.node->begin(); + if (mt0 < t1)//parent - starts earlier than last ends + { + //auto lower = ::std::lower_bound(root.children.begin(), root.children.end(), tree); + /**/ + EASY_BLOCK("Find children", ::profiler::colors::Blue); + auto rlower1 = ++root.children.rbegin(); + for (; rlower1 != root.children.rend() && !(mt0 > blocks[*rlower1].node->begin()); ++rlower1); + auto lower = rlower1.base(); + ::std::move(lower, root.children.end(), ::std::back_inserter(tree.children)); + + root.children.erase(lower, root.children.end()); + EASY_END_BLOCK; + + ::profiler::timestamp_t children_duration = 0; + if (gather_statistics) + { + EASY_BLOCK("Gather statistic within parent", ::profiler::colors::Magenta); + per_parent_statistics.clear(); + + //per_parent_statistics.reserve(tree.children.size()); // this gives slow-down on Windows + //per_parent_statistics.reserve(tree.children.size() * 2); // this gives no speed-up on Windows + // TODO: check this behavior on Linux + + for (auto i : tree.children) + { + auto& child = blocks[i]; + child.per_parent_stats = update_statistics(per_parent_statistics, child, i, block_index); + + children_duration += child.node->duration(); + if (tree.depth < child.depth) + tree.depth = child.depth; + } + } + else + { + for (auto i : tree.children) + { + const auto& child = blocks[i]; + children_duration += child.node->duration(); + if (tree.depth < child.depth) + tree.depth = child.depth; + } + } + + ++tree.depth; + } + } + + root.children.emplace_back(block_index);// ::std::move(tree)); + if (desc->type() == ::profiler::BLOCK_TYPE_EVENT) + root.events.emplace_back(block_index); + + + if (gather_statistics) + { + EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral); + tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index, thread_id); } } - root.children.emplace_back(block_index);// ::std::move(tree)); - if (desc->type() == ::profiler::BLOCK_TYPE_EVENT) - root.events.emplace_back(block_index); + auto oldprogress = progress.exchange(20 + static_cast(70 * i / memory_size), ::std::memory_order_release); + if (oldprogress < 0) + return 0; // Loading interrupted + } + } + if (progress.load(::std::memory_order_acquire) < 0) + return 0; // Loading interrupted - if (gather_statistics) + EASY_BLOCK("Gather statistics for roots", ::profiler::colors::Purple); + if (gather_statistics) + { + ::std::vector<::std::thread> statistics_threads; + statistics_threads.reserve(threaded_trees.size()); + + for (auto& it : threaded_trees) + { + auto& root = it.second; + root.thread_id = it.first; + //root.tree.shrink_to_fit(); + + auto& per_frame_statistics = frame_statistics[root.thread_id]; + auto& per_parent_statistics = parent_statistics[it.first]; + per_parent_statistics.clear(); + + statistics_threads.emplace_back(::std::thread([&per_parent_statistics, &per_frame_statistics, &blocks](::profiler::BlocksTreeRoot& root) { - EASY_BLOCK("Gather per thread statistics", ::profiler::colors::Coral); - tree.per_thread_stats = update_statistics(per_thread_statistics, tree, block_index, thread_id); - } + //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) + //{ + // return blocks[left].node->begin() < blocks[right].node->begin(); + //}); + + for (auto i : root.children) + { + auto& frame = blocks[i]; + frame.per_parent_stats = update_statistics(per_parent_statistics, frame, i, root.thread_id); + + per_frame_statistics.clear(); + update_statistics_recursive(per_frame_statistics, frame, i, i, blocks); + + if (root.depth < frame.depth) + root.depth = frame.depth; + + root.active_time += frame.node->duration(); + } + + ++root.depth; + }, ::std::ref(root))); } - auto oldprogress = progress.exchange(20 + static_cast(70 * i / memory_size), ::std::memory_order_release); - if (oldprogress < 0) - return 0; // Loading interrupted - } - } - - if (progress.load(::std::memory_order_acquire) < 0) - return 0; // Loading interrupted - - EASY_BLOCK("Gather statistics for roots", ::profiler::colors::Purple); - if (gather_statistics) - { - ::std::vector<::std::thread> statistics_threads; - statistics_threads.reserve(threaded_trees.size()); - - for (auto& it : threaded_trees) - { - auto& root = it.second; - root.thread_id = it.first; - //root.tree.shrink_to_fit(); - - auto& per_frame_statistics = frame_statistics[root.thread_id]; - auto& per_parent_statistics = parent_statistics[it.first]; - per_parent_statistics.clear(); - - statistics_threads.emplace_back(::std::thread([&per_parent_statistics, &per_frame_statistics, &blocks](::profiler::BlocksTreeRoot& root) + int j = 0, n = static_cast(statistics_threads.size()); + for (auto& t : statistics_threads) { + t.join(); + progress.store(90 + (10 * ++j) / n, ::std::memory_order_release); + } + } + else + { + int j = 0, n = static_cast(threaded_trees.size()); + for (auto& it : threaded_trees) + { + auto& root = it.second; + root.thread_id = it.first; + //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) //{ // return blocks[left].node->begin() < blocks[right].node->begin(); //}); + //root.tree.shrink_to_fit(); for (auto i : root.children) { auto& frame = blocks[i]; - frame.per_parent_stats = update_statistics(per_parent_statistics, frame, i, root.thread_id); - - per_frame_statistics.clear(); - update_statistics_recursive(per_frame_statistics, frame, i, i, blocks); - if (root.depth < frame.depth) root.depth = frame.depth; - root.active_time += frame.node->duration(); } ++root.depth; - }, ::std::ref(root))); - } - int j = 0, n = static_cast(statistics_threads.size()); - for (auto& t : statistics_threads) - { - t.join(); - progress.store(90 + (10 * ++j) / n, ::std::memory_order_release); + progress.store(90 + (10 * ++j) / n, ::std::memory_order_release); + } } + // No need to delete BlockStatistics instances - they will be deleted inside BlocksTree destructors + + return blocks_counter; } - else + + ////////////////////////////////////////////////////////////////////////// + + PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& inFile, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors) { - int j = 0, n = static_cast(threaded_trees.size()); - for (auto& it : threaded_trees) + EASY_FUNCTION(::profiler::colors::Cyan); + + progress.store(0); + + uint32_t total_descriptors_number = 0; + inFile.read((char*)&total_descriptors_number, sizeof(decltype(total_descriptors_number))); + if (total_descriptors_number == 0) + return false; + + uint64_t descriptors_memory_size = 0; + inFile.read((char*)&descriptors_memory_size, sizeof(decltype(descriptors_memory_size))); + if (descriptors_memory_size == 0) + return false; + + descriptors.reserve(total_descriptors_number); + //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; + serialized_descriptors.set(descriptors_memory_size); + //validate_pointers(progress, olddata, serialized_descriptors, descriptors, descriptors.size()); + + uint64_t i = 0; + while (!inFile.eof() && descriptors.size() < total_descriptors_number) { - auto& root = it.second; - root.thread_id = it.first; - - //::std::sort(root.sync.begin(), root.sync.end(), [&blocks](::profiler::block_index_t left, ::profiler::block_index_t right) - //{ - // return blocks[left].node->begin() < blocks[right].node->begin(); - //}); - - //root.tree.shrink_to_fit(); - for (auto i : root.children) + uint16_t sz = 0; + inFile.read((char*)&sz, sizeof(sz)); + if (sz == 0) { - auto& frame = blocks[i]; - if (root.depth < frame.depth) - root.depth = frame.depth; - root.active_time += frame.node->duration(); + descriptors.push_back(nullptr); + continue; } - ++root.depth; + //if (i + sz > descriptors_memory_size) { + // printf("FILE CORRUPTED\n"); + // return 0; + //} - progress.store(90 + (10 * ++j) / n, ::std::memory_order_release); + char* data = serialized_descriptors[i]; + inFile.read(data, sz); + auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); + descriptors.push_back(descriptor); + + i += sz; + auto oldprogress = progress.exchange(static_cast(100 * i / descriptors_memory_size), ::std::memory_order_release); + if (oldprogress < 0) + return false; // Loading interrupted } - } - // No need to delete BlockStatistics instances - they will be deleted inside BlocksTree destructors - return blocks_counter; + return !descriptors.empty(); + } + + ////////////////////////////////////////////////////////////////////////// + }