diff --git a/include/profiler/profiler.h b/include/profiler/profiler.h index 4008a5c..f6aa7c3 100644 --- a/include/profiler/profiler.h +++ b/include/profiler/profiler.h @@ -26,6 +26,13 @@ along with this program.If not, see . # pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" #endif +namespace profiler { + const uint8_t EASY_VERSION_MAJOR = 0; + const uint8_t EASY_VERSION_MINOR = 1; + const uint16_t EASY_VERSION_REV = 0; + 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 /** diff --git a/include/profiler/reader.h b/include/profiler/reader.h index 2e9ec9b..ce65e2f 100644 --- a/include/profiler/reader.h +++ b/include/profiler/reader.h @@ -331,7 +331,8 @@ extern "C" { ::profiler::descriptors_list_t& descriptors, ::profiler::blocks_t& _blocks, ::profiler::thread_blocks_tree_t& threaded_trees, - bool gather_statistics); + bool gather_statistics, + ::std::stringstream& _log); PROFILER_API ::profiler::block_index_t fillTreesFromStream(::std::atomic& progress, ::std::stringstream& str, ::profiler::SerializedData& serialized_blocks, @@ -339,26 +340,32 @@ extern "C" { ::profiler::descriptors_list_t& descriptors, ::profiler::blocks_t& _blocks, ::profiler::thread_blocks_tree_t& threaded_trees, - bool gather_statistics); + bool gather_statistics, + ::std::stringstream& _log); PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& str, ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors); + ::profiler::descriptors_list_t& descriptors, + ::std::stringstream& _log); } 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, - bool gather_statistics) + 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); + return fillTreesFromFile(progress, filename, serialized_blocks, serialized_descriptors, descriptors, _blocks, threaded_trees, gather_statistics, _log); } -inline bool readDescriptionsFromStream(::std::stringstream& str, ::profiler::SerializedData& serialized_descriptors, ::profiler::descriptors_list_t& descriptors) +inline bool readDescriptionsFromStream(::std::stringstream& str, + ::profiler::SerializedData& serialized_descriptors, + ::profiler::descriptors_list_t& descriptors, + ::std::stringstream& _log) { ::std::atomic progress = ATOMIC_VAR_INIT(0); - return readDescriptionsFromStream(progress, str, serialized_descriptors, descriptors); + return readDescriptionsFromStream(progress, str, serialized_descriptors, descriptors, _log); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp index e21a99d..c539b2e 100644 --- a/profiler_gui/main_window.cpp +++ b/profiler_gui/main_window.cpp @@ -710,13 +710,9 @@ void EasyMainWindow::onFileReaderTimeout() if (m_dialogDescTree != nullptr) m_dialogDescTree->build(); } - 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"; + QMessageBox::warning(this, "Warning", QString("Can not read profiled blocks.\n\nReason:\n%1").arg(m_reader.getError()), QMessageBox::Close); } m_reader.interrupt(); @@ -788,7 +784,7 @@ 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), ::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, _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)); @@ -802,7 +798,7 @@ 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), ::std::memory_order_release); + 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); m_progress.store(100, ::std::memory_order_release); m_bDone.store(true, ::std::memory_order_release); }, EASY_GLOBALS.enable_statistics)); @@ -823,8 +819,8 @@ void EasyFileReader::interrupt() m_blocks.clear(); m_blocksTree.clear(); - decltype(m_stream) dummy; - dummy.swap(m_stream); + { 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, @@ -842,6 +838,11 @@ void EasyFileReader::get(::profiler::SerializedData& _serializedBlocks, ::profil } } +QString EasyFileReader::getError() +{ + return QString(m_errorMessage.str().c_str()); +} + ////////////////////////////////////////////////////////////////////////// void EasyMainWindow::onConnectClicked(bool) @@ -922,7 +923,8 @@ void EasyMainWindow::onGetBlockDescriptionsClicked(bool) // Read descriptions from stream decltype(EASY_GLOBALS.descriptors) descriptors; decltype(m_serializedDescriptors) serializedDescriptors; - if (readDescriptionsFromStream(m_listener.data(), serializedDescriptors, descriptors)) + ::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 @@ -943,6 +945,10 @@ void EasyMainWindow::onGetBlockDescriptionsClicked(bool) onEditBlocksClicked(true); } } + else + { + QMessageBox::warning(this, "Warning", QString("Can not read blocks description from stream.\n\nReason:\n%1").arg(errorMessage.str().c_str()), QMessageBox::Close); + } m_listener.clearData(); } diff --git a/profiler_gui/main_window.h b/profiler_gui/main_window.h index 46a0129..96f439e 100644 --- a/profiler_gui/main_window.h +++ b/profiler_gui/main_window.h @@ -65,6 +65,7 @@ class EasyFileReader Q_DECL_FINAL ::profiler::blocks_t m_blocks; ///< ::profiler::thread_blocks_tree_t m_blocksTree; ///< ::std::stringstream m_stream; ///< + ::std::stringstream m_errorMessage; ///< QString m_filename; ///< ::std::thread m_thread; ///< ::std::atomic_bool m_bDone; ///< @@ -90,6 +91,8 @@ public: ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks, ::profiler::thread_blocks_tree_t& _tree, QString& _filename); + QString getError(); + }; // END of class EasyFileReader. ////////////////////////////////////////////////////////////////////////// diff --git a/reader/main.cpp b/reader/main.cpp index f61ac49..df490ee 100644 --- a/reader/main.cpp +++ b/reader/main.cpp @@ -12,7 +12,7 @@ #include #include #include - +#include class TreePrinter { @@ -113,7 +113,10 @@ int main(int argc, char* argv[]) ::profiler::SerializedData serialized_blocks, serialized_descriptors; ::profiler::descriptors_list_t descriptors; ::profiler::blocks_t blocks; - auto blocks_counter = fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, descriptors, blocks, threaded_trees, true); + ::std::stringstream errorMessage; + auto blocks_counter = fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, descriptors, blocks, threaded_trees, true, errorMessage); + if (blocks_counter == 0) + std::cout << "Can not read blocks from file " << filename.c_str() << "\nReason: " << errorMessage.str(); auto end = std::chrono::system_clock::now(); diff --git a/src/profile_manager.cpp b/src/profile_manager.cpp index 5cd1ebc..55c8b2a 100644 --- a/src/profile_manager.cpp +++ b/src/profile_manager.cpp @@ -38,6 +38,7 @@ using namespace profiler; +const uint32_t PROFILER_SIGNATURE = ('E' << 24) | ('a' << 16) | ('s' << 8) | 'y'; const uint8_t FORCE_ON_FLAG = profiler::FORCE_ON & ~profiler::ON; //auto& MANAGER = ProfileManager::instance(); @@ -553,6 +554,10 @@ uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream) ++it; } + // Write profiler signature and version + _outputStream.write(PROFILER_SIGNATURE); + _outputStream.write(profiler::EASY_FULL_VERSION); + // Write CPU frequency to let GUI calculate real time value from CPU clocks #ifdef _WIN32 _outputStream.write(CPU_FREQUENCY); @@ -829,6 +834,10 @@ void ProfileManager::listen() profiler::OStream os; + // Write profiler signature and version + os.write(PROFILER_SIGNATURE); + os.write(profiler::EASY_FULL_VERSION); + // Write block descriptors m_storedSpin.lock(); os.write(static_cast(m_descriptors.size())); diff --git a/src/reader.cpp b/src/reader.cpp index 6bd7981..ebae752 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -53,6 +53,22 @@ ////////////////////////////////////////////////////////////////////////// +#define EASY_FULL_VER(Major, Minor, Rev) (((uint32_t)(Major) << 24) | ((uint32_t)(Minor) << 16) | (uint32_t)(Rev)) + +const uint32_t COMPATIBLE_VERSIONS[] = { + ::profiler::EASY_FULL_VERSION +}; +const uint16_t COMPATIBLE_VERSIONS_NUM = sizeof(COMPATIBLE_VERSIONS) / sizeof(uint32_t); + +bool isCompatibleVersion(uint32_t _version) +{ + if (_version == ::profiler::EASY_FULL_VERSION) + return true; + return COMPATIBLE_VERSIONS_NUM > 1 && ::std::binary_search(COMPATIBLE_VERSIONS + 1, COMPATIBLE_VERSIONS + COMPATIBLE_VERSIONS_NUM, _version); +} + +#undef EASY_FULL_VER + namespace profiler { void SerializedData::set(char* _data, size_t _size) @@ -219,6 +235,7 @@ 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'; ////////////////////////////////////////////////////////////////////////// @@ -230,13 +247,17 @@ extern "C" { ::profiler::descriptors_list_t& descriptors, ::profiler::blocks_t& blocks, ::profiler::thread_blocks_tree_t& threaded_trees, - bool gather_statistics) + bool gather_statistics, + ::std::stringstream& _log) { progress.store(0); ::std::ifstream inFile(filename, ::std::fstream::binary); if (!inFile.is_open()) + { + _log << "Can not open file " << filename; return 0; + } ::std::stringstream str; @@ -244,7 +265,7 @@ 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); + auto result = fillTreesFromStream(progress, str, serialized_blocks, serialized_descriptors, descriptors, blocks, threaded_trees, gather_statistics, _log); s.rdbuf(oldbuf); return result; @@ -258,12 +279,29 @@ extern "C" { ::profiler::descriptors_list_t& descriptors, ::profiler::blocks_t& blocks, ::profiler::thread_blocks_tree_t& threaded_trees, - bool gather_statistics) + bool gather_statistics, + ::std::stringstream& _log) { EASY_FUNCTION(::profiler::colors::Cyan); progress.store(0); + uint32_t signature = 0; + inFile.read((char*)&signature, sizeof(uint32_t)); + if (signature != PROFILER_SIGNATURE) + { + _log << "Wrong signature " << signature << "\nThis is not EasyProfiler file/stream."; + return 0; + } + + uint32_t version = 0; + inFile.read((char*)&version, sizeof(uint32_t)); + if (!isCompatibleVersion(version)) + { + _log << "Incompatible version: v" << (version >> 24) << "." << ((version & 0x00ff0000) >> 16) << "." << (version & 0x0000ffff); + return 0; + } + int64_t cpu_frequency = 0LL; inFile.read((char*)&cpu_frequency, sizeof(int64_t)); @@ -281,22 +319,34 @@ extern "C" { uint32_t total_blocks_number = 0; inFile.read((char*)&total_blocks_number, sizeof(decltype(total_blocks_number))); if (total_blocks_number == 0) + { + _log << "Profiled blocks number == 0"; return 0; + } uint64_t memory_size = 0; inFile.read((char*)&memory_size, sizeof(decltype(memory_size))); if (memory_size == 0) + { + _log << "Wrong memory size == 0 for " << total_blocks_number << " blocks"; return 0; + } uint32_t total_descriptors_number = 0; inFile.read((char*)&total_descriptors_number, sizeof(decltype(total_descriptors_number))); if (total_descriptors_number == 0) + { + _log << "Blocks description 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) + { + _log << "Wrong memory size == 0 for " << total_descriptors_number << " blocks descriptions"; return 0; + } descriptors.reserve(total_descriptors_number); //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; @@ -327,7 +377,10 @@ extern "C" { i += sz; auto oldprogress = progress.exchange(static_cast(15 * i / descriptors_memory_size), ::std::memory_order_release); if (oldprogress < 0) + { + _log << "Reading was interrupted"; return 0; // Loading interrupted + } } typedef ::std::unordered_map<::profiler::thread_id_t, StatsMap, ::profiler::passthrough_hash> PerThreadStats; @@ -373,7 +426,10 @@ extern "C" { uint16_t sz = 0; inFile.read((char*)&sz, sizeof(sz)); if (sz == 0) + { + _log << "Bad CSwitch block size == 0"; return 0; + } char* data = serialized_blocks[i]; inFile.read(data, sz); @@ -405,7 +461,10 @@ extern "C" { auto oldprogress = progress.exchange(20 + static_cast(70 * i / memory_size), ::std::memory_order_release); if (oldprogress < 0) + { + _log << "Reading was interrupted"; return 0; // Loading interrupted + } } if (inFile.eof()) @@ -423,15 +482,27 @@ extern "C" { uint16_t sz = 0; inFile.read((char*)&sz, sizeof(sz)); if (sz == 0) + { + _log << "Bad block size == 0"; return 0; + } char* data = serialized_blocks[i]; inFile.read(data, sz); i += sz; auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data); + if (baseData->id() >= total_descriptors_number) + { + _log << "Bad block id == " << baseData->id(); + return 0; + } + auto desc = descriptors[baseData->id()]; if (desc == nullptr) + { + _log << "Bad block id == " << baseData->id() << ". Description is null."; return 0; + } auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data); auto t_end = t_begin + 1; @@ -548,12 +619,18 @@ extern "C" { auto oldprogress = progress.exchange(20 + static_cast(70 * i / memory_size), ::std::memory_order_release); if (oldprogress < 0) + { + _log << "Reading was interrupted"; return 0; // Loading interrupted + } } } if (progress.load(::std::memory_order_acquire) < 0) + { + _log << "Reading was interrupted"; return 0; // Loading interrupted + } EASY_BLOCK("Gather statistics for roots", ::profiler::colors::Purple); if (gather_statistics) @@ -639,21 +716,44 @@ extern "C" { PROFILER_API bool readDescriptionsFromStream(::std::atomic& progress, ::std::stringstream& inFile, ::profiler::SerializedData& serialized_descriptors, - ::profiler::descriptors_list_t& descriptors) + ::profiler::descriptors_list_t& descriptors, + ::std::stringstream& _log) { EASY_FUNCTION(::profiler::colors::Cyan); progress.store(0); + uint32_t signature = 0; + inFile.read((char*)&signature, sizeof(uint32_t)); + if (signature != PROFILER_SIGNATURE) + { + _log << "Wrong signature " << signature << "\nThis is not EasyProfiler file/stream."; + return false; + } + + uint32_t version = 0; + inFile.read((char*)&version, sizeof(uint32_t)); + if (!isCompatibleVersion(version)) + { + _log << "Incompatible version: v" << (version >> 24) << "." << ((version & 0x00ff0000) >> 16) << "." << (version & 0x0000ffff); + return false; + } + uint32_t total_descriptors_number = 0; inFile.read((char*)&total_descriptors_number, sizeof(decltype(total_descriptors_number))); if (total_descriptors_number == 0) + { + _log << "Blocks description 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) + { + _log << "Wrong memory size == 0 for " << total_descriptors_number << " blocks descriptions"; return false; + } descriptors.reserve(total_descriptors_number); //const char* olddata = append_regime ? serialized_descriptors.data() : nullptr; @@ -684,7 +784,10 @@ extern "C" { i += sz; auto oldprogress = progress.exchange(static_cast(100 * i / descriptors_memory_size), ::std::memory_order_release); if (oldprogress < 0) + { + _log << "Reading was interrupted"; return false; // Loading interrupted + } } return !descriptors.empty();