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();